ETH Price: $2,397.03 (-8.70%)

Contract

0x21105FEBfb99FFC8cf11B006Fbb117EE223924a0

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

Please try again later

Parent Transaction Hash Block From To
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PerpetualTreasury

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 100000000 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: MIT

pragma solidity 0.8.21;

import "../../libraries/ConverterDec18.sol";
import "../../interface/IShareToken.sol";
import "../functions/PerpetualBaseFunctions.sol";
import "../interfaces/IPerpetualTreasury.sol";
import "../interfaces/IFunctionList.sol";
import "../../libraries/Utils.sol";
import "../../libraries/EnumerableSetUpgradeable.sol";

/**
 *
 * Add liquidity: share tokens are minted immediately. The liquidity is not considered
 * to be protocol owned yet until WITHDRAWAL_DELAY_TIME_SEC seconds passed. The amount
 * considered protocol owned is a linear interpolation between when it was added and
 * that time plus the delay.
 *
 * Remove liquidity: the liquidity provider calls remove liquidity upon which the
 * smart contract stores the amount that is to be removed (lpWithdrawMap).
 * The liquidity can be withdrawn by the liquidity provider after
 * WITHDRAWAL_DELAY_TIME_SEC time.
 *
 */
contract PerpetualTreasury is PerpetualBaseFunctions, IFunctionList, IPerpetualTreasury {
    using ABDKMath64x64 for int128;
    using ABDKMath64x64 for int256;
    using ConverterDec18 for int128;
    using ConverterDec18 for int256;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
    int128 internal constant ONE_PERCENT = 184467440737095516;

    function _adjustAnchorOnLiquidityAdd(
        LiquidityPoolData storage _pool,
        uint128 _newShares
    ) internal {
        _pool.prevTokenAmount = _getShareTokenAmountForPricing(_pool);
        _pool.prevAnchor = uint64(block.timestamp);
        _pool.nextTokenAmount = _pool.nextTokenAmount + _newShares;
    }

    function _adjustAnchorOnLiquidityRemove(
        LiquidityPoolData storage _pool,
        uint128 _sharesToRemove
    ) internal {
        _pool.prevTokenAmount = _getShareTokenAmountForPricing(_pool);
        _pool.prevAnchor = uint64(block.timestamp);
        _pool.nextTokenAmount = _pool.nextTokenAmount - _sharesToRemove;
    }

    function getCollateralTokenAmountForPricing(uint8 _poolId) external view returns (int128) {
        LiquidityPoolData storage pool = liquidityPools[_poolId];
        return _getCollateralTokenAmountForPricing(pool);
    }

    /**
     * Pause pauseLiquidityProvision (withdrawing, adding) by maintainer in emergency situations
     * @param _poolId id of the pool to be paused
     * @param _pauseOn true to pause, false to allow withdrawals
     */
    function pauseLiquidityProvision(uint8 _poolId, bool _pauseOn) external onlyMaintainer {
        if (_pauseOn) {
            liquidityProvisionIsPaused[_poolId] = true;
        } else {
            delete liquidityProvisionIsPaused[_poolId];
        }
        emit LiquidityProvisionPaused(_pauseOn, _poolId);
    }

    /**
     * @notice  Adds liquidity to the liquidity pool.
     *          Liquidity provider deposits collateral and retrieves share tokens in exchange.
     *          The ratio of added cash to share token is determined by current liquidity.
     *          Can only called when the pool is running and there are perpetuals in normal state.
     *          Minimal amount equals broker collateral lot size
     * @param   _poolId        Reference to liquidity pool
     * @param   _tokenAmount   The amount of tokens in collateral currency to add, in margin token unit (dec N)
     */
    function addLiquidity(
        uint8 _poolId,
        uint256 _tokenAmount
    ) external override nonReentrant whenNotPaused {
        require(!liquidityProvisionIsPaused[_poolId], "LP paused");

        //inlines the modifier updateFundingAndPrices: updateFundingAndPrices
        uint24 iPerpetualId = _selectPerpetualIds(_poolId);
        _getUpdateLogic().updateFundingAndPricesBefore(iPerpetualId, false);

        // main
        _validateLiquidityData(_poolId, _tokenAmount);
        LiquidityPoolData storage pool = liquidityPools[_poolId];
        int128 fTokenAmount = int256(_tokenAmount).fromDecN(pool.marginTokenDecimals);
        require(fTokenAmount >= pool.fBrokerCollateralLotSize, "amt too small");

        _checkPoolState(pool);

        _rebalance(pool);

        _transferFromUserToVault(pool, msg.sender, fTokenAmount);

        int128 fShareToMint = _getShareAmountToMint(pool, fTokenAmount);
        IShareToken(pool.shareTokenAddress).mint(msg.sender, fShareToMint.toUDec18());
        pool.totalSupplyShareToken = uint128(IShareToken(pool.shareTokenAddress).totalSupply());
        _getRebalanceLogic().increasePoolCash(pool.id, fTokenAmount);

        _adjustAnchorOnLiquidityAdd(pool, uint128(fShareToMint.toUDec18()));
        emit LiquidityAdded(_poolId, msg.sender, _tokenAmount, fShareToMint.toUDec18());

        //inlines the modifier updateFundingAndPrices:
        _getUpdateLogic().updateFundingAndPricesAfter(iPerpetualId);
    }

    /**
     * @notice  Initiates removal of liquidity from the liquidity pool.
     *          After delay it can be withdrawn using executeLiquidityWithdrawal.
     *          Liquidity providers redeems share token then gets collateral back.
     *          The amount of collateral retrieved differs from the amount when liquidity was added,
     *          due to profit and loss.
     *          Can only be called if there is no perpetual in emergency state.
     *          Pool must be running at least one perpetual in CLEARED or NORMAL state.
     *
     * @param   _poolId            Reference to liquidity pool
     * @param   _shareAmount       The amount of share token to remove, in wei
     */
    function withdrawLiquidity(
        uint8 _poolId,
        uint256 _shareAmount
    ) external override nonReentrant whenNotPaused {
        require(!liquidityProvisionIsPaused[_poolId], "LP paused");
        address user = msg.sender;
        require(lpWithdrawMap[user][_poolId].shareTokens == 0, "removal in queue");
        //inlines the modifier updateFundingAndPrices: updateFundingAndPrices(uint24(0), _poolId)
        uint24 iPerpetualId = _selectPerpetualIds(_poolId);
        _getUpdateLogic().updateFundingAndPricesBefore(iPerpetualId, false);

        // main part
        _validateLiquidityData(_poolId, _shareAmount);
        LiquidityPoolData storage pool = liquidityPools[_poolId];
        _isLPWithdrawValid(pool);
        _rebalance(pool);

        // shareToken always has 18 decimals (created by ShareTokenFactory)
        IShareToken shareToken = IShareToken(pool.shareTokenAddress);
        shareToken.setTransferRestricted(msg.sender);

        // Ensure the users don't end up with dust positions
        // How many shares do we need for a lot?
        int128 fShareTknsForLot = _getShareAmountToMint(pool, pool.fBrokerCollateralLotSize);
        // user requested amount is less than half a lot -> set it to half a lot
        uint256 halfLotD18 = fShareTknsForLot.mul(0x8000000000000000).toUDec18();
        if (_shareAmount < halfLotD18) {
            _shareAmount = halfLotD18;
        }
        // 1) user has less share tokens than 1 lot left -> withdraw entire balance
        // 2) user wants to withdraw more than their balance -> withdraw the balance
        if (
            shareToken.balanceOf(user) < fShareTknsForLot.toUDec18() ||
            _shareAmount > shareToken.balanceOf(user)
        ) {
            _shareAmount = shareToken.balanceOf(user);
        }

        require(_shareAmount > 0, "nothing to withdraw");
        _adjustAnchorOnLiquidityRemove(pool, uint128(_shareAmount));
        lpWithdrawMap[user][_poolId].shareTokens = _shareAmount;
        lpWithdrawMap[user][_poolId].withdrawTimestamp = uint64(block.timestamp);
        activeWithdrawals[_poolId].add(user);
        emit LiquidityWithdrawalInitiated(_poolId, user, _shareAmount);
        //inlines the modifier updateFundingAndPrices:
        _getUpdateLogic().updateFundingAndPricesAfter(iPerpetualId);
    }

    /**
     * Execute liquidity withdrawal that has previously been initiated via
     * function withdrawLiquidity
     * Anyone can start the withdrawal and earn a fee so that LPs are incentivized to remove
     * the liquidity after announcing and not lose that fee
     * @param _poolId pool id from which we want to withdraw
     * @param _lpAddr address of the liquidity provider that initiated a withdrawal request
     */
    function executeLiquidityWithdrawal(
        uint8 _poolId,
        address _lpAddr
    ) external override nonReentrant whenNotPaused {
        require(!liquidityProvisionIsPaused[_poolId], "LP paused");
        require(lpWithdrawMap[_lpAddr][_poolId].shareTokens > 0, "init withdrwl");
        require(
            block.timestamp >= lpWithdrawMap[_lpAddr][_poolId].withdrawTimestamp + _getDelay(),
            "too early"
        );
        //inlines the modifier updateFundingAndPrices: updateFundingAndPrices
        uint24 iPerpetualId = _selectPerpetualIds(_poolId);
        _getUpdateLogic().updateFundingAndPricesBefore(iPerpetualId, false);

        // main part
        LiquidityPoolData storage pool = liquidityPools[_poolId];
        _isLPWithdrawValid(pool);
        uint256 shareAmount = lpWithdrawMap[_lpAddr][_poolId].shareTokens;

        int128 fTokenAmountToReturn = _getTokenAmountToReturn(
            pool,
            int256(shareAmount).fromDec18() // share tokens always have 18 decimals
        );
        IShareToken(pool.shareTokenAddress).burn(_lpAddr, shareAmount);
        pool.totalSupplyShareToken = uint128(IShareToken(pool.shareTokenAddress).totalSupply());
        _getRebalanceLogic().decreasePoolCash(pool.id, fTokenAmountToReturn);
        if (
            msg.sender != _lpAddr &&
            block.timestamp >= lpWithdrawMap[_lpAddr][_poolId].withdrawTimestamp + 2 * _getDelay()
        ) {
            // the lp waited too long, hence the one who executes earns 1% of the LP return
            int128 fFee = fTokenAmountToReturn.mul(ONE_PERCENT);
            if (fFee > pool.fBrokerCollateralLotSize) {
                // fee is capped at 1 broker lot size
                fFee = pool.fBrokerCollateralLotSize;
            }
            fTokenAmountToReturn = fTokenAmountToReturn.sub(fFee);
            _transferFromVaultToUser(pool, msg.sender, fFee);
        }
        _transferFromVaultToUser(pool, _lpAddr, fTokenAmountToReturn);
        activeWithdrawals[_poolId].remove(_lpAddr);
        delete lpWithdrawMap[_lpAddr][_poolId];
        uint256 tokenAmountDecN = fTokenAmountToReturn.toUDecN(pool.marginTokenDecimals);
        emit LiquidityRemoved(_poolId, _lpAddr, tokenAmountDecN, shareAmount);

        //inlines the modifier updateFundingAndPrices:
        _getUpdateLogic().updateFundingAndPricesAfter(iPerpetualId);
    }

    /**
     * @notice  Validates input data.
     *
     * @param   _poolId      Reference to liquidity pool
     * @param   _amount      The amount of token to add. dec18
     */
    function _validateLiquidityData(uint8 _poolId, uint256 _amount) internal view {
        require(_poolId > 0 && _poolId <= iPoolCount, "pool index out of range");
        require(_amount > 0, "invalid amount");
    }

    /**
     * @notice Checks pool state.
     * @dev throws error if pool isn't running or doesn't have active perpetuals
     *
     * @param   _pool    Reference to liquidity pool
     */
    function _checkPoolState(LiquidityPoolData storage _pool) internal view {
        require(_pool.isRunning, "not running");
        uint256 length = perpetualIds[_pool.id].length;
        bool isActivePerpetuals;
        for (uint256 i = 0; i < length; i++) {
            uint24 id = perpetualIds[_pool.id][i];
            PerpetualData storage perpetual = perpetuals[_pool.id][id];
            if (perpetual.state == PerpetualState.NORMAL) {
                isActivePerpetuals = true;
                break;
            }
        }
        require(isActivePerpetuals, "no active perpetual");
    }

    /**
     * @notice Checks whether LP can remove liquidity: no pool in emergency
     * and at least one pool cleared or normal
     * @dev throws error if pool isn't running or doesn't have active perpetuals
     *
     * @param   _pool    Reference to liquidity pool
     */
    function _isLPWithdrawValid(LiquidityPoolData storage _pool) internal view {
        require(_pool.isRunning, "not running");
        uint256 length = perpetualIds[_pool.id].length;
        bool isWithdrawValid;
        for (uint256 i = 0; i < length; i++) {
            uint24 id = perpetualIds[_pool.id][i];
            PerpetualData storage perpetual = perpetuals[_pool.id][id];
            require(perpetual.state != PerpetualState.EMERGENCY, "no withdraw in emergency");
            if (
                perpetual.state == PerpetualState.NORMAL ||
                perpetual.state == PerpetualState.CLEARED
            ) {
                isWithdrawValid = true;
            }
        }
        require(isWithdrawValid, "no active perpetual");
    }

    /**
     * @notice  Calculates amount of share tokens to be minted.
     *
     * @param   _pool        Reference to liquidity pool
     * @param   _fAmount     The amount of token to add. 64.64 float
     * @return shares to mint for the given token amount in ABDK64.64 format
     */
    function _getShareAmountToMint(
        LiquidityPoolData storage _pool,
        int128 _fAmount
    ) internal view returns (int128) {
        int128 fShareTotalSupply = int256(IERC20(_pool.shareTokenAddress).totalSupply())
            .fromDec18();
        int128 fShareToMint;
        if (fShareTotalSupply == 0) {
            fShareToMint = _fAmount;
        } else {
            fShareToMint = _fAmount.mul(fShareTotalSupply).div(_pool.fPnLparticipantsCashCC);
        }
        return fShareToMint;
    }

    /**
     * @notice  Calculates amount of tokens to be returned.
     *
     * @param   _pool        Reference to liquidity pool
     * @param   _fShareAmount     The amount of share token to burn. 64.64 float
     */
    function _getTokenAmountToReturn(
        LiquidityPoolData storage _pool,
        int128 _fShareAmount
    ) internal view returns (int128) {
        int128 fShareTotalSupply = int256(IERC20(_pool.shareTokenAddress).totalSupply())
            .fromDec18();
        int128 fTokenAmountToReturn;
        if (_pool.fPnLparticipantsCashCC > 0) {
            fTokenAmountToReturn = _fShareAmount.mul(_pool.fPnLparticipantsCashCC).div(
                fShareTotalSupply
            );
        }
        return fTokenAmountToReturn;
    }

    /**
     * Get the current share-token price for the given pool.
     * Price is in decimal 18 format
     * @param _poolId id of the pool
     * @return price in decimal-18 format
     */
    function getShareTokenPriceD18(uint8 _poolId) external view override returns (uint256 price) {
        PerpStorage.LiquidityPoolData storage pool = liquidityPools[_poolId];
        int128 fShareTotalSupply = int256(IERC20(pool.shareTokenAddress).totalSupply())
            .fromDec18();
        if (fShareTotalSupply > 0) {
            price = (pool.fPnLparticipantsCashCC).div(fShareTotalSupply).toUDec18();
        } else {
            price = 1e18; //=1
        }
    }

    /**
     * To re-balance the AMM margin to the initial margin for a maximum of 10 perpetuals
     * in the given pool.
     * @dev test in moduloLoop.py
     * @param   _pool Reference to liquidity pool
     */
    function _rebalance(LiquidityPoolData storage _pool) internal {
        uint256 length = _pool.iPerpetualCount;
        // restrict to a maximum of 10 perpetuals selected using block.number to avoid gas issues
        uint256 start = length > 10 ? (block.number % (length - 9)) : 0;
        uint256 end = length > 10 ? start + 10 : length;
        for (uint256 i = start; i < end; i++) {
            uint24 id = perpetualIds[_pool.id][i];
            _getRebalanceLogic().rebalance(id);
        }
    }

    /**
     * For a given amount of shares, we calculate the amount of
     * collateral tokens to return
     * @param _poolId id of the liquidity pool
     * @param _shareAmount amount of shares
     */
    function getTokenAmountToReturn(
        uint8 _poolId,
        uint256 _shareAmount
    ) external view override returns (uint256) {
        PerpStorage.LiquidityPoolData storage pool = liquidityPools[_poolId];
        return _getTokenAmountToReturn(pool, int256(_shareAmount).fromDec18()).toUDec18();
    }

    /**
     * Getter function to retrieve a given number of liquidity-provider WithdrawRequests for a poolId
     * @param _poolId id of the pool
     * @param _fromIdx start the address array at this index
     * @param _numRequests maximal number of responses
     */
    function getWithdrawRequests(
        uint8 _poolId,
        uint256 _fromIdx,
        uint256 _numRequests
    ) external view returns (WithdrawRequest[] memory) {
        // Get the addresses from activeWithdrawals
        address[] memory addresses = activeWithdrawals[_poolId].enumerateAll();
        WithdrawRequest[] memory requests = new WithdrawRequest[](_numRequests); // Create an array to store the WithdrawRequest structs
        uint256 counter = 0;
        for (uint256 i = _fromIdx; i < addresses.length && counter < _numRequests; i++) {
            // Get the WithdrawRequest for the address and poolId
            WithdrawRequest memory request = lpWithdrawMap[addresses[i]][_poolId];
            request.lp = addresses[i];
            requests[counter] = request; // Store the WithdrawRequest in the array
            counter++;
        }
        return requests;
    }

    function getFunctionList() external pure virtual override returns (bytes4[] memory, bytes32) {
        bytes32 moduleName = Utils.stringToBytes32("PerpetualTreasury");
        bytes4[] memory functionList = new bytes4[](8);
        functionList[0] = this.addLiquidity.selector;
        functionList[1] = this.withdrawLiquidity.selector;
        functionList[2] = this.executeLiquidityWithdrawal.selector;
        functionList[3] = this.getTokenAmountToReturn.selector;
        functionList[4] = this.getCollateralTokenAmountForPricing.selector;
        functionList[5] = this.pauseLiquidityProvision.selector;
        functionList[6] = this.getShareTokenPriceD18.selector;
        functionList[7] = this.getWithdrawRequests.selector;
        return (functionList, moduleName);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20PermitUpgradeable {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";
import "../extensions/IERC20PermitUpgradeable.sol";
import "../../../utils/AddressUpgradeable.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20Upgradeable {
    using AddressUpgradeable for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20PermitUpgradeable token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20Upgradeable token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && AddressUpgradeable.isContract(address(token));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(address from, address to, uint256 amount) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/introspection/ERC165Checker.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Library used to query support of an interface declared via {IERC165}.
 *
 * Note that these functions return the actual result of the query: they do not
 * `revert` if an interface is not supported. It is up to the caller to decide
 * what to do in these cases.
 */
library ERC165Checker {
    // As per the EIP-165 spec, no interface should ever match 0xffffffff
    bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;

    /**
     * @dev Returns true if `account` supports the {IERC165} interface.
     */
    function supportsERC165(address account) internal view returns (bool) {
        // Any contract that implements ERC165 must explicitly indicate support of
        // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
        return
            supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&
            !supportsERC165InterfaceUnchecked(account, _INTERFACE_ID_INVALID);
    }

    /**
     * @dev Returns true if `account` supports the interface defined by
     * `interfaceId`. Support for {IERC165} itself is queried automatically.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
        // query support of both ERC165 as per the spec and support of _interfaceId
        return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
    }

    /**
     * @dev Returns a boolean array where each value corresponds to the
     * interfaces passed in and whether they're supported or not. This allows
     * you to batch check interfaces for a contract where your expectation
     * is that some interfaces may not be supported.
     *
     * See {IERC165-supportsInterface}.
     *
     * _Available since v3.4._
     */
    function getSupportedInterfaces(
        address account,
        bytes4[] memory interfaceIds
    ) internal view returns (bool[] memory) {
        // an array of booleans corresponding to interfaceIds and whether they're supported or not
        bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);

        // query support of ERC165 itself
        if (supportsERC165(account)) {
            // query support of each interface in interfaceIds
            for (uint256 i = 0; i < interfaceIds.length; i++) {
                interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
            }
        }

        return interfaceIdsSupported;
    }

    /**
     * @dev Returns true if `account` supports all the interfaces defined in
     * `interfaceIds`. Support for {IERC165} itself is queried automatically.
     *
     * Batch-querying can lead to gas savings by skipping repeated checks for
     * {IERC165} support.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
        // query support of ERC165 itself
        if (!supportsERC165(account)) {
            return false;
        }

        // query support of each interface in interfaceIds
        for (uint256 i = 0; i < interfaceIds.length; i++) {
            if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
                return false;
            }
        }

        // all interfaces supported
        return true;
    }

    /**
     * @notice Query if a contract implements an interface, does not check ERC165 support
     * @param account The address of the contract to query for support of an interface
     * @param interfaceId The interface identifier, as specified in ERC-165
     * @return true if the contract at account indicates support of the interface with
     * identifier interfaceId, false otherwise
     * @dev Assumes that account contains a contract that supports ERC165, otherwise
     * the behavior of this method is undefined. This precondition can be checked
     * with {supportsERC165}.
     *
     * Some precompiled contracts will falsely indicate support for a given interface, so caution
     * should be exercised when using this function.
     *
     * Interface identification is specified in ERC-165.
     */
    function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
        // prepare call
        bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId);

        // perform static call
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly {
            success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0x00)
        }

        return success && returnSize >= 0x20 && returnValue > 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165Storage.sol)

pragma solidity ^0.8.0;

import "./ERC165.sol";

/**
 * @dev Storage based implementation of the {IERC165} interface.
 *
 * Contracts may inherit from this and call {_registerInterface} to declare
 * their support of an interface.
 */
abstract contract ERC165Storage is ERC165 {
    /**
     * @dev Mapping of interface ids to whether or not it's supported.
     */
    mapping(bytes4 => bool) private _supportedInterfaces;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return super.supportsInterface(interfaceId) || _supportedInterfaces[interfaceId];
    }

    /**
     * @dev Registers the contract as an implementer of the interface defined by
     * `interfaceId`. Support of the actual ERC165 interface is automatic and
     * registering its interface id is not required.
     *
     * See {IERC165-supportsInterface}.
     *
     * Requirements:
     *
     * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
     */
    function _registerInterface(bytes4 interfaceId) internal virtual {
        require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
        _supportedInterfaces[interfaceId] = true;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import "./PythStructs.sol";
import "./IPythEvents.sol";

/// @title Consume prices from the Pyth Network (https://pyth.network/).
/// @dev Please refer to the guidance at https://docs.pyth.network/consumers/best-practices for how to consume prices safely.
/// @author Pyth Data Association
interface IPyth is IPythEvents {
    /// @notice Returns the period (in seconds) that a price feed is considered valid since its publish time
    function getValidTimePeriod() external view returns (uint validTimePeriod);

    /// @notice Returns the price and confidence interval.
    /// @dev Reverts if the price has not been updated within the last `getValidTimePeriod()` seconds.
    /// @param id The Pyth Price Feed ID of which to fetch the price and confidence interval.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getPrice(
        bytes32 id
    ) external view returns (PythStructs.Price memory price);

    /// @notice Returns the exponentially-weighted moving average price and confidence interval.
    /// @dev Reverts if the EMA price is not available.
    /// @param id The Pyth Price Feed ID of which to fetch the EMA price and confidence interval.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getEmaPrice(
        bytes32 id
    ) external view returns (PythStructs.Price memory price);

    /// @notice Returns the price of a price feed without any sanity checks.
    /// @dev This function returns the most recent price update in this contract without any recency checks.
    /// This function is unsafe as the returned price update may be arbitrarily far in the past.
    ///
    /// Users of this function should check the `publishTime` in the price to ensure that the returned price is
    /// sufficiently recent for their application. If you are considering using this function, it may be
    /// safer / easier to use either `getPrice` or `getPriceNoOlderThan`.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getPriceUnsafe(
        bytes32 id
    ) external view returns (PythStructs.Price memory price);

    /// @notice Returns the price that is no older than `age` seconds of the current time.
    /// @dev This function is a sanity-checked version of `getPriceUnsafe` which is useful in
    /// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
    /// recently.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getPriceNoOlderThan(
        bytes32 id,
        uint age
    ) external view returns (PythStructs.Price memory price);

    /// @notice Returns the exponentially-weighted moving average price of a price feed without any sanity checks.
    /// @dev This function returns the same price as `getEmaPrice` in the case where the price is available.
    /// However, if the price is not recent this function returns the latest available price.
    ///
    /// The returned price can be from arbitrarily far in the past; this function makes no guarantees that
    /// the returned price is recent or useful for any particular application.
    ///
    /// Users of this function should check the `publishTime` in the price to ensure that the returned price is
    /// sufficiently recent for their application. If you are considering using this function, it may be
    /// safer / easier to use either `getEmaPrice` or `getEmaPriceNoOlderThan`.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getEmaPriceUnsafe(
        bytes32 id
    ) external view returns (PythStructs.Price memory price);

    /// @notice Returns the exponentially-weighted moving average price that is no older than `age` seconds
    /// of the current time.
    /// @dev This function is a sanity-checked version of `getEmaPriceUnsafe` which is useful in
    /// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
    /// recently.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getEmaPriceNoOlderThan(
        bytes32 id,
        uint age
    ) external view returns (PythStructs.Price memory price);

    /// @notice Update price feeds with given update messages.
    /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
    /// `getUpdateFee` with the length of the `updateData` array.
    /// Prices will be updated if they are more recent than the current stored prices.
    /// The call will succeed even if the update is not the most recent.
    /// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid.
    /// @param updateData Array of price update data.
    function updatePriceFeeds(bytes[] calldata updateData) external payable;

    /// @notice Wrapper around updatePriceFeeds that rejects fast if a price update is not necessary. A price update is
    /// necessary if the current on-chain publishTime is older than the given publishTime. It relies solely on the
    /// given `publishTimes` for the price feeds and does not read the actual price update publish time within `updateData`.
    ///
    /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
    /// `getUpdateFee` with the length of the `updateData` array.
    ///
    /// `priceIds` and `publishTimes` are two arrays with the same size that correspond to senders known publishTime
    /// of each priceId when calling this method. If all of price feeds within `priceIds` have updated and have
    /// a newer or equal publish time than the given publish time, it will reject the transaction to save gas.
    /// Otherwise, it calls updatePriceFeeds method to update the prices.
    ///
    /// @dev Reverts if update is not needed or the transferred fee is not sufficient or the updateData is invalid.
    /// @param updateData Array of price update data.
    /// @param priceIds Array of price ids.
    /// @param publishTimes Array of publishTimes. `publishTimes[i]` corresponds to known `publishTime` of `priceIds[i]`
    function updatePriceFeedsIfNecessary(
        bytes[] calldata updateData,
        bytes32[] calldata priceIds,
        uint64[] calldata publishTimes
    ) external payable;

    /// @notice Returns the required fee to update an array of price updates.
    /// @param updateData Array of price update data.
    /// @return feeAmount The required fee in Wei.
    function getUpdateFee(
        bytes[] calldata updateData
    ) external view returns (uint feeAmount);

    /// @notice Parse `updateData` and return price feeds of the given `priceIds` if they are all published
    /// within `minPublishTime` and `maxPublishTime`.
    ///
    /// You can use this method if you want to use a Pyth price at a fixed time and not the most recent price;
    /// otherwise, please consider using `updatePriceFeeds`. This method does not store the price updates on-chain.
    ///
    /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
    /// `getUpdateFee` with the length of the `updateData` array.
    ///
    ///
    /// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
    /// no update for any of the given `priceIds` within the given time range.
    /// @param updateData Array of price update data.
    /// @param priceIds Array of price ids.
    /// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
    /// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
    /// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
    function parsePriceFeedUpdates(
        bytes[] calldata updateData,
        bytes32[] calldata priceIds,
        uint64 minPublishTime,
        uint64 maxPublishTime
    ) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
}

File 18 of 48 : IPythEvents.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @title IPythEvents contains the events that Pyth contract emits.
/// @dev This interface can be used for listening to the updates for off-chain and testing purposes.
interface IPythEvents {
    /// @dev Emitted when the price feed with `id` has received a fresh update.
    /// @param id The Pyth Price Feed ID.
    /// @param publishTime Publish time of the given price update.
    /// @param price Price of the given price update.
    /// @param conf Confidence interval of the given price update.
    event PriceFeedUpdate(
        bytes32 indexed id,
        uint64 publishTime,
        int64 price,
        uint64 conf
    );

    /// @dev Emitted when a batch price update is processed successfully.
    /// @param chainId ID of the source chain that the batch price update comes from.
    /// @param sequenceNumber Sequence number of the batch price update.
    event BatchPriceFeedUpdate(uint16 chainId, uint64 sequenceNumber);
}

File 19 of 48 : PythErrors.sol
// SPDX-License-Identifier: Apache 2

pragma solidity ^0.8.0;

library PythErrors {
    // Function arguments are invalid (e.g., the arguments lengths mismatch)
    error InvalidArgument();
    // Update data is coming from an invalid data source.
    error InvalidUpdateDataSource();
    // Update data is invalid (e.g., deserialization error)
    error InvalidUpdateData();
    // Insufficient fee is paid to the method.
    error InsufficientFee();
    // There is no fresh update, whereas expected fresh updates.
    error NoFreshUpdate();
    // There is no price feed found within the given range or it does not exists.
    error PriceFeedNotFoundWithinRange();
    // Price feed not found or it is not pushed on-chain yet.
    error PriceFeedNotFound();
    // Requested price is stale.
    error StalePrice();
    // Given message is not a valid Wormhole VAA.
    error InvalidWormholeVaa();
    // Governance message is invalid (e.g., deserialization error).
    error InvalidGovernanceMessage();
    // Governance message is not for this contract.
    error InvalidGovernanceTarget();
    // Governance message is coming from an invalid data source.
    error InvalidGovernanceDataSource();
    // Governance message is old.
    error OldGovernanceMessage();
}

File 20 of 48 : PythStructs.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

contract PythStructs {
    // A price with a degree of uncertainty, represented as a price +- a confidence interval.
    //
    // The confidence interval roughly corresponds to the standard error of a normal distribution.
    // Both the price and confidence are stored in a fixed-point numeric representation,
    // `x * (10^expo)`, where `expo` is the exponent.
    //
    // Please refer to the documentation at https://docs.pyth.network/consumers/best-practices for how
    // to how this price safely.
    struct Price {
        // Price
        int64 price;
        // Confidence interval around the price
        uint64 conf;
        // Price exponent
        int32 expo;
        // Unix timestamp describing when the price was published
        uint publishTime;
    }

    // PriceFeed represents a current aggregate price from pyth publisher feeds.
    struct PriceFeed {
        // The price ID.
        bytes32 id;
        // Latest available price
        Price price;
        // Latest available exponentially-weighted moving average price
        Price emaPrice;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
// D8X, 2022

pragma solidity 0.8.21;

/**
 * This is a modified version of the OpenZeppelin ownable contract
 * Modifications
 * - instead of an owner, we have two actors: maintainer and governance
 * - maintainer can have certain priviledges but cannot transfer maintainer mandate
 * - governance can exchange maintainer and exchange itself
 * - renounceOwnership is removed
 *
 *
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Maintainable {
    address private _maintainer;
    address private _governance;

    event MaintainerTransferred(address indexed previousMaintainer, address indexed newMaintainer);
    event GovernanceTransferred(address indexed previousGovernance, address indexed newGovernance);

    /**
     * @dev Initializes the contract setting the deployer as the initial maintainer.
     */
    constructor() {
        _transferMaintainer(msg.sender);
        _transferGovernance(msg.sender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function maintainer() public view virtual returns (address) {
        return _maintainer;
    }

    /**
     * @dev Returns the address of the governance.
     */
    function governance() public view virtual returns (address) {
        return _governance;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyMaintainer() {
        require(maintainer() == msg.sender, "only maintainer");
        _;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyGovernance() {
        require(governance() == msg.sender, "only governance");
        _;
    }

    /**
     * @dev Transfers maintainer mandate of the contract to a new account (`newMaintainer`).
     * Can only be called by the governance.
     */
    function transferMaintainer(address newMaintainer) public virtual {
        require(msg.sender == _governance, "only governance");
        require(newMaintainer != address(0), "zero address");
        _transferMaintainer(newMaintainer);
    }

    /**
     * @dev Transfers governance mandate of the contract to a new account (`newGovernance`).
     * Can only be called by the governance.
     */
    function transferGovernance(address newGovernance) public virtual {
        require(msg.sender == _governance, "only governance");
        require(newGovernance != address(0), "zero address");
        _transferGovernance(newGovernance);
    }

    /**
     * @dev Transfers maintainer of the contract to a new account (`newMaintainer`).
     * Internal function without access restriction.
     */
    function _transferMaintainer(address newMaintainer) internal virtual {
        address oldM = _maintainer;
        _maintainer = newMaintainer;
        emit MaintainerTransferred(oldM, newMaintainer);
    }

    /**
     * @dev Transfers governance of the contract to a new account (`newGovernance`).
     * Internal function without access restriction.
     */
    function _transferGovernance(address newGovernance) internal virtual {
        address oldG = _governance;
        _governance = newGovernance;
        emit GovernanceTransferred(oldG, newGovernance);
    }
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.21;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IShareToken is IERC20 {
    function mint(address _account, uint256 _amount) external;

    function setTransferRestricted(address _account) external;

    function burn(address _account, uint256 _amount) external;
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.21;

interface IShareTokenFactory {
    function createShareToken(uint8 _poolId, address _marginTokenAddr) external returns (address);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.21;

interface ISpotOracle {
    /**
     * @dev The market is closed if the market is not in its regular trading period.
     */
    function isMarketClosed() external view returns (bool);

    function setMarketClosed(bool _marketClosed) external;

    /**
     *  Spot price.
     */
    function getSpotPrice() external view returns (int128, uint256);

    /**
     * Get base currency symbol.
     */
    function getBaseCurrency() external view returns (bytes4);

    /**
     * Get quote currency symbol.
     */
    function getQuoteCurrency() external view returns (bytes4);

    /**
     * Price Id
     */
    function priceId() external view returns (bytes32);

    /**
     * Address of the underlying feed.
     */
    function priceFeed() external view returns (address);

    /**
     * Conservative update period of this feed in seconds.
     */
    function feedPeriod() external view returns (uint256);
}

// SPDX-License-Identifier: BSD-4-Clause
/*
 * ABDK Math 64.64 Smart Contract Library.  Copyright © 2019 by ABDK Consulting.
 * Author: Mikhail Vladimirov <[email protected]>
 */
pragma solidity 0.8.21;

/**
 * Smart contract library of mathematical functions operating with signed
 * 64.64-bit fixed point numbers.  Signed 64.64-bit fixed point number is
 * basically a simple fraction whose numerator is signed 128-bit integer and
 * denominator is 2^64.  As long as denominator is always the same, there is no
 * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are
 * represented by int128 type holding only the numerator.
 */
library ABDKMath64x64 {
    /*
     * Minimum value signed 64.64-bit fixed point number may have.
     */
    int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;

    /*
     * Maximum value signed 64.64-bit fixed point number may have.
     */
    int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

    /**
     * Convert signed 256-bit integer number into signed 64.64-bit fixed point
     * number.  Revert on overflow.
     *
     * @param x signed 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function fromInt(int256 x) internal pure returns (int128) {
        require(x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF, "ABDK.fromInt");
        return int128(x << 64);
    }

    /**
     * Convert signed 64.64 fixed point number into signed 64-bit integer number
     * rounding down.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64-bit integer number
     */
    function toInt(int128 x) internal pure returns (int64) {
        return int64(x >> 64);
    }

    /**
     * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point
     * number.  Revert on overflow.
     *
     * @param x unsigned 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function fromUInt(uint256 x) internal pure returns (int128) {
        require(x <= 0x7FFFFFFFFFFFFFFF, "ABDK.fromUInt");
        return int128(int256(x << 64));
    }

    /**
     * Convert signed 64.64 fixed point number into unsigned 64-bit integer
     * number rounding down.  Revert on underflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return unsigned 64-bit integer number
     */
    function toUInt(int128 x) internal pure returns (uint64) {
        require(x >= 0, "ABDK.toUInt");
        return uint64(uint128(x >> 64));
    }

    /**
     * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point
     * number rounding down.  Revert on overflow.
     *
     * @param x signed 128.128-bin fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function from128x128(int256 x) internal pure returns (int128) {
        int256 result = x >> 64;
        require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.from128x128");
        return int128(result);
    }

    /**
     * Convert signed 64.64 fixed point number into signed 128.128 fixed point
     * number.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 128.128 fixed point number
     */
    function to128x128(int128 x) internal pure returns (int256) {
        return int256(x) << 64;
    }

    /**
     * Calculate x + y.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function add(int128 x, int128 y) internal pure returns (int128) {
        int256 result = int256(x) + y;
        require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.add");
        return int128(result);
    }

    /**
     * Calculate x - y.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function sub(int128 x, int128 y) internal pure returns (int128) {
        int256 result = int256(x) - y;
        require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.sub");
        return int128(result);
    }

    /**
     * Calculate x * y rounding down.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function mul(int128 x, int128 y) internal pure returns (int128) {
        int256 result = (int256(x) * y) >> 64;
        require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.mul");
        return int128(result);
    }

    /**
     * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point
     * number and y is signed 256-bit integer number.  Revert on overflow.
     *
     * @param x signed 64.64 fixed point number
     * @param y signed 256-bit integer number
     * @return signed 256-bit integer number
     */
    function muli(int128 x, int256 y) internal pure returns (int256) {
        if (x == MIN_64x64) {
            require(
                y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF &&
                    y <= 0x1000000000000000000000000000000000000000000000000,
                "ABDK.muli-1"
            );
            return -y << 63;
        } else {
            bool negativeResult = false;
            if (x < 0) {
                x = -x;
                negativeResult = true;
            }
            if (y < 0) {
                y = -y;
                // We rely on overflow behavior here
                negativeResult = !negativeResult;
            }
            uint256 absoluteResult = mulu(x, uint256(y));
            if (negativeResult) {
                require(
                    absoluteResult <=
                        0x8000000000000000000000000000000000000000000000000000000000000000,
                    "ABDK.muli-2"
                );
                return -int256(absoluteResult);
                // We rely on overflow behavior here
            } else {
                require(
                    absoluteResult <=
                        0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,
                    "ABDK.muli-3"
                );
                return int256(absoluteResult);
            }
        }
    }

    /**
     * Calculate x * y rounding down, where x is signed 64.64 fixed point number
     * and y is unsigned 256-bit integer number.  Revert on overflow.
     *
     * @param x signed 64.64 fixed point number
     * @param y unsigned 256-bit integer number
     * @return unsigned 256-bit integer number
     */
    function mulu(int128 x, uint256 y) internal pure returns (uint256) {
        if (y == 0) return 0;

        require(x >= 0, "ABDK.mulu-1");

        uint256 lo = (uint256(int256(x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64;
        uint256 hi = uint256(int256(x)) * (y >> 128);

        require(hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "ABDK.mulu-2");
        hi <<= 64;

        require(
            hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo,
            "ABDK.mulu-3"
        );
        return hi + lo;
    }

    /**
     * Calculate x / y rounding towards zero.  Revert on overflow or when y is
     * zero.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function div(int128 x, int128 y) internal pure returns (int128) {
        require(y != 0, "ABDK.div-1");
        int256 result = (int256(x) << 64) / y;
        require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.div-2");
        return int128(result);
    }

    /**
     * Calculate x / y rounding towards zero, where x and y are signed 256-bit
     * integer numbers.  Revert on overflow or when y is zero.
     *
     * @param x signed 256-bit integer number
     * @param y signed 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function divi(int256 x, int256 y) internal pure returns (int128) {
        require(y != 0, "ABDK.divi-1");

        bool negativeResult = false;
        if (x < 0) {
            x = -x;
            // We rely on overflow behavior here
            negativeResult = true;
        }
        if (y < 0) {
            y = -y;
            // We rely on overflow behavior here
            negativeResult = !negativeResult;
        }
        uint128 absoluteResult = divuu(uint256(x), uint256(y));
        if (negativeResult) {
            require(absoluteResult <= 0x80000000000000000000000000000000, "ABDK.divi-2");
            return -int128(absoluteResult);
            // We rely on overflow behavior here
        } else {
            require(absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "ABDK.divi-3");
            return int128(absoluteResult);
            // We rely on overflow behavior here
        }
    }

    /**
     * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
     * integer numbers.  Revert on overflow or when y is zero.
     *
     * @param x unsigned 256-bit integer number
     * @param y unsigned 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function divu(uint256 x, uint256 y) internal pure returns (int128) {
        require(y != 0, "ABDK.divu-1");
        uint128 result = divuu(x, y);
        require(result <= uint128(MAX_64x64), "ABDK.divu-2");
        return int128(result);
    }

    /**
     * Calculate -x.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function neg(int128 x) internal pure returns (int128) {
        require(x != MIN_64x64, "ABDK.neg");
        return -x;
    }

    /**
     * Calculate |x|.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function abs(int128 x) internal pure returns (int128) {
        require(x != MIN_64x64, "ABDK.abs");
        return x < 0 ? -x : x;
    }

    /**
     * Calculate 1 / x rounding towards zero.  Revert on overflow or when x is
     * zero.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function inv(int128 x) internal pure returns (int128) {
        require(x != 0, "ABDK.inv-1");
        int256 result = int256(0x100000000000000000000000000000000) / x;
        require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.inv-2");
        return int128(result);
    }

    /**
     * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function avg(int128 x, int128 y) internal pure returns (int128) {
        return int128((int256(x) + int256(y)) >> 1);
    }

    /**
     * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down.
     * Revert on overflow or in case x * y is negative.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function gavg(int128 x, int128 y) internal pure returns (int128) {
        int256 m = int256(x) * int256(y);
        require(m >= 0, "ABDK.gavg-1");
        require(
            m < 0x4000000000000000000000000000000000000000000000000000000000000000,
            "ABDK.gavg-2"
        );
        return int128(sqrtu(uint256(m)));
    }

    /**
     * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number
     * and y is unsigned 256-bit integer number.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y uint256 value
     * @return signed 64.64-bit fixed point number
     */
    function pow(int128 x, uint256 y) internal pure returns (int128) {
        bool negative = x < 0 && y & 1 == 1;

        uint256 absX = uint128(x < 0 ? -x : x);
        uint256 absResult;
        absResult = 0x100000000000000000000000000000000;

        if (absX <= 0x10000000000000000) {
            absX <<= 63;
            while (y != 0) {
                if (y & 0x1 != 0) {
                    absResult = (absResult * absX) >> 127;
                }
                absX = (absX * absX) >> 127;

                if (y & 0x2 != 0) {
                    absResult = (absResult * absX) >> 127;
                }
                absX = (absX * absX) >> 127;

                if (y & 0x4 != 0) {
                    absResult = (absResult * absX) >> 127;
                }
                absX = (absX * absX) >> 127;

                if (y & 0x8 != 0) {
                    absResult = (absResult * absX) >> 127;
                }
                absX = (absX * absX) >> 127;

                y >>= 4;
            }

            absResult >>= 64;
        } else {
            uint256 absXShift = 63;
            if (absX < 0x1000000000000000000000000) {
                absX <<= 32;
                absXShift -= 32;
            }
            if (absX < 0x10000000000000000000000000000) {
                absX <<= 16;
                absXShift -= 16;
            }
            if (absX < 0x1000000000000000000000000000000) {
                absX <<= 8;
                absXShift -= 8;
            }
            if (absX < 0x10000000000000000000000000000000) {
                absX <<= 4;
                absXShift -= 4;
            }
            if (absX < 0x40000000000000000000000000000000) {
                absX <<= 2;
                absXShift -= 2;
            }
            if (absX < 0x80000000000000000000000000000000) {
                absX <<= 1;
                absXShift -= 1;
            }

            uint256 resultShift;
            while (y != 0) {
                require(absXShift < 64, "ABDK.pow-1");

                if (y & 0x1 != 0) {
                    absResult = (absResult * absX) >> 127;
                    resultShift += absXShift;
                    if (absResult > 0x100000000000000000000000000000000) {
                        absResult >>= 1;
                        resultShift += 1;
                    }
                }
                absX = (absX * absX) >> 127;
                absXShift <<= 1;
                if (absX >= 0x100000000000000000000000000000000) {
                    absX >>= 1;
                    absXShift += 1;
                }

                y >>= 1;
            }

            require(resultShift < 64, "ABDK.pow-2");
            absResult >>= 64 - resultShift;
        }
        int256 result = negative ? -int256(absResult) : int256(absResult);
        require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.pow-3");
        return int128(result);
    }

    /**
     * Calculate sqrt (x) rounding down.  Revert if x < 0.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function sqrt(int128 x) internal pure returns (int128) {
        require(x >= 0, "ABDK.sqrt");
        return int128(sqrtu(uint256(int256(x)) << 64));
    }

    /**
     * Calculate binary logarithm of x.  Revert if x <= 0.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function log_2(int128 x) internal pure returns (int128) {
        require(x > 0, "ABDK.log_2");

        int256 msb;
        int256 xc = x;
        if (xc >= 0x10000000000000000) {
            xc >>= 64;
            msb += 64;
        }
        if (xc >= 0x100000000) {
            xc >>= 32;
            msb += 32;
        }
        if (xc >= 0x10000) {
            xc >>= 16;
            msb += 16;
        }
        if (xc >= 0x100) {
            xc >>= 8;
            msb += 8;
        }
        if (xc >= 0x10) {
            xc >>= 4;
            msb += 4;
        }
        if (xc >= 0x4) {
            xc >>= 2;
            msb += 2;
        }
        if (xc >= 0x2) msb += 1;
        // No need to shift xc anymore

        int256 result = (msb - 64) << 64;
        uint256 ux = uint256(int256(x)) << uint256(127 - msb);
        for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) {
            ux *= ux;
            uint256 b = ux >> 255;
            ux >>= 127 + b;
            result += bit * int256(b);
        }

        return int128(result);
    }

    /**
     * Calculate natural logarithm of x.  Revert if x <= 0.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function ln(int128 x) internal pure returns (int128) {
        unchecked {
            require(x > 0, "ABDK.ln");

            return
                int128(
                    int256((uint256(int256(log_2(x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF) >> 128)
                );
        }
    }

    /**
     * Calculate binary exponent of x.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function exp_2(int128 x) internal pure returns (int128) {
        require(x < 0x400000000000000000, "ABDK.exp_2-1");
        // Overflow

        if (x < -0x400000000000000000) return 0;
        // Underflow

        uint256 result = 0x80000000000000000000000000000000;

        if (x & 0x8000000000000000 > 0)
            result = (result * 0x16A09E667F3BCC908B2FB1366EA957D3E) >> 128;
        if (x & 0x4000000000000000 > 0)
            result = (result * 0x1306FE0A31B7152DE8D5A46305C85EDEC) >> 128;
        if (x & 0x2000000000000000 > 0)
            result = (result * 0x1172B83C7D517ADCDF7C8C50EB14A791F) >> 128;
        if (x & 0x1000000000000000 > 0)
            result = (result * 0x10B5586CF9890F6298B92B71842A98363) >> 128;
        if (x & 0x800000000000000 > 0)
            result = (result * 0x1059B0D31585743AE7C548EB68CA417FD) >> 128;
        if (x & 0x400000000000000 > 0)
            result = (result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8) >> 128;
        if (x & 0x200000000000000 > 0)
            result = (result * 0x10163DA9FB33356D84A66AE336DCDFA3F) >> 128;
        if (x & 0x100000000000000 > 0)
            result = (result * 0x100B1AFA5ABCBED6129AB13EC11DC9543) >> 128;
        if (x & 0x80000000000000 > 0)
            result = (result * 0x10058C86DA1C09EA1FF19D294CF2F679B) >> 128;
        if (x & 0x40000000000000 > 0)
            result = (result * 0x1002C605E2E8CEC506D21BFC89A23A00F) >> 128;
        if (x & 0x20000000000000 > 0)
            result = (result * 0x100162F3904051FA128BCA9C55C31E5DF) >> 128;
        if (x & 0x10000000000000 > 0)
            result = (result * 0x1000B175EFFDC76BA38E31671CA939725) >> 128;
        if (x & 0x8000000000000 > 0)
            result = (result * 0x100058BA01FB9F96D6CACD4B180917C3D) >> 128;
        if (x & 0x4000000000000 > 0)
            result = (result * 0x10002C5CC37DA9491D0985C348C68E7B3) >> 128;
        if (x & 0x2000000000000 > 0)
            result = (result * 0x1000162E525EE054754457D5995292026) >> 128;
        if (x & 0x1000000000000 > 0)
            result = (result * 0x10000B17255775C040618BF4A4ADE83FC) >> 128;
        if (x & 0x800000000000 > 0) result = (result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB) >> 128;
        if (x & 0x400000000000 > 0) result = (result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9) >> 128;
        if (x & 0x200000000000 > 0) result = (result * 0x10000162E43F4F831060E02D839A9D16D) >> 128;
        if (x & 0x100000000000 > 0) result = (result * 0x100000B1721BCFC99D9F890EA06911763) >> 128;
        if (x & 0x80000000000 > 0) result = (result * 0x10000058B90CF1E6D97F9CA14DBCC1628) >> 128;
        if (x & 0x40000000000 > 0) result = (result * 0x1000002C5C863B73F016468F6BAC5CA2B) >> 128;
        if (x & 0x20000000000 > 0) result = (result * 0x100000162E430E5A18F6119E3C02282A5) >> 128;
        if (x & 0x10000000000 > 0) result = (result * 0x1000000B1721835514B86E6D96EFD1BFE) >> 128;
        if (x & 0x8000000000 > 0) result = (result * 0x100000058B90C0B48C6BE5DF846C5B2EF) >> 128;
        if (x & 0x4000000000 > 0) result = (result * 0x10000002C5C8601CC6B9E94213C72737A) >> 128;
        if (x & 0x2000000000 > 0) result = (result * 0x1000000162E42FFF037DF38AA2B219F06) >> 128;
        if (x & 0x1000000000 > 0) result = (result * 0x10000000B17217FBA9C739AA5819F44F9) >> 128;
        if (x & 0x800000000 > 0) result = (result * 0x1000000058B90BFCDEE5ACD3C1CEDC823) >> 128;
        if (x & 0x400000000 > 0) result = (result * 0x100000002C5C85FE31F35A6A30DA1BE50) >> 128;
        if (x & 0x200000000 > 0) result = (result * 0x10000000162E42FF0999CE3541B9FFFCF) >> 128;
        if (x & 0x100000000 > 0) result = (result * 0x100000000B17217F80F4EF5AADDA45554) >> 128;
        if (x & 0x80000000 > 0) result = (result * 0x10000000058B90BFBF8479BD5A81B51AD) >> 128;
        if (x & 0x40000000 > 0) result = (result * 0x1000000002C5C85FDF84BD62AE30A74CC) >> 128;
        if (x & 0x20000000 > 0) result = (result * 0x100000000162E42FEFB2FED257559BDAA) >> 128;
        if (x & 0x10000000 > 0) result = (result * 0x1000000000B17217F7D5A7716BBA4A9AE) >> 128;
        if (x & 0x8000000 > 0) result = (result * 0x100000000058B90BFBE9DDBAC5E109CCE) >> 128;
        if (x & 0x4000000 > 0) result = (result * 0x10000000002C5C85FDF4B15DE6F17EB0D) >> 128;
        if (x & 0x2000000 > 0) result = (result * 0x1000000000162E42FEFA494F1478FDE05) >> 128;
        if (x & 0x1000000 > 0) result = (result * 0x10000000000B17217F7D20CF927C8E94C) >> 128;
        if (x & 0x800000 > 0) result = (result * 0x1000000000058B90BFBE8F71CB4E4B33D) >> 128;
        if (x & 0x400000 > 0) result = (result * 0x100000000002C5C85FDF477B662B26945) >> 128;
        if (x & 0x200000 > 0) result = (result * 0x10000000000162E42FEFA3AE53369388C) >> 128;
        if (x & 0x100000 > 0) result = (result * 0x100000000000B17217F7D1D351A389D40) >> 128;
        if (x & 0x80000 > 0) result = (result * 0x10000000000058B90BFBE8E8B2D3D4EDE) >> 128;
        if (x & 0x40000 > 0) result = (result * 0x1000000000002C5C85FDF4741BEA6E77E) >> 128;
        if (x & 0x20000 > 0) result = (result * 0x100000000000162E42FEFA39FE95583C2) >> 128;
        if (x & 0x10000 > 0) result = (result * 0x1000000000000B17217F7D1CFB72B45E1) >> 128;
        if (x & 0x8000 > 0) result = (result * 0x100000000000058B90BFBE8E7CC35C3F0) >> 128;
        if (x & 0x4000 > 0) result = (result * 0x10000000000002C5C85FDF473E242EA38) >> 128;
        if (x & 0x2000 > 0) result = (result * 0x1000000000000162E42FEFA39F02B772C) >> 128;
        if (x & 0x1000 > 0) result = (result * 0x10000000000000B17217F7D1CF7D83C1A) >> 128;
        if (x & 0x800 > 0) result = (result * 0x1000000000000058B90BFBE8E7BDCBE2E) >> 128;
        if (x & 0x400 > 0) result = (result * 0x100000000000002C5C85FDF473DEA871F) >> 128;
        if (x & 0x200 > 0) result = (result * 0x10000000000000162E42FEFA39EF44D91) >> 128;
        if (x & 0x100 > 0) result = (result * 0x100000000000000B17217F7D1CF79E949) >> 128;
        if (x & 0x80 > 0) result = (result * 0x10000000000000058B90BFBE8E7BCE544) >> 128;
        if (x & 0x40 > 0) result = (result * 0x1000000000000002C5C85FDF473DE6ECA) >> 128;
        if (x & 0x20 > 0) result = (result * 0x100000000000000162E42FEFA39EF366F) >> 128;
        if (x & 0x10 > 0) result = (result * 0x1000000000000000B17217F7D1CF79AFA) >> 128;
        if (x & 0x8 > 0) result = (result * 0x100000000000000058B90BFBE8E7BCD6D) >> 128;
        if (x & 0x4 > 0) result = (result * 0x10000000000000002C5C85FDF473DE6B2) >> 128;
        if (x & 0x2 > 0) result = (result * 0x1000000000000000162E42FEFA39EF358) >> 128;
        if (x & 0x1 > 0) result = (result * 0x10000000000000000B17217F7D1CF79AB) >> 128;

        result >>= uint256(int256(63 - (x >> 64)));
        require(result <= uint256(int256(MAX_64x64)), "ABDK.exp_2-2");

        return int128(int256(result));
    }

    /**
     * Calculate natural exponent of x.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function exp(int128 x) internal pure returns (int128) {
        require(x < 0x400000000000000000, "ABDK.exp");
        // Overflow

        if (x < -0x400000000000000000) return 0;
        // Underflow

        return exp_2(int128((int256(x) * 0x171547652B82FE1777D0FFDA0D23A7D12) >> 128));
    }

    /**
     * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
     * integer numbers.  Revert on overflow or when y is zero.
     *
     * @param x unsigned 256-bit integer number
     * @param y unsigned 256-bit integer number
     * @return unsigned 64.64-bit fixed point number
     */
    function divuu(uint256 x, uint256 y) private pure returns (uint128) {
        require(y != 0, "ABDK.divuu-1");

        uint256 result;

        if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) result = (x << 64) / y;
        else {
            uint256 msb = 192;
            uint256 xc = x >> 192;
            if (xc >= 0x100000000) {
                xc >>= 32;
                msb += 32;
            }
            if (xc >= 0x10000) {
                xc >>= 16;
                msb += 16;
            }
            if (xc >= 0x100) {
                xc >>= 8;
                msb += 8;
            }
            if (xc >= 0x10) {
                xc >>= 4;
                msb += 4;
            }
            if (xc >= 0x4) {
                xc >>= 2;
                msb += 2;
            }
            if (xc >= 0x2) msb += 1;
            // No need to shift xc anymore

            result = (x << (255 - msb)) / (((y - 1) >> (msb - 191)) + 1);
            require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "ABDK.divuu-2");

            uint256 hi = result * (y >> 128);
            uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

            uint256 xh = x >> 192;
            uint256 xl = x << 64;

            if (xl < lo) xh -= 1;
            xl -= lo;
            // We rely on overflow behavior here
            lo = hi << 128;
            if (xl < lo) xh -= 1;
            xl -= lo;
            // We rely on overflow behavior here

            assert(xh == hi >> 128);

            result += xl / y;
        }

        require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "ABDK.divuu-3");
        return uint128(result);
    }

    /**
     * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer
     * number.
     *
     * @param x unsigned 256-bit integer number
     * @return unsigned 128-bit integer number
     */
    function sqrtu(uint256 x) private pure returns (uint128) {
        if (x == 0) return 0;
        else {
            uint256 xx = x;
            uint256 r = 1;
            if (xx >= 0x100000000000000000000000000000000) {
                xx >>= 128;
                r <<= 64;
            }
            if (xx >= 0x10000000000000000) {
                xx >>= 64;
                r <<= 32;
            }
            if (xx >= 0x100000000) {
                xx >>= 32;
                r <<= 16;
            }
            if (xx >= 0x10000) {
                xx >>= 16;
                r <<= 8;
            }
            if (xx >= 0x100) {
                xx >>= 8;
                r <<= 4;
            }
            if (xx >= 0x10) {
                xx >>= 4;
                r <<= 2;
            }
            if (xx >= 0x8) {
                r <<= 1;
            }
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            // Seven iterations should be enough
            uint256 r1 = x / r;
            return uint128(r < r1 ? r : r1);
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "./ABDKMath64x64.sol";

library ConverterDec18 {
    using ABDKMath64x64 for int128;
    /*
     * Minimum value signed 64.64-bit fixed point number may have.
     */
    int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;

    /*
     * Maximum value signed 64.64-bit fixed point number may have.
     */
    int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

    int256 private constant DECIMALS = 10**18;

    int128 private constant ONE_64x64 = 0x010000000000000000;

    int128 public constant HALF_TBPS = 92233720368548; //1e-5 * 0.5 * 2**64

    // convert tenth of basis point to dec 18:
    uint256 public constant TBPSTODEC18 = 0x9184e72a000; // hex(10^18 * 10^-5)=(10^13)
    // convert tenth of basis point to ABDK 64x64:
    int128 public constant TBPSTOABDK = 0xa7c5ac471b48; // hex(2^64 * 10^-5)
    // convert two-digit integer reprentation to ABDK
    int128 public constant TDRTOABDK = 0x28f5c28f5c28f5c; // hex(2^64 * 10^-2)

    function tbpsToDec18(uint16 Vtbps) internal pure returns (uint256) {
        return TBPSTODEC18 * uint256(Vtbps);
    }

    function tbpsToABDK(uint16 Vtbps) internal pure returns (int128) {
        return int128(uint128(TBPSTOABDK) * uint128(Vtbps));
    }

    function TDRToABDK(uint16 V2Tdr) internal pure returns (int128) {
        return int128(uint128(TDRTOABDK) * uint128(V2Tdr));
    }

    function ABDKToTbps(int128 Vabdk) internal pure returns (uint16) {
        // add 0.5 * 1e-5 to ensure correct rounding to tenth of bps
        return uint16(uint128(Vabdk.add(HALF_TBPS) / TBPSTOABDK));
    }

    function fromDec18(int256 x) internal pure returns (int128) {
        int256 result = (x * ONE_64x64) / DECIMALS;
        require(x >= MIN_64x64 && x <= MAX_64x64, "result out of range");
        return int128(result);
    }

    function toDec18(int128 x) internal pure returns (int256) {
        return (int256(x) * DECIMALS) / ONE_64x64;
    }

    function toUDec18(int128 x) internal pure returns (uint256) {
        require(x >= 0, "negative value");
        return uint256(toDec18(x));
    }

    function toUDecN(int128 x, uint8 decimals) internal pure returns (uint256) {
        require(x >= 0, "negative value");
        return uint256((int256(x) * int256(10**decimals)) / ONE_64x64);
    }

    function fromDecN(int256 x, uint8 decimals) internal pure returns (int128) {
        int256 result = (x * ONE_64x64) / int256(10**decimals);
        require(x >= MIN_64x64 && x <= MAX_64x64, "result out of range");
        return int128(result);
    }
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.21;

/**
 * @title Library for managing loan sets.
 *
 * @notice Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * Include with `using EnumerableBytes4Set for EnumerableBytes4Set.Bytes4Set;`.
 * */
library EnumerableBytes4Set {
    struct Bytes4Set {
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes4 => uint256) index;
        bytes4[] values;
    }

    /**
     * @notice Add a value to a set. O(1).
     *
     * @param set The set of values.
     * @param value The new value to add.
     *
     * @return False if the value was already in the set.
     */
    function addBytes4(Bytes4Set storage set, bytes4 value) internal returns (bool) {
        if (!contains(set, value)) {
            set.values.push(value);
            set.index[value] = set.values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @notice Remove a value from a set. O(1).
     *
     * @param set The set of values.
     * @param value The value to remove.
     *
     * @return False if the value was not present in the set.
     */
    function removeBytes4(Bytes4Set storage set, bytes4 value) internal returns (bool) {
        if (contains(set, value)) {
            uint256 toDeleteIndex = set.index[value] - 1;
            uint256 lastIndex = set.values.length - 1;

            /// If the element we're deleting is the last one,
            /// we can just remove it without doing a swap.
            if (lastIndex != toDeleteIndex) {
                bytes4 lastValue = set.values[lastIndex];

                /// Move the last value to the index where the deleted value is.
                set.values[toDeleteIndex] = lastValue;

                /// Update the index for the moved value.
                set.index[lastValue] = toDeleteIndex + 1; // All indexes are 1-based
            }

            /// Delete the index entry for the deleted value.
            delete set.index[value];

            /// Delete the old entry for the moved value.
            set.values.pop();

            return true;
        } else {
            return false;
        }
    }

    /**
     * @notice Find out whether a value exists in the set.
     *
     * @param set The set of values.
     * @param value The value to find.
     *
     * @return True if the value is in the set. O(1).
     */
    function contains(Bytes4Set storage set, bytes4 value) internal view returns (bool) {
        return set.index[value] != 0;
    }

    /**
     * @notice Get all set values.
     *
     * @param set The set of values.
     * @param start The offset of the returning set.
     * @param count The limit of number of values to return.
     *
     * @return output An array with all values in the set. O(N).
     *
     * @dev Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * WARNING: This function may run out of gas on large sets: use {length} and
     * {get} instead in these cases.
     */
    function enumerate(
        Bytes4Set storage set,
        uint256 start,
        uint256 count
    ) internal view returns (bytes4[] memory output) {
        uint256 end = start + count;
        require(end >= start, "addition overflow");
        end = set.values.length < end ? set.values.length : end;
        if (end == 0 || start >= end) {
            return output;
        }

        output = new bytes4[](end - start);
        for (uint256 i; i < end - start; i++) {
            output[i] = set.values[i + start];
        }
        return output;
    }

    /**
     * @notice Get the legth of the set.
     *
     * @param set The set of values.
     *
     * @return the number of elements on the set. O(1).
     */
    function length(Bytes4Set storage set) internal view returns (uint256) {
        return set.values.length;
    }

    /**
     * @notice Get an item from the set by its index.
     *
     * @dev Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     *
     * @param set The set of values.
     * @param index The index of the value to return.
     *
     * @return the element stored at position `index` in the set. O(1).
     */
    function get(Bytes4Set storage set, uint256 index) internal view returns (bytes4) {
        return set.values[index];
    }
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.21;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSetUpgradeable {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            bytes32 lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        require(set._values.length > index, "EnumerableSet: idx out of bounds");
        return set._values[index];
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    function enumerate(
        AddressSet storage set,
        uint256 start,
        uint256 count
    ) internal view returns (address[] memory output) {
        uint256 end = start + count;
        require(end >= start, "addition overflow");
        uint256 len = length(set);
        end = len < end ? len : end;
        if (end == 0 || start >= end) {
            return output;
        }

        output = new address[](end - start);
        for (uint256 i; i < end - start; i++) {
            output[i] = at(set, i + start);
        }
        return output;
    }

    function enumerateAll(AddressSet storage set) internal view returns (address[] memory output) {
        return enumerate(set, 0, length(set));
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.21;

library Utils {
    function stringToBytes32(string memory source) internal pure returns (bytes32 result) {
        bytes memory tempEmptyStringTest = bytes(source);
        if (tempEmptyStringTest.length == 0) {
            return 0x0;
        }

        assembly {
            result := mload(add(source, 32))
        }
    }
}

File 30 of 48 : AbstractOracle.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "@openzeppelin/contracts/utils/introspection/ERC165Storage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "../interface/ISpotOracle.sol";
import "./OracleInterfaceID.sol";

abstract contract AbstractOracle is Ownable, ERC165Storage, OracleInterfaceID, ISpotOracle {
    constructor() {
        _registerInterface(_getOracleInterfaceID());
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
import "@pythnetwork/pyth-sdk-solidity/PythErrors.sol";
import "../libraries/ABDKMath64x64.sol";
import "./SpotOracle.sol";

contract OracleFactory is Ownable, OracleInterfaceID {
    using ERC165Checker for address;
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using ABDKMath64x64 for int128;

    // solhint-disable-next-line const-name-snakecase
    int128 internal constant ONE_64x64 = 0x10000000000000000; // 2^64

    uint256 internal immutable maxFeedTimeGapSec;

    address public immutable pyth;
    address public immutable onDemandFeed;

    struct OracleData {
        address oracle;
        bool isInverse;
    }

    // baseCurrency => quoteCurrency => oracles' addresses
    mapping(bytes4 => mapping(bytes4 => OracleData[])) internal routes;
    // price Id => address of spot oracle with that id
    mapping(bytes32 => address) internal oracles;

    event OracleCreated(bytes4 baseCurrency, bytes4 quoteCurrency, address oracle);
    event OracleAdded(bytes4 baseCurrency, bytes4 quoteCurrency, address oracle);
    event ShortRouteAdded(bytes4 baseCurrency, bytes4 quoteCurrency, address oracle);
    event RouteAdded(
        bytes4 baseCurrency,
        bytes4 quoteCurrency,
        address[] oracle,
        bool[] isInverse
    );
    event SetMarketClosed(
        bytes4 baseCurrency,
        bytes4 quoteCurrency,
        address oracle,
        bool marketClosed
    );

    /**
     * @param _maxFeedTimeGapSec Maximum time difference between two feed updates until they are considered out of sync
     */
    constructor(
        uint256 _maxFeedTimeGapSec,
        address _pythFeedAddress,
        address _onDemandfeedAddress
    ) {
        require(_maxFeedTimeGapSec > 0, "max feed time> 0");
        maxFeedTimeGapSec = _maxFeedTimeGapSec;
        pyth = _pythFeedAddress;
        onDemandFeed = _onDemandfeedAddress;
    }

    /**
     * @notice Deploys Oracle contract for currency pair.
     * @dev The route for the given pair will be set (CANNOT BE overwritten if it was already set).
     *
     * @param   _baseCurrency   The base currency symbol.
     * @param   _quoteCurrency  The quote currency symbol.
     * @param   _tradingBreakMins delay after which we consider mkt closed
     */
    function createOracle(
        bytes4 _baseCurrency,
        bytes4 _quoteCurrency,
        uint16 _tradingBreakMins,
        address _feedAddress,
        bytes32 _priceId,
        uint256 _feedPeriod
    ) external virtual onlyOwner returns (address) {
        require(_baseCurrency != "", "invalid base currency");
        require(_quoteCurrency != 0, "invalid quote currency");
        require(_baseCurrency != _quoteCurrency, "base and quote should differ");
        require(_feedAddress == pyth || _feedAddress == onDemandFeed, "invalid feed");
        address oracle = address(
            new SpotOracle(
                _baseCurrency,
                _quoteCurrency,
                _tradingBreakMins,
                _feedAddress,
                _priceId,
                _feedPeriod
            )
        );
        oracles[_priceId] = oracle;
        //note: we don't transfer the ownership of the oracle, factory
        //      remains owner
        _setRoute(_baseCurrency, _quoteCurrency, oracle);

        //checks that price can be calculated
        _getSpotPrice(_baseCurrency, _quoteCurrency);

        emit OracleCreated(_baseCurrency, _quoteCurrency, oracle);

        return oracle;
    }

    /**
     * @notice Sets Oracle contract for currency pair.
     * @dev The route for the given pair will be set (overwritten if it was already set).
     *
     * @param   _oracle   The Oracle contract (should implement ISpotOracle interface).
     */
    function addOracle(address _oracle) external onlyOwner {
        require(_oracle.supportsInterface(_getOracleInterfaceID()), "invalid oracle");

        bytes4 baseCurrency = ISpotOracle(_oracle).getBaseCurrency();
        bytes4 quoteCurrency = ISpotOracle(_oracle).getQuoteCurrency();
        _setRoute(baseCurrency, quoteCurrency, _oracle);

        //checks that price can be calculated
        _getSpotPrice(baseCurrency, quoteCurrency);
        emit OracleAdded(baseCurrency, quoteCurrency, _oracle);
    }

    /**
     * @notice Sets Oracle as a shortest route for the given currency pair.
     *
     * @param   _baseCurrency   The base currency symbol.
     * @param   _quoteCurrency  The quote currency symbol.
     * @param   _oracle         The Oracle contract (should implement ISpotOracle interface).
     */
    function _setRoute(bytes4 _baseCurrency, bytes4 _quoteCurrency, address _oracle) internal {
        require(routes[_baseCurrency][_quoteCurrency].length == 0, "route exists");
        delete routes[_baseCurrency][_quoteCurrency];
        delete oracles[ISpotOracle(_oracle).priceId()];
        routes[_baseCurrency][_quoteCurrency].push(OracleData(address(_oracle), false));
        oracles[ISpotOracle(_oracle).priceId()] = _oracle;
        emit ShortRouteAdded(_baseCurrency, _quoteCurrency, _oracle);
    }

    /**
     * setMarketClosed of short-route oracle
     * @param   _baseCurrency   The base currency symbol.
     * @param   _quoteCurrency  The quote currency symbol.
     * @param   _marketClosed   market closed or re-open
     */
    function setMarketClosed(
        bytes4 _baseCurrency,
        bytes4 _quoteCurrency,
        bool _marketClosed
    ) external onlyOwner {
        require(routes[_baseCurrency][_quoteCurrency].length == 1, "only short routes");
        address spotOracle = routes[_baseCurrency][_quoteCurrency][0].oracle;
        ISpotOracle(spotOracle).setMarketClosed(_marketClosed);
        emit SetMarketClosed(_baseCurrency, _quoteCurrency, spotOracle, _marketClosed);
    }

    /**
     * @notice Sets the given array of oracles as a route for the given currency pair.
     *
     * @param   _baseCurrency   The base currency symbol.
     * @param   _quoteCurrency  The quote currency symbol.
     * @param   _oracles        The array Oracle contracts.
     * @param   _isInverse      The array of flags whether price is inverted.
     */
    function addRoute(
        bytes4 _baseCurrency,
        bytes4 _quoteCurrency,
        address[] calldata _oracles,
        bool[] calldata _isInverse
    ) external onlyOwner {
        _validateRoute(_baseCurrency, _quoteCurrency, _oracles, _isInverse);

        uint256 length = _oracles.length;
        require(routes[_baseCurrency][_quoteCurrency].length == 0, "route exists");
        for (uint256 i = 0; i < length; i++) {
            routes[_baseCurrency][_quoteCurrency].push(OracleData(_oracles[i], _isInverse[i]));
            oracles[ISpotOracle(_oracles[i]).priceId()] = _oracles[i];
        }

        //checks that price can be calculated
        _getSpotPrice(_baseCurrency, _quoteCurrency);

        emit RouteAdded(_baseCurrency, _quoteCurrency, _oracles, _isInverse);
    }

    /**
     * @notice Validates the given array of oracles as a route for the given currency pair.
     *
     * @param   _baseCurrency   The base currency symbol.
     * @param   _quoteCurrency  The quote currency symbol.
     * @param   _oracles        The array Oracle contracts.
     * @param   _isInverse      The array of flags whether price is inverted.
     */
    function _validateRoute(
        bytes4 _baseCurrency,
        bytes4 _quoteCurrency,
        address[] calldata _oracles,
        bool[] calldata _isInverse
    ) internal view {
        uint256 length = _oracles.length;
        require(length > 0, "no oracles");
        require(length == _isInverse.length, "arrays mismatch");

        bytes4 srcCurrency;
        bytes4 destCurrency;
        require(_oracles[0].supportsInterface(_getOracleInterfaceID()), "invalid oracle [1]");
        if (!_isInverse[0]) {
            srcCurrency = ISpotOracle(_oracles[0]).getBaseCurrency();
            require(_baseCurrency == srcCurrency, "invalid route [1]");
            destCurrency = ISpotOracle(_oracles[0]).getQuoteCurrency();
        } else {
            srcCurrency = ISpotOracle(_oracles[0]).getQuoteCurrency();
            require(_baseCurrency == srcCurrency, "invalid route [2]");
            destCurrency = ISpotOracle(_oracles[0]).getBaseCurrency();
        }
        for (uint256 i = 1; i < length; i++) {
            require(_oracles[i].supportsInterface(_getOracleInterfaceID()), "invalid oracle [2]");
            bytes4 oracleBaseCurrency = ISpotOracle(_oracles[i]).getBaseCurrency();
            bytes4 oracleQuoteCurrency = ISpotOracle(_oracles[i]).getQuoteCurrency();
            if (!_isInverse[i]) {
                require(destCurrency == oracleBaseCurrency, "invalid route [3]");
                destCurrency = oracleQuoteCurrency;
            } else {
                require(destCurrency == oracleQuoteCurrency, "invalid route [4]");
                destCurrency = oracleBaseCurrency;
            }
        }
        require(_quoteCurrency == destCurrency, "invalid route [5]");
    }

    /**
     * @notice Returns the route for the given currency pair.
     *
     * @param   _baseCurrency   The base currency symbol.
     * @param   _quoteCurrency  The quote currency symbol.
     */
    function getRoute(
        bytes4 _baseCurrency,
        bytes4 _quoteCurrency
    ) external view returns (OracleData[] memory) {
        return routes[_baseCurrency][_quoteCurrency];
    }

    /**
     * @notice Calculates spot price.
     *
     * @param   _baseCurrency   The base currency symbol.
     * @param   _quoteCurrency  The quote currency symbol.
     */
    function getSpotPrice(
        bytes4 _baseCurrency,
        bytes4 _quoteCurrency
    ) external view returns (int128, uint256) {
        return _getSpotPrice(_baseCurrency, _quoteCurrency);
    }

    /**
     * @notice Determines if a route from _baseCurrency to _quoteCurrency exists
     * @param _baseCurrency The base currency symbol
     * @param _quoteCurrency The quote currency symbol
     */
    function existsRoute(
        bytes4 _baseCurrency,
        bytes4 _quoteCurrency
    ) external view returns (bool) {
        OracleData[] storage routeOracles = routes[_baseCurrency][_quoteCurrency];
        return routeOracles.length > 0;
    }

    /**
     * @notice Returns the spot price of one _baseCurrency in _quoteCurrency.
     *
     * @dev Price can be zero which needs to be captured outside this function
     * @param _baseCurrency in bytes4 representation
     * @param _quoteCurrency in bytes4 representation
     * @return fPrice Oracle price
     * @return timestamp Oracle timestamp
     */
    function _getSpotPrice(
        bytes4 _baseCurrency,
        bytes4 _quoteCurrency
    ) internal view returns (int128 fPrice, uint256 timestamp) {
        OracleData[] storage routeOracles = routes[_baseCurrency][_quoteCurrency];
        uint256 length = routeOracles.length;
        bool isInverse;
        if (length == 0) {
            routeOracles = routes[_quoteCurrency][_baseCurrency];
            length = routeOracles.length;
            require(length > 0, "route not found");
            isInverse = true;
        }

        fPrice = ONE_64x64;
        int128 oraclePrice;
        uint256 oracleTimestamp;
        for (uint256 i = 0; i < length; i++) {
            OracleData storage oracleData = routeOracles[i];
            (oraclePrice, oracleTimestamp) = ISpotOracle(oracleData.oracle).getSpotPrice();
            timestamp = oracleTimestamp < timestamp || timestamp == 0
                ? oracleTimestamp
                : timestamp;
            if (oraclePrice == 0) {
                //e.g. market closed
                return (0, timestamp);
            }
            if (!oracleData.isInverse) {
                fPrice = fPrice.mul(oraclePrice);
            } else {
                fPrice = fPrice.div(oraclePrice);
            }
        }
        if (isInverse) {
            fPrice = ONE_64x64.div(fPrice);
        }
    }

    /**
     * @notice Returns the ids used to determine the price of a given currency pair
     * @param _baseQuote Currency pair
     */
    function getRouteIds(
        bytes4[2] calldata _baseQuote
    ) external view returns (bytes32[] memory, bool[] memory) {
        // try direct route first
        OracleData[] storage route = routes[_baseQuote[0]][_baseQuote[1]];
        uint256 numFeeds = route.length;
        if (numFeeds == 0) {
            // inverse
            route = routes[_baseQuote[1]][_baseQuote[0]];
            numFeeds = route.length;
        }
        // slice
        bytes32[] memory id = new bytes32[](numFeeds);
        bool[] memory isPyth = new bool[](numFeeds);
        for (uint256 i = 0; i < numFeeds; i++) {
            address oracle = route[i].oracle;
            id[i] = ISpotOracle(oracle).priceId();
            isPyth[i] = ISpotOracle(oracle).priceFeed() == pyth;
        }
        return (id, isPyth);
    }

    /**
     * @dev Checks that the time submitted satisfies the age requirement, with the necessary overrides
     * @param _publishTime Timestamp in seconds
     * @param _maxAcceptableFeedAge Maximal age that the caller would accept (in seconds)
     * @param _oracle Address of the spot oracle
     */
    function _checkPublishTime(
        uint64 _publishTime,
        uint256 _maxAcceptableFeedAge,
        address _oracle
    ) internal view returns (address priceFeed) {
        priceFeed = ISpotOracle(_oracle).priceFeed();
        // check age of updates:
        // 1) max age set by Oracle
        uint256 maxAgeSec = IPyth(priceFeed).getValidTimePeriod();
        // 2) caller's required feed age in seconds
        // choose feed's age if _maxAcceptableFeedAge not given (0), else use _maxAcceptableFeedAge capped at feed's valid age
        maxAgeSec = _maxAcceptableFeedAge == 0 || _maxAcceptableFeedAge > maxAgeSec
            ? maxAgeSec
            : _maxAcceptableFeedAge;
        // some feeds (e.g. USDC) might be updating slower than requested,
        // hence maxAgeSec can be overruled by the slower time from the feed
        uint256 overrideAge = ISpotOracle(_oracle).feedPeriod();
        overrideAge = maxAgeSec < overrideAge ? overrideAge : maxAgeSec;
        require(_publishTime + overrideAge >= block.timestamp, "updt too old");
    }

    /**
     * @dev Performs the actual price submission to the price feed.
     * Feed addresses are determined at deploy-time and are not arbitrary.
     * @param _isPyth True if shold submit the updates to the pyth proxy
     * @param _fee how much fee to send
     * @param _updates update data
     * @param _ids price ids to update
     * @param _times publish times of updates
     */
    function _submitUpdates(
        bool _isPyth,
        uint256 _fee,
        bytes[] memory _updates,
        bytes32[] memory _ids,
        uint64[] memory _times
    ) internal returns (bool needed) {
        address feed = _isPyth ? pyth : onDemandFeed;
        // slither-disable-next-line arbitrary-send-eth
        try IPyth(feed).updatePriceFeedsIfNecessary{ value: _fee }(_updates, _ids, _times) {
            needed = true;
        } catch (bytes memory _err) {
            if (PythErrors.NoFreshUpdate.selector == bytes32(_err)) {
                // reverted because no update is needed
                needed = false;
            } else {
                revert("invalid updt");
            }
        }
    }

    /**
     * @notice Update price feeds.
     *
     * @dev Reverts if update is invalid, not paid for, or unnecessary.
     * 1) if _blockAge is zero, publish times only need to be compatible with the price feed requirements,
     * 2) if _blockAge > 0, the update data should not be older than the given age based on the minimal blocktime:
     *     publish TS + maximum age >= current TS
     * 3) publish TSs must be close to each other (within 2 blocktimes)
     * @param _updateData Update data
     * @param _priceIds Ids of the feeds to update
     * @param _publishTimes Timestamps of each update
     * @param _maxAcceptableFeedAge maximal age that the caller of this function would accept (in seconds) can be
     *  overriden to an older age by ISpotOracle(oracle).feedPeriod()
     */
    function updatePriceFeeds(
        bytes[] calldata _updateData,
        bytes32[] calldata _priceIds,
        uint64[] calldata _publishTimes,
        uint256 _maxAcceptableFeedAge
    ) external payable {
        // check data size
        require(_updateData.length > 0, "no data");
        require(
            _priceIds.length == _updateData.length && _updateData.length == _publishTimes.length,
            "array mismatch"
        );

        // check publish times, and count how many pyth oracles there are
        uint256 numPythOracles;
        {
            // first oracle
            uint256 oldest = _publishTimes[0];
            uint256 latest = _publishTimes[0];
            address oracle = oracles[_priceIds[0]];
            require(oracle != address(0), "no oracle for price id");
            if (_checkPublishTime(_publishTimes[0], _maxAcceptableFeedAge, oracle) == pyth) {
                numPythOracles = 1;
            }
            // all others (if any)
            for (uint256 i = 1; i < _publishTimes.length; i++) {
                // check we know the id
                oracle = oracles[_priceIds[i]];
                require(oracle != address(0), "no oracle for price id");
                // checlk publish times and age
                if (_checkPublishTime(_publishTimes[i], _maxAcceptableFeedAge, oracle) == pyth) {
                    numPythOracles += 1;
                }
                // track latest and oldest publish times
                oldest = oldest < _publishTimes[i] ? oldest : _publishTimes[i];
                latest = latest > _publishTimes[i] ? latest : _publishTimes[i];
            }
            require(latest <= oldest + maxFeedTimeGapSec, "not in sync");
        }

        // if all pyth or not-pyth, pass-through to the corresponding feed
        if (numPythOracles == _priceIds.length || numPythOracles == 0) {
            address priceFeed = numPythOracles == 0 ? onDemandFeed : pyth;
            // we send the whole msg.value
            uint256 fee = IPyth(priceFeed).getUpdateFee(_updateData);
            require(fee <= msg.value, "insufficient fee");
            if (!_submitUpdates(numPythOracles > 0, fee, _updateData, _priceIds, _publishTimes)) {
                revert("not needed");
            }
        } else {
            // it's a mix, so we need to do two rounds of submissions
            bool needed;
            uint256 pythFee;
            {
                // pyth first
                bytes[] memory updates = new bytes[](numPythOracles);
                bytes32[] memory ids = new bytes32[](numPythOracles);
                uint64[] memory times = new uint64[](numPythOracles);

                uint256 j;
                for (uint256 i = 0; j < numPythOracles && i < _priceIds.length; i++) {
                    if (ISpotOracle(oracles[_priceIds[i]]).priceFeed() == pyth) {
                        updates[j] = _updateData[i];
                        ids[j] = _priceIds[i];
                        times[j] = _publishTimes[i];
                        j++;
                    }
                }
                pythFee = IPyth(pyth).getUpdateFee(updates);
                require(pythFee < msg.value, "insufficient fee");
                needed = _submitUpdates(true, pythFee, updates, ids, times);
            }
            {
                // remaining oracles
                // note: variable is reused to save storage, but this is NOT pyth
                numPythOracles = _priceIds.length - numPythOracles;
                bytes[] memory updates = new bytes[](numPythOracles);
                bytes32[] memory ids = new bytes32[](numPythOracles);
                uint64[] memory times = new uint64[](numPythOracles);
                uint256 j;
                for (uint256 i = 0; j < numPythOracles && i < _priceIds.length; i++) {
                    if (ISpotOracle(oracles[_priceIds[i]]).priceFeed() == onDemandFeed) {
                        updates[j] = _updateData[i];
                        ids[j] = _priceIds[i];
                        times[j] = _publishTimes[i];
                        j++;
                    }
                }
                uint256 onDemandFee = IPyth(onDemandFeed).getUpdateFee(updates);
                require(onDemandFee + pythFee <= msg.value, "insufficient fee");
                bool t = _submitUpdates(false, onDemandFee, updates, ids, times);
                needed = needed || t;
            }
            if (!needed) revert("not needed");
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "../interface/ISpotOracle.sol";

contract OracleInterfaceID {
    function _getOracleInterfaceID() internal pure returns (bytes4) {
        ISpotOracle i;
        return
            i.isMarketClosed.selector ^
            i.getSpotPrice.selector ^
            i.getBaseCurrency.selector ^
            i.getQuoteCurrency.selector ^
            i.priceFeed.selector ^
            i.priceId.selector;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
import "./AbstractOracle.sol";
import "../libraries/ConverterDec18.sol";

/**
 *  Spot oracle has different states:
 *  - market is closed: if explicitely set, the price returns 0
 *  - the price can return 0 at any point in time. This is considered as "market closed" and
 *    must be handled outside the oracle
 */
contract SpotOracle is AbstractOracle {
    using ConverterDec18 for int256;
    using ERC165Checker for address;

    bytes4 private immutable baseCurrency;
    bytes4 private immutable quoteCurrency;

    // @dev either pyth or on-demand, not both at the same time
    address public immutable priceFeed;
    bytes32 public immutable priceId;
    uint256 public immutable feedPeriod;

    uint64 private timestampClosed;

    // if a price is older than tradingBreakMins, the market is considered
    // closed and the price returns zero.
    // For example, if equities trade weekdays from 9am to 5pm, the trading break
    // is 16 hours (from 5pm to 9am) and we could set _tradingBreakMins
    // to 8 hours * 60. For crypto that trades 24/7 we could set it to 1h.
    uint16 private immutable tradingBreakMins;

    bool private marketClosed;

    constructor(
        bytes4 _baseCurrency,
        bytes4 _quoteCurrency,
        uint16 _tradingBreakMins,
        address _priceFeed,
        bytes32 _priceId,
        uint256 _period
    ) {
        require(_tradingBreakMins > 1, "too small");
        require(_priceFeed != address(0), "invalid price feed");
        require(_period < 60 * _tradingBreakMins, "period too long");
        baseCurrency = _baseCurrency;
        quoteCurrency = _quoteCurrency;
        tradingBreakMins = _tradingBreakMins;
        priceFeed = _priceFeed;
        priceId = _priceId;
        feedPeriod = _period;
    }

    /**
     * @dev Sets the market is closed flag.
     */
    function setMarketClosed(bool _marketClosed) external override onlyOwner {
        marketClosed = _marketClosed;
        timestampClosed = uint64(block.timestamp);
    }

    /**
     * @dev The market is closed if the market is not in its regular trading period.
     */
    function isMarketClosed() external view override returns (bool) {
        (int128 price, ) = getSpotPrice();
        return price == 0;
    }

    /**
     *  Spot price.
     *  Returns 0 if market is closed
     */
    function getSpotPrice() public view virtual override returns (int128 fPrice, uint256 ts) {
        if (marketClosed) {
            return (0, timestampClosed);
        }
        PythStructs.Price memory pythPrice = IPyth(priceFeed).getPriceUnsafe(priceId);
        ts = pythPrice.publishTime;
        // price is zero unless the market on break and feed price is not positive
        if (pythPrice.price > 0 && ts + tradingBreakMins * 60 >= block.timestamp) {
            // price = pythPrice.price * 10 ^ pythPrice.expo;
            int256 price = int256(pythPrice.price) * int256(0x010000000000000000); // x * 2^64
            int256 decimals = int256(
                pythPrice.expo < 0 ? 10 ** uint32(-pythPrice.expo) : 10 ** uint32(pythPrice.expo)
            );
            price = pythPrice.expo < 0 ? price / decimals : price * decimals;
            fPrice = int128(price);
            require(fPrice > 0, "price overflow");
        }
    }

    /**
     * Get base currency symbol.
     */
    function getBaseCurrency() external view override returns (bytes4) {
        return baseCurrency;
    }

    /**
     * Get quote currency symbol.
     */
    function getQuoteCurrency() external view override returns (bytes4) {
        return quoteCurrency;
    }
}

File 34 of 48 : PerpStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";

import "../../interface/IShareTokenFactory.sol";
import "../../libraries/ABDKMath64x64.sol";
import "./../functions/AMMPerpLogic.sol";
import "../../libraries/EnumerableSetUpgradeable.sol";
import "../../libraries/EnumerableBytes4Set.sol";
import "../../governance/Maintainable.sol";

/* solhint-disable max-states-count */
contract PerpStorage is Maintainable, Pausable, ReentrancyGuard {
    using ABDKMath64x64 for int128;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
    using EnumerableBytes4Set for EnumerableBytes4Set.Bytes4Set; // enumerable map of bytes4 or addresses
    /**
     * @notice  Perpetual state:
     *          - INVALID:      Uninitialized or not non-existent perpetual.
     *          - INITIALIZING: Only when LiquidityPoolData.isRunning == false. Traders cannot perform operations.
     *          - NORMAL:       Full functional state. Traders are able to perform all operations.
     *          - EMERGENCY:    Perpetual is unsafe and the perpetual needs to be settled.
     *          - SETTLE:       Perpetual ready to be settled
     *          - CLEARED:      All margin accounts are cleared. Traders can withdraw remaining margin balance.
     */
    enum PerpetualState {
        INVALID,
        INITIALIZING,
        NORMAL,
        EMERGENCY,
        SETTLE,
        CLEARED
    }

    // margin and liquidity pool are held in 'collateral currency' which can be either of
    // quote currency, base currency, or quanto currency
    // solhint-disable-next-line const-name-snakecase
    int128 internal constant ONE_64x64 = 0x10000000000000000; // 2^64
    int128 internal constant FUNDING_INTERVAL_SEC = 0x70800000000000000000; //3600 * 8 * 0x10000000000000000 = 8h in seconds scaled by 2^64 for ABDKMath64x64
    int128 internal constant MIN_NUM_LOTS_PER_POSITION = 0x0a0000000000000000; // 10, minimal position size in number of lots
    uint8 internal constant MASK_ORDER_CANCELLED = 0x1;
    uint8 internal constant MASK_ORDER_EXECUTED = 0x2;
    // at target, 1% of missing amount is transferred
    // at every rebalance
    uint8 internal iPoolCount;
    // delay required for trades to mitigate oracle front-running in seconds
    uint8 internal iTradeDelaySec;
    address internal ammPerpLogic;

    IShareTokenFactory internal shareTokenFactory;

    //pool id (incremental index, starts from 1) => pool data
    mapping(uint8 => LiquidityPoolData) internal liquidityPools;

    //perpetual id  => pool id
    mapping(uint24 => uint8) internal perpetualPoolIds;

    address internal orderBookFactory;

    /**
     * @notice  Data structure to store oracle price data.
     */
    struct PriceTimeData {
        int128 fPrice;
        uint64 time;
    }

    /**
     * @notice  Data structure to store user margin information.
     */
    struct MarginAccount {
        int128 fLockedInValueQC; // unrealized value locked-in when trade occurs
        int128 fCashCC; // cash in collateral currency (base, quote, or quanto)
        int128 fPositionBC; // position in base currency (e.g., 1 BTC for BTCUSD)
        int128 fUnitAccumulatedFundingStart; // accumulated funding rate
    }

    /**
     * @notice  Store information for a given perpetual market.
     */
    struct PerpetualData {
        // ------ 0
        uint8 poolId;
        uint24 id;
        int32 fInitialMarginRate; //parameter: initial margin
        int32 fSigma2; // parameter: volatility of base-quote pair
        uint32 iLastFundingTime; //timestamp since last funding rate payment
        int32 fDFCoverNRate; // parameter: cover-n rule for default fund. E.g., fDFCoverNRate=0.05 -> we try to cover 5% of active accounts with default fund
        int32 fMaintenanceMarginRate; // parameter: maintenance margin
        PerpetualState state; // Perpetual AMM state
        AMMPerpLogic.CollateralCurrency eCollateralCurrency; //parameter: in what currency is the collateral held?
        // uint16 minimalSpreadTbps; //parameter: minimal spread between long and short perpetual price
        // ------ 1
        bytes4 S2BaseCCY; //base currency of S2
        bytes4 S2QuoteCCY; //quote currency of S2
        uint16 incentiveSpreadTbps; //parameter: maximum spread added to the PD
        uint16 minimalSpreadTbps; //parameter: minimal spread between long and short perpetual price
        bytes4 S3BaseCCY; //base currency of S3
        bytes4 S3QuoteCCY; //quote currency of S3
        int32 fSigma3; // parameter: volatility of quanto-quote pair
        int32 fRho23; // parameter: correlation of quanto/base returns
        uint16 liquidationPenaltyRateTbps; //parameter: penalty if AMM closes the position and not the trader
        //------- 2
        PriceTimeData currentMarkPremiumRate; //relative diff to index price EMA, used for markprice.
        //------- 3
        int128 premiumRatesEMA; // EMA of premium rate
        int128 fUnitAccumulatedFunding; //accumulated funding in collateral currency
        //------- 4
        int128 fOpenInterest; //open interest is the larger of the amount of long and short positions in base currency
        int128 fTargetAMMFundSize; //target liquidity pool funds to allocate to the AMM
        //------- 5
        int128 fCurrentTraderExposureEMA; // trade amounts (storing absolute value)
        int128 fCurrentFundingRate; // current instantaneous funding rate
        //------- 6
        int128 fLotSizeBC; //parameter: minimal trade unit (in base currency) to avoid dust positions
        int128 fReferralRebateCC; //parameter: referall rebate in collateral currency
        //------- 7
        int128 fTargetDFSize; // target default fund size
        int128 fkStar; // signed trade size that minimizes the AMM risk
        //------- 8
        int128 fAMMTargetDD; // parameter: target distance to default (=inverse of default probability)
        int128 fAMMMinSizeCC; // parameter: minimal size of AMM pool, regardless of current exposure
        //------- 9
        int128 fMinimalTraderExposureEMA; // parameter: minimal value for fCurrentTraderExposureEMA that we don't want to undershoot
        int128 fMinimalAMMExposureEMA; // parameter: minimal abs value for fCurrentAMMExposureEMA that we don't want to undershoot
        //------- 10
        int128 fSettlementS3PriceData; //quanto index
        int128 fSettlementS2PriceData; //base-quote pair. Used as last price in normal state.
        //------- 11
        int128 fTotalMarginBalance; //calculated for settlement, in collateral currency
        int32 fMarkPriceEMALambda; // parameter: Lambda parameter for EMA used in mark-price for funding rates
        int32 fFundingRateClamp; // parameter: funding rate clamp between which we charge 1bps
        int32 fMaximalTradeSizeBumpUp; // parameter: >1, users can create a maximal position of size fMaximalTradeSizeBumpUp*fCurrentAMMExposureEMA
        uint32 iLastTargetPoolSizeTime; //timestamp (seconds) since last update of fTargetDFSize and fTargetAMMFundSize
        //------- 12

        //-------
        int128[2] fStressReturnS3; // parameter: negative and positive stress returns for quanto-quote asset
        int128[2] fDFLambda; // parameter: EMA lambda for AMM and trader exposure K,k: EMA*lambda + (1-lambda)*K. 0 regular lambda, 1 if current value exceeds past
        int128[2] fCurrentAMMExposureEMA; // 0: negative aggregated exposure (storing negative value), 1: positive
        int128[2] fStressReturnS2; // parameter: negative and positive stress returns for base-quote asset
        // -----
    }

    address internal oracleFactoryAddress;

    // users
    mapping(uint24 => EnumerableSetUpgradeable.AddressSet) internal activeAccounts; //perpetualId => traderAddressSet
    // accounts
    mapping(uint24 => mapping(address => MarginAccount)) internal marginAccounts;
    // delegates
    mapping(address => address) internal delegates;

    // broker maps: poolId -> brokeraddress-> lots contributed
    // contains non-zero entries for brokers. Brokers pay default fund contributions.
    mapping(uint8 => mapping(address => uint32)) internal brokerMap;

    struct LiquidityPoolData {
        bool isRunning; // state
        uint8 iPerpetualCount; // state
        uint8 id; // parameter: index, starts from 1
        int32 fCeilPnLShare; // parameter: cap on the share of PnL allocated to liquidity providers
        uint8 marginTokenDecimals; // parameter: decimals of margin token, inferred from token contract
        uint16 iTargetPoolSizeUpdateTime; //parameter: timestamp in seconds. How often we update the pool's target size
        address marginTokenAddress; //parameter: address of the margin token
        // -----
        uint64 prevAnchor; // state: keep track of timestamp since last withdrawal was initiated
        int128 fRedemptionRate; // state: used for settlement in case of AMM default
        address shareTokenAddress; // parameter
        // -----
        int128 fPnLparticipantsCashCC; // state: addLiquidity/withdrawLiquidity + profit/loss - rebalance
        int128 fTargetAMMFundSize; // state: target liquidity for all perpetuals in pool (sum)
        // -----
        int128 fDefaultFundCashCC; // state: profit/loss
        int128 fTargetDFSize; // state: target default fund size for all perpetuals in pool
        // -----
        int128 fBrokerCollateralLotSize; // param:how much collateral do brokers deposit when providing "1 lot" (not trading lot)
        uint128 prevTokenAmount; // state
        // -----
        uint128 nextTokenAmount; // state
        uint128 totalSupplyShareToken; // state
        // -----
        int128 fBrokerFundCashCC; // state: amount of cash in broker fund
    }

    address internal treasuryAddress; // address for the protocol treasury

    //pool id => perpetual id list
    mapping(uint8 => uint24[]) internal perpetualIds;

    //pool id => perpetual id => data
    mapping(uint8 => mapping(uint24 => PerpetualData)) internal perpetuals;

    /// @dev flag whether MarginTradeOrder was already executed or cancelled
    mapping(bytes32 => uint8) internal executedOrCancelledOrders;

    //proxy
    mapping(bytes32 => EnumerableBytes4Set.Bytes4Set) internal moduleActiveFuncSignatureList;
    mapping(bytes32 => address) internal moduleNameToAddress;
    mapping(address => bytes32) internal moduleAddressToModuleName;

    // fee structure
    struct VolumeEMA {
        int128 fTradingVolumeEMAusd; //trading volume EMA in usd
        uint64 timestamp; // timestamp of last trade
    }

    uint256[] public traderVolumeTiers; // dec18, regardless of token
    uint256[] public brokerVolumeTiers; // dec18, regardless of token
    uint16[] public traderVolumeFeesTbps;
    uint16[] public brokerVolumeFeesTbps;
    mapping(uint24 => address) public perpBaseToUSDOracle;
    mapping(uint24 => int128) public perpToLastBaseToUSD;
    mapping(uint8 => mapping(address => VolumeEMA)) public traderVolumeEMA;
    mapping(uint8 => mapping(address => VolumeEMA)) public brokerVolumeEMA;
    uint64 public lastBaseToUSDUpdateTs;

    // liquidity withdrawals
    struct WithdrawRequest {
        address lp;
        uint256 shareTokens;
        uint64 withdrawTimestamp;
    }

    mapping(address => mapping(uint8 => WithdrawRequest)) internal lpWithdrawMap;

    // users who initiated withdrawals are registered here
    mapping(uint8 => EnumerableSetUpgradeable.AddressSet) internal activeWithdrawals; //poolId => lpAddressSet

    mapping(uint8 => bool) public liquidityProvisionIsPaused;
}
/* solhint-enable max-states-count */

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "../../libraries/ABDKMath64x64.sol";
import "../../libraries/ConverterDec18.sol";
import "../../perpetual/interfaces/IAMMPerpLogic.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract AMMPerpLogic is Ownable, IAMMPerpLogic {
    using ABDKMath64x64 for int128;
    /* solhint-disable const-name-snakecase */
    int128 internal constant ONE_64x64 = 0x10000000000000000; // 2^64
    int128 internal constant TWO_64x64 = 0x20000000000000000; // 2*2^64
    int128 internal constant FOUR_64x64 = 0x40000000000000000; //4*2^64
    int128 internal constant HALF_64x64 = 0x8000000000000000; //0.5*2^64
    int128 internal constant TWENTY_64x64 = 0x140000000000000000; //20*2^64
    int128 private constant CDF_CONST_0 = 0x023a6ce358298c;
    int128 private constant CDF_CONST_1 = -0x216c61522a6f3f;
    int128 private constant CDF_CONST_2 = 0xc9320d9945b6c3;
    int128 private constant CDF_CONST_3 = -0x01bcfd4bf0995aaf;
    int128 private constant CDF_CONST_4 = -0x086de76427c7c501;
    int128 private constant CDF_CONST_5 = 0x749741d084e83004;
    int128 private constant CDF_CONST_6 = 0xcc42299ea1b28805;
    int128 private constant CDF_CONST_7 = 0x0281b263fec4e0a007;
    int128 private constant EXPM1_Q0 = 0x0a26c00000000000000000;
    int128 private constant EXPM1_Q1 = 0x0127500000000000000000;
    int128 private constant EXPM1_P0 = 0x0513600000000000000000;
    int128 private constant EXPM1_P1 = 0x27600000000000000000;
    int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

    /* solhint-enable const-name-snakecase */

    enum CollateralCurrency {
        QUOTE,
        BASE,
        QUANTO
    }

    struct AMMVariables {
        // all variables are
        // signed 64.64-bit fixed point number
        int128 fLockedValue1; // L1 in quote currency
        int128 fPoolM1; // M1 in quote currency
        int128 fPoolM2; // M2 in base currency
        int128 fPoolM3; // M3 in quanto currency
        int128 fAMM_K2; // AMM exposure (positive if trader long)
        int128 fCurrentTraderExposureEMA; // current average unsigned trader exposure
    }

    struct MarketVariables {
        int128 fIndexPriceS2; // base index
        int128 fIndexPriceS3; // quanto index
        int128 fSigma2; // standard dev of base currency
        int128 fSigma3; // standard dev of quanto currency
        int128 fRho23; // correlation base/quanto currency
    }

    /**
     * Calculate a EWMA when the last observation happened n periods ago
     * @dev Given is x_t = (1 - lambda) * mean + lambda * x_t-1, and x_0 = _newObs
     * it returns the value of x_deltaTime
     * @param _mean long term mean
     * @param _newObs observation deltaTime periods ago
     * @param _fLambda lambda of the EWMA
     * @param _deltaTime number of periods elapsed
     * @return result EWMA at deltaPeriods
     */
    function _emaWithTimeJumps(
        uint16 _mean,
        uint16 _newObs,
        int128 _fLambda,
        uint256 _deltaTime
    ) internal pure returns (int128 result) {
        _fLambda = _fLambda.pow(_deltaTime);
        result = ConverterDec18.tbpsToABDK(_mean).mul(ONE_64x64.sub(_fLambda));
        result = result.add(_fLambda.mul(ConverterDec18.tbpsToABDK(_newObs)));
    }

    /**
     *  Calculate the normal CDF value of _fX, i.e.,
     *  k=P(X<=_fX), for X~normal(0,1)
     *  The approximation is of the form
     *  Phi(x) = 1 - phi(x) / (x + exp(p(x))),
     *  where p(x) is a polynomial of degree 6
     *  @param _fX signed 64.64-bit fixed point number
     *  @return fY approximated normal-cdf evaluated at X
     */
    function _normalCDF(int128 _fX) internal pure returns (int128 fY) {
        bool isNegative = _fX < 0;
        if (isNegative) {
            _fX = _fX.neg();
        }
        if (_fX > FOUR_64x64) {
            fY = int128(0);
        } else {
            fY = _fX.mul(CDF_CONST_0).add(CDF_CONST_1);
            fY = _fX.mul(fY).add(CDF_CONST_2);
            fY = _fX.mul(fY).add(CDF_CONST_3);
            fY = _fX.mul(fY).add(CDF_CONST_4);
            fY = _fX.mul(fY).add(CDF_CONST_5).mul(_fX).neg().exp();
            fY = fY.mul(CDF_CONST_6).add(_fX);
            fY = _fX.mul(_fX).mul(HALF_64x64).neg().exp().div(CDF_CONST_7).div(fY);
        }
        if (!isNegative) {
            fY = ONE_64x64.sub(fY);
        }
        return fY;
    }

    /**
     *  Calculate the target size for the default fund
     *
     *  @param _fK2AMM       signed 64.64-bit fixed point number, Conservative negative[0]/positive[1] AMM exposure
     *  @param _fk2Trader    signed 64.64-bit fixed point number, Conservative (absolute) trader exposure
     *  @param _fCoverN      signed 64.64-bit fixed point number, cover-n rule for default fund parameter
     *  @param fStressRet2   signed 64.64-bit fixed point number, negative[0]/positive[1] stress returns for base/quote pair
     *  @param fStressRet3   signed 64.64-bit fixed point number, negative[0]/positive[1] stress returns for quanto/quote currency
     *  @param fIndexPrices  signed 64.64-bit fixed point number, spot price for base/quote[0] and quanto/quote[1] pairs
     *  @param _eCCY         enum that specifies in which currency the collateral is held: QUOTE, BASE, QUANTO
     *  @return approximated normal-cdf evaluated at X
     */
    function calculateDefaultFundSize(
        int128[2] memory _fK2AMM,
        int128 _fk2Trader,
        int128 _fCoverN,
        int128[2] memory fStressRet2,
        int128[2] memory fStressRet3,
        int128[2] memory fIndexPrices,
        AMMPerpLogic.CollateralCurrency _eCCY
    ) external pure override returns (int128) {
        require(_fK2AMM[0] < 0, "_fK2AMM[0] must be negative");
        require(_fK2AMM[1] > 0, "_fK2AMM[1] must be positive");
        require(_fk2Trader > 0, "_fk2Trader must be positive");

        int128[2] memory fEll;
        // downward stress scenario
        fEll[0] = (_fK2AMM[0].abs().add(_fk2Trader.mul(_fCoverN))).mul(
            ONE_64x64.sub((fStressRet2[0].exp()))
        );
        // upward stress scenario
        fEll[1] = (_fK2AMM[1].abs().add(_fk2Trader.mul(_fCoverN))).mul(
            (fStressRet2[1].exp().sub(ONE_64x64))
        );
        int128 fIstar;
        if (_eCCY == AMMPerpLogic.CollateralCurrency.BASE) {
            fIstar = fEll[0].div(fStressRet2[0].exp());
            int128 fI2 = fEll[1].div(fStressRet2[1].exp());
            if (fI2 > fIstar) {
                fIstar = fI2;
            }
        } else if (_eCCY == AMMPerpLogic.CollateralCurrency.QUANTO) {
            fIstar = fEll[0].div(fStressRet3[0].exp());
            int128 fI2 = fEll[1].div(fStressRet3[1].exp());
            if (fI2 > fIstar) {
                fIstar = fI2;
            }
            fIstar = fIstar.mul(fIndexPrices[0].div(fIndexPrices[1]));
        } else {
            assert(_eCCY == AMMPerpLogic.CollateralCurrency.QUOTE);
            if (fEll[0] > fEll[1]) {
                fIstar = fEll[0].mul(fIndexPrices[0]);
            } else {
                fIstar = fEll[1].mul(fIndexPrices[0]);
            }
        }
        return fIstar;
    }

    /**
     *  Calculate the risk neutral Distance to Default (Phi(DD)=default probability) when
     *  there is no quanto currency collateral.
     *  We assume r=0 everywhere.
     *  The underlying distribution is log-normal, hence the log below.
     *  All variables are 64.64-bit fixed point number (or struct thereof)
     *  @param fSigma2 current Market variables (price&params)
     *  @param _fSign signed 64.64-bit fixed point number, sign of denominator of distance to default
     *  @return _fThresh signed 64.64-bit fixed point number, number for which the log is the unnormalized distance to default
     */
    function _calculateRiskNeutralDDNoQuanto(
        int128 fSigma2,
        int128 _fSign,
        int128 _fThresh
    ) internal pure returns (int128) {
        require(_fThresh > 0, "argument to log must be >0");
        int128 _fLogTresh = _fThresh.ln();
        int128 fSigma2_2 = fSigma2.mul(fSigma2);
        int128 fMean = fSigma2_2.div(TWO_64x64).neg();
        int128 fDistanceToDefault = ABDKMath64x64.sub(_fLogTresh, fMean).div(fSigma2);
        // because 1-Phi(x) = Phi(-x) we change the sign if _fSign<0
        // now we would like to get the normal cdf of that beast
        if (_fSign < 0) {
            fDistanceToDefault = fDistanceToDefault.neg();
        }
        return fDistanceToDefault;
    }

    /**
     *  Calculate the standard deviation for the random variable
     *  evolving when quanto currencies are involved.
     *  We assume r=0 everywhere.
     *  All variables are 64.64-bit fixed point number (or struct thereof)
     *  @param _mktVars current Market variables (price&params)
     *  @param _fC3 signed 64.64-bit fixed point number current AMM/Market variables
     *  @param _fC3_2 signed 64.64-bit fixed point number, squared fC3
     *  @return fSigmaZ standard deviation, 64.64-bit fixed point number
     */
    function _calculateStandardDeviationQuanto(
        MarketVariables memory _mktVars,
        int128 _fC3,
        int128 _fC3_2
    ) internal pure returns (int128 fSigmaZ) {
        // fVarA = (exp(sigma2^2) - 1)
        int128 fVarA = _mktVars.fSigma2.mul(_mktVars.fSigma2);

        // fVarB = 2*(exp(sigma2*sigma3*rho) - 1)
        int128 fVarB = _mktVars.fSigma2.mul(_mktVars.fSigma3).mul(_mktVars.fRho23).mul(TWO_64x64);

        // fVarC = exp(sigma3^2) - 1
        int128 fVarC = _mktVars.fSigma3.mul(_mktVars.fSigma3);

        // sigmaZ = fVarA*C^2 + fVarB*C + fVarC
        fSigmaZ = fVarA.mul(_fC3_2).add(fVarB.mul(_fC3)).add(fVarC).sqrt();
    }

    /**
     *  Calculate the risk neutral Distance to Default (Phi(DD)=default probability) when
     *  presence of quanto currency collateral.
     *
     *  We approximate the distribution with a normal distribution
     *  We assume r=0 everywhere.
     *  All variables are 64.64-bit fixed point number
     *  @param _ammVars current AMM/Market variables
     *  @param _mktVars current Market variables (price&params)
     *  @param _fSign 64.64-bit fixed point number, current AMM/Market variables
     *  @return fDistanceToDefault signed 64.64-bit fixed point number
     */
    function _calculateRiskNeutralDDWithQuanto(
        AMMVariables memory _ammVars,
        MarketVariables memory _mktVars,
        int128 _fSign,
        int128 _fThresh
    ) internal pure returns (int128 fDistanceToDefault) {
        require(_fSign > 0, "no sign in quanto case");
        // 1) Calculate C3
        int128 fC3 = _mktVars.fIndexPriceS2.mul(_ammVars.fPoolM2.sub(_ammVars.fAMM_K2)).div(
            _ammVars.fPoolM3.mul(_mktVars.fIndexPriceS3)
        );
        int128 fC3_2 = fC3.mul(fC3);

        // 2) Calculate Variance
        int128 fSigmaZ = _calculateStandardDeviationQuanto(_mktVars, fC3, fC3_2);

        // 3) Calculate mean
        int128 fMean = fC3.add(ONE_64x64);
        // 4) Distance to default
        fDistanceToDefault = _fThresh.sub(fMean).div(fSigmaZ);
    }

    function calculateRiskNeutralPD(
        AMMVariables memory _ammVars,
        MarketVariables memory _mktVars,
        int128 _fTradeAmount,
        bool _withCDF
    ) external view virtual override returns (int128, int128) {
        return _calculateRiskNeutralPD(_ammVars, _mktVars, _fTradeAmount, _withCDF);
    }

    /**
     *  Calculate the risk neutral default probability (>=0).
     *  Function decides whether pricing with or without quanto CCY is chosen.
     *  We assume r=0 everywhere.
     *  All variables are 64.64-bit fixed point number (or struct thereof)
     *  @param _ammVars         current AMM variables.
     *  @param _mktVars         current Market variables (price&params)
     *  @param _fTradeAmount    Trade amount (can be 0), hence amounts k2 are not already factored in
     *                          that is, function will set K2:=K2+k2, L1:=L1+k2*s2 (k2=_fTradeAmount)
     *  @param _withCDF         bool. If false, the normal-cdf is not evaluated (in case the caller is only
     *                          interested in the distance-to-default, this saves calculations)
     *  @return (default probabilit, distance to default) ; 64.64-bit fixed point numbers
     */
    function _calculateRiskNeutralPD(
        AMMVariables memory _ammVars,
        MarketVariables memory _mktVars,
        int128 _fTradeAmount,
        bool _withCDF
    ) internal pure returns (int128, int128) {
        int128 dL = _fTradeAmount.mul(_mktVars.fIndexPriceS2);
        int128 dK = _fTradeAmount;
        _ammVars.fLockedValue1 = _ammVars.fLockedValue1.add(dL);
        _ammVars.fAMM_K2 = _ammVars.fAMM_K2.add(dK);
        // -L1 - k*s2 - M1
        int128 fNumerator = (_ammVars.fLockedValue1.neg()).sub(_ammVars.fPoolM1);
        // s2*(M2-k2-K2) if no quanto, else M3 * s3
        int128 fDenominator = _ammVars.fPoolM3 == 0
            ? (_ammVars.fPoolM2.sub(_ammVars.fAMM_K2)).mul(_mktVars.fIndexPriceS2)
            : _ammVars.fPoolM3.mul(_mktVars.fIndexPriceS3);
        // handle edge sign cases first
        int128 fThresh;
        if (_ammVars.fPoolM3 == 0) {
            if (fNumerator < 0) {
                if (fDenominator >= 0) {
                    // P( den * exp(x) < 0) = 0
                    return (int128(0), TWENTY_64x64.neg());
                } else {
                    // num < 0 and den < 0, and P(exp(x) > infty) = 0
                    int256 result = (int256(fNumerator) << 64) / fDenominator;
                    if (result > MAX_64x64) {
                        return (int128(0), TWENTY_64x64.neg());
                    }
                    fThresh = int128(result);
                }
            } else if (fNumerator > 0) {
                if (fDenominator <= 0) {
                    // P( exp(x) >= 0) = 1
                    return (int128(ONE_64x64), TWENTY_64x64);
                } else {
                    // num > 0 and den > 0, and P(exp(x) < infty) = 1
                    int256 result = (int256(fNumerator) << 64) / fDenominator;
                    if (result > MAX_64x64) {
                        return (int128(ONE_64x64), TWENTY_64x64);
                    }
                    fThresh = int128(result);
                }
            } else {
                return
                    fDenominator >= 0
                        ? (int128(0), TWENTY_64x64.neg())
                        : (int128(ONE_64x64), TWENTY_64x64);
            }
        } else {
            // denom is O(M3 * S3), div should not overflow
            fThresh = fNumerator.div(fDenominator);
        }
        // if we're here fDenominator !=0 and fThresh did not overflow
        // sign tells us whether we consider norm.cdf(f(threshold)) or 1-norm.cdf(f(threshold))
        // we recycle fDenominator to store the sign since it's no longer used
        fDenominator = fDenominator < 0 ? ONE_64x64.neg() : ONE_64x64;
        int128 dd = _ammVars.fPoolM3 == 0
            ? _calculateRiskNeutralDDNoQuanto(_mktVars.fSigma2, fDenominator, fThresh)
            : _calculateRiskNeutralDDWithQuanto(_ammVars, _mktVars, fDenominator, fThresh);

        int128 q;
        if (_withCDF) {
            q = _normalCDF(dd);
        }
        return (q, dd);
    }

    /**
     *  Calculate additional/non-risk based slippage.
     *  Ensures slippage is bounded away from zero for small trades,
     *  and plateaus for larger-than-average trades, so that price becomes risk based.
     *
     *  All variables are 64.64-bit fixed point number (or struct thereof)
     *  @param _ammVars current AMM variables - we need the current average exposure per trader
     *  @param _fTradeAmount 64.64-bit fixed point number, signed size of trade
     *  @return 64.64-bit fixed point number, a number between minus one and one
     */
    function _calculateBoundedSlippage(
        AMMVariables memory _ammVars,
        int128 _fTradeAmount
    ) internal pure returns (int128) {
        int128 fTradeSizeEMA = _ammVars.fCurrentTraderExposureEMA;
        int128 fSlippageSize = ONE_64x64;
        if (_fTradeAmount.abs() < fTradeSizeEMA) {
            fSlippageSize = fSlippageSize.sub(_fTradeAmount.abs().div(fTradeSizeEMA));
            fSlippageSize = ONE_64x64.sub(fSlippageSize.mul(fSlippageSize));
        }
        return _fTradeAmount > 0 ? fSlippageSize : fSlippageSize.neg();
    }

    /**
     *  Calculate AMM price.
     *
     *  All variables are 64.64-bit fixed point number (or struct thereof)
     *  @param _ammVars current AMM variables.
     *  @param _mktVars current Market variables (price&params)
     *                 Trader amounts k2 must already be factored in
     *                 that is, K2:=K2+k2, L1:=L1+k2*s2
     *  @param _fTradeAmount 64.64-bit fixed point number, signed size of trade
     *  @param _fHBidAskSpread half bid-ask spread, 64.64-bit fixed point number
     *  @return 64.64-bit fixed point number, AMM price
     */
    function calculatePerpetualPrice(
        AMMVariables memory _ammVars,
        MarketVariables memory _mktVars,
        int128 _fTradeAmount,
        int128 _fHBidAskSpread,
        int128 _fIncentiveSpread
    ) external view virtual override returns (int128) {
        // add minimal spread in quote currency
        _fHBidAskSpread = _fTradeAmount > 0 ? _fHBidAskSpread : _fHBidAskSpread.neg();
        if (_fTradeAmount == 0) {
            _fHBidAskSpread = 0;
        }
        // get risk-neutral default probability (always >0)
        {
            int128 fQ;
            int128 dd;
            int128 fkStar = _ammVars.fPoolM2.sub(_ammVars.fAMM_K2);
            (fQ, dd) = _calculateRiskNeutralPD(_ammVars, _mktVars, _fTradeAmount, true);
            if (_ammVars.fPoolM3 != 0) {
                // amend K* (see whitepaper)
                int128 nominator = _mktVars.fRho23.mul(_mktVars.fSigma2.mul(_mktVars.fSigma3));
                int128 denom = _mktVars.fSigma2.mul(_mktVars.fSigma2);
                int128 h = nominator.div(denom).mul(_ammVars.fPoolM3);
                h = h.mul(_mktVars.fIndexPriceS3).div(_mktVars.fIndexPriceS2);
                fkStar = fkStar.add(h);
            }
            // decide on sign of premium
            if (_fTradeAmount < fkStar) {
                fQ = fQ.neg();
            }
            // no rebate if exposure increases
            if (_fTradeAmount > 0 && _ammVars.fAMM_K2 > 0) {
                fQ = fQ > 0 ? fQ : int128(0);
            } else if (_fTradeAmount < 0 && _ammVars.fAMM_K2 < 0) {
                fQ = fQ < 0 ? fQ : int128(0);
            }
            // handle discontinuity at zero
            if (
                _fTradeAmount == 0 &&
                ((fQ < 0 && _ammVars.fAMM_K2 > 0) || (fQ > 0 && _ammVars.fAMM_K2 < 0))
            ) {
                fQ = fQ.div(TWO_64x64);
            }
            _fHBidAskSpread = _fHBidAskSpread.add(fQ);
        }
        // get additional slippage
        if (_fTradeAmount != 0) {
            _fIncentiveSpread = _fIncentiveSpread.mul(
                _calculateBoundedSlippage(_ammVars, _fTradeAmount)
            );
            _fHBidAskSpread = _fHBidAskSpread.add(_fIncentiveSpread);
        }
        // s2*(1 + sign(qp-q)*q + sign(k)*minSpread)
        return _mktVars.fIndexPriceS2.mul(ONE_64x64.add(_fHBidAskSpread));
    }

    /**
     *  Calculate target collateral M1 (Quote Currency), when no M2, M3 is present
     *  The targeted default probability is expressed using the inverse
     *  _fTargetDD = Phi^(-1)(targetPD)
     *  _fK2 in absolute terms must be 'reasonably large'
     *  sigma3, rho23, IndexpriceS3 not relevant.
     *  @param _fK2 signed 64.64-bit fixed point number, !=0, EWMA of actual K.
     *  @param _fL1 signed 64.64-bit fixed point number, >0, EWMA of actual L.
     *  @param  _mktVars contains 64.64 values for fIndexPriceS2*, fIndexPriceS3, fSigma2*, fSigma3, fRho23
     *  @param _fTargetDD signed 64.64-bit fixed point number
     *  @return M1Star signed 64.64-bit fixed point number, >0
     */
    function getTargetCollateralM1(
        int128 _fK2,
        int128 _fL1,
        MarketVariables memory _mktVars,
        int128 _fTargetDD
    ) external pure virtual override returns (int128) {
        assert(_fK2 != 0);
        assert(_mktVars.fSigma3 == 0);
        assert(_mktVars.fIndexPriceS3 == 0);
        assert(_mktVars.fRho23 == 0);
        int128 fMu2 = HALF_64x64.neg().mul(_mktVars.fSigma2).mul(_mktVars.fSigma2);
        int128 ddScaled = _fK2 < 0
            ? _mktVars.fSigma2.mul(_fTargetDD)
            : _mktVars.fSigma2.mul(_fTargetDD).neg();
        int128 A1 = ABDKMath64x64.exp(fMu2.add(ddScaled));
        return _fK2.mul(_mktVars.fIndexPriceS2).mul(A1).sub(_fL1);
    }

    /**
     *  Calculate target collateral *M2* (Base Currency), when no M1, M3 is present
     *  The targeted default probability is expressed using the inverse
     *  _fTargetDD = Phi^(-1)(targetPD)
     *  _fK2 in absolute terms must be 'reasonably large'
     *  sigma3, rho23, IndexpriceS3 not relevant.
     *  @param _fK2 signed 64.64-bit fixed point number, EWMA of actual K.
     *  @param _fL1 signed 64.64-bit fixed point number, EWMA of actual L.
     *  @param _mktVars contains 64.64 values for fIndexPriceS2, fIndexPriceS3, fSigma2, fSigma3, fRho23
     *  @param _fTargetDD signed 64.64-bit fixed point number
     *  @return M2Star signed 64.64-bit fixed point number
     */
    function getTargetCollateralM2(
        int128 _fK2,
        int128 _fL1,
        MarketVariables memory _mktVars,
        int128 _fTargetDD
    ) external pure virtual override returns (int128) {
        assert(_fK2 != 0);
        assert(_mktVars.fSigma3 == 0);
        assert(_mktVars.fIndexPriceS3 == 0);
        assert(_mktVars.fRho23 == 0);
        int128 fMu2 = HALF_64x64.mul(_mktVars.fSigma2).mul(_mktVars.fSigma2).neg();
        int128 ddScaled = _fL1 < 0
            ? _mktVars.fSigma2.mul(_fTargetDD)
            : _mktVars.fSigma2.mul(_fTargetDD).neg();
        int128 A1 = ABDKMath64x64.exp(fMu2.add(ddScaled)).mul(_mktVars.fIndexPriceS2);
        return _fK2.sub(_fL1.div(A1));
    }

    /**
     *  Calculate target collateral M3 (Quanto Currency), when no M1, M2 not present
     *  @param _fK2 signed 64.64-bit fixed point number. EWMA of actual K.
     *  @param _fL1 signed 64.64-bit fixed point number.  EWMA of actual L.
     *  @param  _mktVars contains 64.64 values for
     *           fIndexPriceS2, fIndexPriceS3, fSigma2, fSigma3, fRho23 - all required
     *  @param _fTargetDD signed 64.64-bit fixed point number
     *  @return M2Star signed 64.64-bit fixed point number
     */
    function getTargetCollateralM3(
        int128 _fK2,
        int128 _fL1,
        MarketVariables memory _mktVars,
        int128 _fTargetDD
    ) external pure override returns (int128) {
        assert(_fK2 != 0);
        assert(_mktVars.fSigma3 != 0);
        assert(_mktVars.fIndexPriceS3 != 0);
        // we solve the quadratic equation A x^2 + Bx + C = 0
        // B = 2 * [X + Y * target_dd^2 * (exp(rho*sigma2*sigma3) - 1) ]
        // C = X^2  - Y^2 * target_dd^2 * (exp(sigma2^2) - 1)
        // where:
        // X = L1 / S3 - Y and Y = K2 * S2 / S3
        // we re-use L1 for X and K2 for Y to save memory since they don't enter the equations otherwise
        _fK2 = _fK2.mul(_mktVars.fIndexPriceS2).div(_mktVars.fIndexPriceS3); // Y
        _fL1 = _fL1.div(_mktVars.fIndexPriceS3).sub(_fK2); // X
        // we only need the square of the target DD
        _fTargetDD = _fTargetDD.mul(_fTargetDD);
        // and we only need B/2
        int128 fHalfB = _fL1.add(
            _fK2.mul(_fTargetDD.mul(_mktVars.fRho23.mul(_mktVars.fSigma2.mul(_mktVars.fSigma3))))
        );
        int128 fC = _fL1.mul(_fL1).sub(
            _fK2.mul(_fK2).mul(_fTargetDD).mul(_mktVars.fSigma2.mul(_mktVars.fSigma2))
        );
        // A = 1 - (exp(sigma3^2) - 1) * target_dd^2
        int128 fA = ONE_64x64.sub(_mktVars.fSigma3.mul(_mktVars.fSigma3).mul(_fTargetDD));
        // we re-use C to store the discriminant: D = (B/2)^2 - A * C
        fC = fHalfB.mul(fHalfB).sub(fA.mul(fC));
        if (fC < 0) {
            // no solutions -> AMM is in profit, probability is smaller than target regardless of capital
            return int128(0);
        }
        // we want the larger of (-B/2 + sqrt((B/2)^2-A*C)) / A and (-B/2 - sqrt((B/2)^2-A*C)) / A
        // so it depends on the sign of A, or, equivalently, the sign of sqrt(...)/A
        fC = ABDKMath64x64.sqrt(fC).div(fA);
        fHalfB = fHalfB.div(fA);
        return fC > 0 ? fC.sub(fHalfB) : fC.neg().sub(fHalfB);
    }

    /**
     *  Calculate the required deposit for a new position
     *  of size _fPosition+_fTradeAmount and leverage _fTargetLeverage,
     *  having an existing position with balance fBalance0 and size _fPosition.
     *  This is the amount to be added to the margin collateral and can be negative (hence remove).
     *  Fees not factored-in.
     *  @param _fPosition0   signed 64.64-bit fixed point number. Position in base currency
     *  @param _fBalance0   signed 64.64-bit fixed point number. Current balance.
     *  @param _fTradeAmount signed 64.64-bit fixed point number. Trade amt in base currency
     *  @param _fTargetLeverage signed 64.64-bit fixed point number. Desired leverage
     *  @param _fPrice signed 64.64-bit fixed point number. Price for the trade of size _fTradeAmount
     *  @param _fS2Mark signed 64.64-bit fixed point number. Mark-price
     *  @param _fS3 signed 64.64-bit fixed point number. Collateral 2 quote conversion
     *  @return signed 64.64-bit fixed point number. Required cash_cc
     */
    function getDepositAmountForLvgPosition(
        int128 _fPosition0,
        int128 _fBalance0,
        int128 _fTradeAmount,
        int128 _fTargetLeverage,
        int128 _fPrice,
        int128 _fS2Mark,
        int128 _fS3,
        int128 _fS2
    ) external pure override returns (int128) {
        // calculation has to be aligned with _getAvailableMargin and _executeTrade
        // calculation
        // otherwise the calculated deposit might not be enough to declare
        // the margin to be enough
        // aligned with get available margin balance
        int128 fPremiumCash = _fTradeAmount.mul(_fPrice.sub(_fS2));
        int128 fDeltaLockedValue = _fTradeAmount.mul(_fS2);
        int128 fPnL = _fTradeAmount.mul(_fS2Mark);
        // we replace _fTradeAmount * price/S3 by
        // fDeltaLockedValue + fPremiumCash to be in line with
        // _executeTrade
        fPnL = fPnL.sub(fDeltaLockedValue).sub(fPremiumCash);
        int128 fLvgFrac = _fPosition0.add(_fTradeAmount).abs();
        fLvgFrac = fLvgFrac.mul(_fS2Mark).div(_fTargetLeverage);
        fPnL = fPnL.sub(fLvgFrac).div(_fS3);
        _fBalance0 = _fBalance0.add(fPnL);
        return _fBalance0.neg();
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "../core/PerpStorage.sol";
import "../interfaces/ILibraryEvents.sol";
import "../../libraries/ConverterDec18.sol";
import "../../libraries/EnumerableSetUpgradeable.sol";
import "../interfaces/IPerpetualRebalanceLogic.sol";
import "../interfaces/IPerpetualBrokerFeeLogic.sol";
import "../interfaces/IPerpetualUpdateLogic.sol";
import "../interfaces/IPerpetualMarginViewLogic.sol";
import "../interfaces/IPerpetualTradeLogic.sol";
import "../interfaces/IPerpetualTreasury.sol";
import "../interfaces/IPerpetualGetter.sol";
import "../interfaces/IPerpetualSetter.sol";
import "../../oracle/OracleFactory.sol";

contract PerpetualBaseFunctions is PerpStorage, ILibraryEvents {
    using ABDKMath64x64 for int128;
    using ConverterDec18 for int128;
    using ConverterDec18 for int256;
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
    uint64 internal constant WITHDRAWAL_DELAY_TIME_SEC = 1 * 86400; // 1 day

    /**
     * @dev Get LiquidityPool storage reference corresponding to a given perpetual id
     * @param _iPerpetualId Perpetual id, unique across liquidity pools
     * @return LiquidityPoolData
     */
    function _getLiquidityPoolFromPerpetual(
        uint24 _iPerpetualId
    ) internal view returns (LiquidityPoolData storage) {
        uint8 poolId = perpetualPoolIds[_iPerpetualId];
        return liquidityPools[poolId];
    }

    /**
     * @dev Get id of the LiquidityPool corresponding to a given perpetual id
     * @param _iPerpetualId Perpetual id, unique across liquidity pools
     * @return Liquidity Pool id
     */
    function _getPoolIdFromPerpetual(uint24 _iPerpetualId) internal view returns (uint8) {
        return perpetualPoolIds[_iPerpetualId];
    }

    /**
     * @dev Get perpetual reference from its 'globally' unique id
     * @param _iPerpetualId Perpetual id, unique across liquidity pools
     * @return PerpetualData
     */
    function _getPerpetual(uint24 _iPerpetualId) internal view returns (PerpetualData storage) {
        uint8 poolId = perpetualPoolIds[_iPerpetualId];
        require(poolId > 0, "perp not found");

        return perpetuals[poolId][_iPerpetualId];
    }

    /**
     * @dev Check if the account of the trader is empty in the perpetual, which means fCashCC = 0 and fPositionBC = 0
     * @param _perpetual The perpetual object
     * @param _traderAddr The address of the trader
     * @return True if the account of the trader is empty in the perpetual
     */
    function _isEmptyAccount(
        PerpetualData storage _perpetual,
        address _traderAddr
    ) internal view returns (bool) {
        MarginAccount storage account = marginAccounts[_perpetual.id][_traderAddr];
        return account.fCashCC == 0 && account.fPositionBC == 0;
    }

    /**
     * @dev Update the trader's cash in the margin account (trader can also be the AMM)
     * The 'cash' is denominated in collateral currency.
     * @param _perpetual   The perpetual struct
     * @param _traderAddr The address of the trader
     * @param _fDeltaCash signed 64.64-bit fixed point number.
     *                    Change of trader margin in collateral currency.
     */
    function _updateTraderMargin(
        PerpetualData storage _perpetual,
        address _traderAddr,
        int128 _fDeltaCash
    ) internal {
        if (_fDeltaCash == 0) {
            return;
        }
        MarginAccount storage account = marginAccounts[_perpetual.id][_traderAddr];
        account.fCashCC = account.fCashCC.add(_fDeltaCash);
    }

    /**
     * @dev Transfer from the user to the vault account.
     * Called by perp contracts only, no risk of third party using arbitrary _userAddr.
     * @param   _pool  Liquidity Pool
     * @param   _userAddr       The address of the account
     * @param   _fAmount        The amount of erc20 token to transfer in ABDK64x64 format.
     */
    function _transferFromUserToVault(
        LiquidityPoolData storage _pool,
        address _userAddr,
        int128 _fAmount
    ) internal {
        if (_fAmount <= 0) {
            return;
        }
        uint256 amountWei = _fAmount.toUDecN(_pool.marginTokenDecimals);
        IERC20Upgradeable marginToken = IERC20Upgradeable(_pool.marginTokenAddress);
        // slither-disable-next-line arbitrary-send-erc20
        marginToken.safeTransferFrom(_userAddr, address(this), amountWei);
    }

    /**
     * What is the current amount of collateral that we can
     * consider as borrowed?
     * Linear interpolation between prevBlock and nextBlock
     * @param _pool reference to liquidity pool
     * @return collateral amount in ABDK 64.64 format
     */
    function _getCollateralTokenAmountForPricing(
        LiquidityPoolData storage _pool
    ) internal view returns (int128) {
        if (_pool.totalSupplyShareToken == 0) {
            return 0;
        }
        int128 pnlPartCash = _pool.fPnLparticipantsCashCC;
        uint256 shareProportion = (uint256(_getShareTokenAmountForPricing(_pool)) * 10 ** 18) /
            uint256(_pool.totalSupplyShareToken);
        return int256(shareProportion).fromDec18().mul(pnlPartCash);
    }

    /**
     * to simplify testing we write an internal function
     * @return delay for withdrawing
     */
    function _getDelay() internal pure virtual returns (uint64) {
        return WITHDRAWAL_DELAY_TIME_SEC;
    }

    /**
     * Internal implementation of getShareTokenAmountForPricing
     * @param _pool  reference of liquidity pool
     * @return share token number
     */
    function _getShareTokenAmountForPricing(
        LiquidityPoolData storage _pool
    ) internal view returns (uint128) {
        uint64 thisTs = uint64(block.timestamp);
        if (thisTs >= _pool.prevAnchor + _getDelay()) {
            return _pool.nextTokenAmount;
        }
        uint128 ratioDec100 = ((thisTs - _pool.prevAnchor) * 100) / (_getDelay());
        return
            _pool.nextTokenAmount > _pool.prevTokenAmount
                ? _pool.prevTokenAmount +
                    (ratioDec100 * (_pool.nextTokenAmount - _pool.prevTokenAmount)) /
                    100
                : _pool.prevTokenAmount -
                    (ratioDec100 * (_pool.prevTokenAmount - _pool.nextTokenAmount)) /
                    100;
    }

    /**
     * Transfer from the vault to the user account.
     * @param   _pool Liquidity pool
     * @param   _traderAddr    The address of the account
     * @param   _fAmount       The amount of erc20 token to transfer
     */
    function _transferFromVaultToUser(
        LiquidityPoolData storage _pool,
        address _traderAddr,
        int128 _fAmount
    ) internal {
        if (_fAmount <= 0) {
            return;
        }
        uint256 amountWei = _fAmount.toUDecN(_pool.marginTokenDecimals);
        if (amountWei == 0) {
            return;
        }
        IERC20Upgradeable marginToken = IERC20Upgradeable(_pool.marginTokenAddress);
        // transfer the margin token to the user
        marginToken.safeTransfer(_traderAddr, amountWei);
    }

    /**
     * @dev Get safe Oracle price of the base index S2 of a given perpetual
     */
    function _getSafeOraclePriceS2(
        PerpetualData storage _perpetual
    ) internal view returns (int128) {
        return
            _getSafeOraclePrice(
                _perpetual.S2BaseCCY,
                _perpetual.S2QuoteCCY,
                _perpetual.fSettlementS2PriceData
            );
    }

    /**
     * @dev Get safe Oracle price of the quanto index S3 of a given perpetual
     * @param _perpetual Perpetual storage reference
     */
    function _getSafeOraclePriceS3(
        PerpetualData storage _perpetual
    ) internal view returns (int128) {
        return
            _getSafeOraclePrice(
                _perpetual.S3BaseCCY,
                _perpetual.S3QuoteCCY,
                _perpetual.fSettlementS3PriceData
            );
    }

    /**
     * @dev Get safe oracle price for a given currency pair and fallback price
     * The fallback or settlement price is used when the market is closed (oracle returns 0 price)
     * @param base Base currency
     * @param quote Quote currency
     * @param _fSettlement Settlement price to default to when markets close
     */
    function _getSafeOraclePrice(
        bytes4 base,
        bytes4 quote,
        int128 _fSettlement
    ) internal view returns (int128) {
        (int128 fPrice, ) = OracleFactory(oracleFactoryAddress).getSpotPrice(base, quote);
        if (fPrice == 0) {
            // return settlement price
            return _fSettlement;
        }
        return fPrice;
    }

    /**
     * @dev Get oracle price for a given currency pair
     * Not safe in the sense that it could return 0 if markets are closed
     * @param _baseQuote Currency pair
     */
    function _getOraclePrice(bytes4[2] memory _baseQuote) internal view returns (int128) {
        (int128 fPrice, ) = OracleFactory(oracleFactoryAddress).getSpotPrice(
            _baseQuote[0],
            _baseQuote[1]
        );
        return fPrice;
    }

    /**
     * Get the multiplier that converts <base> into
     * the value of <collateralcurrency>
     * Hence 1 if collateral currency = base currency
     * If the state of the perpetual is not "NORMAL",
     * use the settlement price
     * @param   _perpetual           The reference of perpetual storage.
     * @param   _isMarkPriceRequest  If true, get the conversion for the mark-price. If false for spot.
     * @param   _bUseOracle If false, the settlement price is used to compute the B2Q conversion
     * @return  The index price of the collateral for the given perpetual.
     */
    function _getBaseToCollateralConversionMultiplier(
        PerpetualData storage _perpetual,
        bool _isMarkPriceRequest,
        bool _bUseOracle
    ) internal view returns (int128) {
        AMMPerpLogic.CollateralCurrency ccy = _perpetual.eCollateralCurrency;
        /*
        Quote: Pos * markprice --> quote currency
        Base: Pos * markprice / indexprice; E.g., 0.1 BTC * 36500 / 36000
        Quanto: Pos * markprice / index3price. E.g., 0.1 BTC * 36500 / 2000 = 1.83 ETH
        where markprice is replaced by indexprice if _isMarkPriceRequest=FALSE
        */
        int128 fPx2;
        int128 fPxIndex2;
        if (!_bUseOracle || _perpetual.state != PerpetualState.NORMAL) {
            fPxIndex2 = _perpetual.fSettlementS2PriceData;
            require(fPxIndex2 > 0, "settl px S2 not set");
        } else {
            fPxIndex2 = _getSafeOraclePriceS2(_perpetual);
        }

        if (_isMarkPriceRequest) {
            fPx2 = _getPerpetualMarkPrice(_perpetual, _bUseOracle);
        } else {
            fPx2 = fPxIndex2;
        }

        if (ccy == AMMPerpLogic.CollateralCurrency.BASE) {
            // equals ONE if _isMarkPriceRequest=FALSE
            return fPx2.div(fPxIndex2);
        }
        if (ccy == AMMPerpLogic.CollateralCurrency.QUANTO) {
            // Example: 0.5 contracts of ETHUSD paid in BTC
            //  the rate is ETHUSD * 1/BTCUSD
            //  BTCUSD = 31000 => 0.5/31000 = 0.00003225806452 BTC
            return
                _bUseOracle && _perpetual.state == PerpetualState.NORMAL
                    ? fPx2.div(_getSafeOraclePriceS3(_perpetual))
                    : fPx2.div(_perpetual.fSettlementS3PriceData);
        } else {
            // Example: 0.5 contracts of ETHUSD paid in USD
            //  the rate is ETHUSD
            //  ETHUSD = 2000 => 0.5 * 2000 = 1000
            require(ccy == AMMPerpLogic.CollateralCurrency.QUOTE, "unknown state");
            return fPx2;
        }
    }

    /**
     * Get the mark price of the perpetual. If the state of the perpetual is not "NORMAL",
     * return the settlement price
     * @param   _perpetual The perpetual in the liquidity pool
     * @param   _bUseOracle If false, the mark premium is applied to the current settlement price.
     * @return  markPrice  The mark price of current perpetual.
     */
    function _getPerpetualMarkPrice(
        PerpetualData storage _perpetual,
        bool _bUseOracle
    ) internal view returns (int128) {
        int128 fPremiumRate = _perpetual.currentMarkPremiumRate.fPrice;
        int128 markPrice = _bUseOracle && _perpetual.state == PerpetualState.NORMAL
            ? (_getSafeOraclePriceS2(_perpetual)).mul(ONE_64x64.add(fPremiumRate))
            : (_perpetual.fSettlementS2PriceData).mul(ONE_64x64.add(fPremiumRate));
        return markPrice;
    }

    /**
     * Get the multiplier that converts <collateralcurrency> into
     * the value of <quotecurrency>
     * Hence 1 if collateral currency = quote currency
     * If the state of the perpetual is not "NORMAL",
     * use the settlement price
     * @param   _perpetual           The reference of perpetual storage.
     * @param   _bUseOracle          If false, the settlement price is used to compute the B2Q conversion
     * @return  The index price of the collateral for the given perpetual.
     */
    function _getCollateralToQuoteConversionMultiplier(
        PerpetualData storage _perpetual,
        bool _bUseOracle
    ) internal view returns (int128) {
        AMMPerpLogic.CollateralCurrency ccy = _perpetual.eCollateralCurrency;
        /*
            Quote: 1
            Base: S2, e.g. we hold 1 BTC -> 36000 USD
            Quanto: S3, e.g., we hold 1 ETH -> 2000 USD
        */
        if (ccy == AMMPerpLogic.CollateralCurrency.BASE) {
            return
                _bUseOracle && _perpetual.state == PerpetualState.NORMAL
                    ? _getSafeOraclePriceS2(_perpetual)
                    : _perpetual.fSettlementS2PriceData;
        }
        if (ccy == AMMPerpLogic.CollateralCurrency.QUANTO) {
            return
                _bUseOracle && _perpetual.state == PerpetualState.NORMAL
                    ? _getSafeOraclePriceS3(_perpetual)
                    : _perpetual.fSettlementS3PriceData;
        } else {
            return ONE_64x64;
        }
    }

    /**
     * Determines the amount of funds allocated to a given perpetual from its corresponding liquidity pool
     * @dev These are the funds that are used for:
     *  - Risk calculations: e.g. pricing and k star
     *  - Settlement: insufficient allocated funds can cause the perpetual to enter emergency state
     * The funds are determined by allocating to each perpetual either its target amount,
     * or a pro-rated amount if the total funds in the pool are below its overall target
     * @param _perpetual Perpetual reference
     * @return fFunds Amount of funds in collateral currency
     */
    function _getPerpetualAllocatedFunds(
        PerpetualData storage _perpetual
    ) internal view returns (int128 fFunds) {
        if (_perpetual.fTargetAMMFundSize <= 0) {
            return 0;
        }
        LiquidityPoolData storage pool = liquidityPools[_perpetual.poolId];
        int128 fPricingCash = _getCollateralTokenAmountForPricing(pool);
        if (fPricingCash <= 0) {
            return 0;
        }
        if (fPricingCash > pool.fTargetAMMFundSize) {
            fFunds = _perpetual.fTargetAMMFundSize;
        } else {
            fFunds = fPricingCash.mul(_perpetual.fTargetAMMFundSize.div(pool.fTargetAMMFundSize));
        }
    }

    /**
     * Prepare data for pricing functions (AMMPerpModule)
     * @param   _perpetual The reference of perpetual storage.
     * @param   _bUseOracle Should the oracle price be used? If false or market is closed, the settlement price is used
     */
    function _prepareAMMAndMarketData(
        PerpetualData storage _perpetual,
        bool _bUseOracle
    )
        internal
        view
        returns (AMMPerpLogic.AMMVariables memory, AMMPerpLogic.MarketVariables memory)
    {
        // prepare data
        AMMPerpLogic.AMMVariables memory ammState;
        AMMPerpLogic.MarketVariables memory marketState;

        marketState.fIndexPriceS2 = _bUseOracle
            ? _getSafeOraclePriceS2(_perpetual)
            : _perpetual.fSettlementS2PriceData;
        marketState.fSigma2 = int128(_perpetual.fSigma2) << 35;
        MarginAccount storage AMMMarginAcc = marginAccounts[_perpetual.id][address(this)];
        // get current locked-in value
        ammState.fLockedValue1 = AMMMarginAcc.fLockedInValueQC.neg();

        // get current position of all traders (= - AMM position)
        ammState.fAMM_K2 = AMMMarginAcc.fPositionBC.neg();
        // get cash from PnL fund that we can use when pricing
        int128 fPricingPnLCashCC = _getPerpetualAllocatedFunds(_perpetual);
        // add cash from AMM margin account
        fPricingPnLCashCC = fPricingPnLCashCC.add(AMMMarginAcc.fCashCC);
        AMMPerpLogic.CollateralCurrency ccy = _perpetual.eCollateralCurrency;
        if (ccy == AMMPerpLogic.CollateralCurrency.BASE) {
            ammState.fPoolM2 = fPricingPnLCashCC;
        } else if (ccy == AMMPerpLogic.CollateralCurrency.QUANTO) {
            ammState.fPoolM3 = fPricingPnLCashCC;
            // additional parameters for quanto case
            int128 fPx = _bUseOracle
                ? _getSafeOraclePriceS3(_perpetual)
                : _perpetual.fSettlementS3PriceData;
            marketState.fIndexPriceS3 = fPx;
            marketState.fSigma3 = int128(_perpetual.fSigma3) << 35;
            marketState.fRho23 = int128(_perpetual.fRho23) << 35;
        } else {
            assert(ccy == AMMPerpLogic.CollateralCurrency.QUOTE);
            ammState.fPoolM1 = fPricingPnLCashCC;
        }
        ammState.fCurrentTraderExposureEMA = _perpetual.fCurrentTraderExposureEMA;
        return (ammState, marketState);
    }

    /**
     * @dev     Select an arbitrary perpetual that will be processed
     *
     * @param   _iPoolIdx       pool index of that perpetual
     */
    function _selectPerpetualIds(uint8 _iPoolIdx) internal view returns (uint24) {
        require(_iPoolIdx > 0, "pool not found");
        LiquidityPoolData storage liquidityPool = liquidityPools[_iPoolIdx];
        require(liquidityPool.iPerpetualCount > 0, "no perp in pool");
        // idx doesn't have to be random
        // slither-disable-next-line weak-prng
        uint16 idx = uint16(block.timestamp % uint64(liquidityPool.iPerpetualCount));
        return perpetualIds[liquidityPool.id][idx];
    }

    /*
     * Check if two numbers have the same sign. Zero has the same sign with any number
     * @param   _fX 64.64 fixed point number
     * @param   _fY 64.64 fixed point number
     * @return  True if the numbers have the same sign or one of them is zero.
     */
    function _hasTheSameSign(int128 _fX, int128 _fY) internal pure returns (bool) {
        if (_fX == 0 || _fY == 0) {
            return true;
        }
        return (_fX ^ _fY) >> 127 == 0;
    }

    /**
     * Calculate Exponentially Weighted Moving Average.
     * Returns updated EMA based on
     * _fEMA = _fLambda * _fEMA + (1-_fLambda)* _fCurrentObs
     * @param _fEMA signed 64.64-bit fixed point number
     * @param _fCurrentObs signed 64.64-bit fixed point number
     * @param _fLambda signed 64.64-bit fixed point number
     * @return fNewEMA updated EMA, signed 64.64-bit fixed point number
     */
    function _ema(
        int128 _fEMA,
        int128 _fCurrentObs,
        int128 _fLambda
    ) internal pure returns (int128 fNewEMA) {
        require(_fLambda > 0, "EMALambda must be gt 0");
        require(_fLambda < ONE_64x64, "EMALambda must be st 1");
        // result must be between the two values _fCurrentObs and _fEMA, so no overflow
        fNewEMA = ABDKMath64x64.add(
            _fEMA.mul(_fLambda),
            ABDKMath64x64.mul(ONE_64x64.sub(_fLambda), _fCurrentObs)
        );
    }

    function _getTradeLogic() internal view returns (IPerpetualTradeLogic) {
        return IPerpetualTradeLogic(address(this));
    }

    function _getAMMPerpLogic() internal view returns (IAMMPerpLogic) {
        return IAMMPerpLogic(address(ammPerpLogic));
    }

    function _getRebalanceLogic() internal view returns (IPerpetualRebalanceLogic) {
        return IPerpetualRebalanceLogic(address(this));
    }

    function _getBrokerFeeLogic() internal view returns (IPerpetualBrokerFeeLogic) {
        return IPerpetualBrokerFeeLogic(address(this));
    }

    function _getUpdateLogic() internal view returns (IPerpetualUpdateLogic) {
        return IPerpetualUpdateLogic(address(this));
    }

    function _getMarginViewLogic() internal view returns (IPerpetualMarginViewLogic) {
        return IPerpetualMarginViewLogic(address(this));
    }

    function _getPerpetualGetter() internal view returns (IPerpetualGetter) {
        return IPerpetualGetter(address(this));
    }

    function _getPerpetualSetter() internal view returns (IPerpetualSetter) {
        return IPerpetualSetter(address(this));
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "../functions/AMMPerpLogic.sol";

interface IAMMPerpLogic {
    function calculateDefaultFundSize(
        int128[2] memory _fK2AMM,
        int128 _fk2Trader,
        int128 _fCoverN,
        int128[2] memory fStressRet2,
        int128[2] memory fStressRet3,
        int128[2] memory fIndexPrices,
        AMMPerpLogic.CollateralCurrency _eCCY
    ) external pure returns (int128);

    function calculateRiskNeutralPD(
        AMMPerpLogic.AMMVariables memory _ammVars,
        AMMPerpLogic.MarketVariables memory _mktVars,
        int128 _fTradeAmount,
        bool _withCDF
    ) external view returns (int128, int128);

    function calculatePerpetualPrice(
        AMMPerpLogic.AMMVariables memory _ammVars,
        AMMPerpLogic.MarketVariables memory _mktVars,
        int128 _fTradeAmount,
        int128 _fBidAskSpread,
        int128 _fIncentiveSpread
    ) external view returns (int128);

    function getTargetCollateralM1(
        int128 _fK2,
        int128 _fL1,
        AMMPerpLogic.MarketVariables memory _mktVars,
        int128 _fTargetDD
    ) external pure returns (int128);

    function getTargetCollateralM2(
        int128 _fK2,
        int128 _fL1,
        AMMPerpLogic.MarketVariables memory _mktVars,
        int128 _fTargetDD
    ) external pure returns (int128);

    function getTargetCollateralM3(
        int128 _fK2,
        int128 _fL1,
        AMMPerpLogic.MarketVariables memory _mktVars,
        int128 _fTargetDD
    ) external pure returns (int128);

    function getDepositAmountForLvgPosition(
        int128 _fPosition0,
        int128 _fBalance0,
        int128 _fTradeAmount,
        int128 _fTargetLeverage,
        int128 _fPrice,
        int128 _fS2Mark,
        int128 _fS3,
        int128 _fS2
    ) external pure returns (int128);
}

File 38 of 48 : IFunctionList.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IFunctionList {
    function getFunctionList()
        external
        pure
        returns (bytes4[] memory functionSignatures, bytes32 moduleName);
}

File 39 of 48 : ILibraryEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import "./IPerpetualOrder.sol";

/**
 * @notice  The libraryEvents defines events that will be raised from modules (contract/modules).
 * @dev     DO REMEMBER to add new events in modules here.
 */
interface ILibraryEvents {
    // PerpetualModule
    event Clear(uint24 indexed perpetualId, address indexed trader);
    event Settle(uint24 indexed perpetualId, address indexed trader, int256 amount);
    event SettlementComplete(uint24 indexed perpetualId);
    event SetNormalState(uint24 indexed perpetualId);
    event SetEmergencyState(
        uint24 indexed perpetualId,
        int128 fSettlementMarkPremiumRate,
        int128 fSettlementS2Price,
        int128 fSettlementS3Price
    );
    event SettleState(uint24 indexed perpetualId);
    event SetClearedState(uint24 indexed perpetualId);

    // Participation pool
    event LiquidityAdded(
        uint8 indexed poolId,
        address indexed user,
        uint256 tokenAmount,
        uint256 shareAmount
    );
    event LiquidityProvisionPaused(bool pauseOn, uint8 poolId);
    event LiquidityRemoved(
        uint8 indexed poolId,
        address indexed user,
        uint256 tokenAmount,
        uint256 shareAmount
    );
    event LiquidityWithdrawalInitiated(
        uint8 indexed poolId,
        address indexed user,
        uint256 shareAmount
    );

    // setters
    // oracles
    event SetOracles(uint24 indexed perpetualId, bytes4[2] baseQuoteS2, bytes4[2] baseQuoteS3);
    // perp parameters
    event SetPerpetualBaseParameters(uint24 indexed perpetualId, int128[7] baseParams);
    event SetPerpetualRiskParameters(
        uint24 indexed perpetualId,
        int128[5] underlyingRiskParams,
        int128[12] defaultFundRiskParams
    );
    event SetParameter(uint24 indexed perpetualId, string name, int128 value);
    event SetParameterPair(uint24 indexed perpetualId, string name, int128 value1, int128 value2);
    // pool parameters
    event SetPoolParameter(uint8 indexed poolId, string name, int128 value);

    event TransferAddressTo(string name, address oldOBFactory, address newOBFactory); // only governance
    event SetBlockDelay(uint8 delay);

    // fee structure parameters
    event SetBrokerDesignations(uint32[] designations, uint16[] fees);
    event SetBrokerTiers(uint256[] tiers, uint16[] feesTbps);
    event SetTraderTiers(uint256[] tiers, uint16[] feesTbps);
    event SetTraderVolumeTiers(uint256[] tiers, uint16[] feesTbps);
    event SetBrokerVolumeTiers(uint256[] tiers, uint16[] feesTbps);
    event SetUtilityToken(address tokenAddr);

    event BrokerLotsTransferred(
        uint8 indexed poolId,
        address oldOwner,
        address newOwner,
        uint32 numLots
    );
    event BrokerVolumeTransferred(
        uint8 indexed poolId,
        address oldOwner,
        address newOwner,
        int128 fVolume
    );

    // brokers
    event UpdateBrokerAddedCash(uint8 indexed poolId, uint32 iLots, uint32 iNewBrokerLots);

    // TradeModule

    event Trade(
        uint24 indexed perpetualId,
        address indexed trader,
        IPerpetualOrder.Order order,
        bytes32 orderDigest,
        int128 newPositionSizeBC,
        int128 price,
        int128 fFeeCC,
        int128 fPnlCC,
        int128 fB2C
    );

    event UpdateMarginAccount(
        uint24 indexed perpetualId,
        address indexed trader,
        int128 fFundingPaymentCC
    );

    event Liquidate(
        uint24 perpetualId,
        address indexed liquidator,
        address indexed trader,
        int128 amountLiquidatedBC,
        int128 liquidationPrice,
        int128 newPositionSizeBC,
        int128 fFeeCC,
        int128 fPnlCC
    );

    event PerpetualLimitOrderCancelled(uint24 indexed perpetualId, bytes32 indexed orderHash);
    event DistributeFees(
        uint8 indexed poolId,
        uint24 indexed perpetualId,
        address indexed trader,
        int128 protocolFeeCC,
        int128 participationFundFeeCC
    );

    // PerpetualManager/factory
    event RunLiquidityPool(uint8 _liqPoolID);
    event LiquidityPoolCreated(
        uint8 id,
        address marginTokenAddress,
        address shareTokenAddress,
        uint16 iTargetPoolSizeUpdateTime,
        int128 fBrokerCollateralLotSize
    );
    event PerpetualCreated(
        uint8 poolId,
        uint24 id,
        int128[7] baseParams,
        int128[5] underlyingRiskParams,
        int128[12] defaultFundRiskParams,
        uint256 eCollateralCurrency
    );

    // emit tokenAddr==0x0 if the token paid is the aggregated token, otherwise the address of the token
    event TokensDeposited(uint24 indexed perpetualId, address indexed trader, int128 amount);
    event TokensWithdrawn(uint24 indexed perpetualId, address indexed trader, int128 amount);

    event UpdateMarkPrice(
        uint24 indexed perpetualId,
        int128 fMidPricePremium,
        int128 fMarkPricePremium,
        int128 fSpotIndexPrice
    );

    event UpdateFundingRate(uint24 indexed perpetualId, int128 fFundingRate);

    event SetDelegate(address indexed trader, address indexed delegate, uint256 index);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import "../interfaces/IPerpetualOrder.sol";
import "../../interface/ISpotOracle.sol";

interface IPerpetualBrokerFeeLogic {
    function determineExchangeFee(IPerpetualOrder.Order memory _order)
        external
        view
        returns (uint16);

    function updateVolumeEMAOnNewTrade(
        uint24 _iPerpetualId,
        address _traderAddr,
        address _brokerAddr,
        int128 _tradeAmountBC
    ) external;

    function queryExchangeFee(
        uint8 _poolId,
        address _traderAddr,
        address _brokerAddr
    ) external view returns (uint16);

    function splitProtocolFee(uint16 fee) external pure returns (int128, int128);

    function setFeesForDesignation(uint32[] calldata _designations, uint16[] calldata _fees)
        external;

    function getLastPerpetualBaseToUSDConversion(uint24 _iPerpetualId)
        external
        view
        returns (int128);

    function getFeeForTraderVolume(uint8 _poolId, address _traderAddr)
        external
        view
        returns (uint16);

    function getFeeForBrokerVolume(uint8 _poolId, address _brokerAddr)
        external
        view
        returns (uint16);

    function setOracleFactoryForPerpetual(uint24 _iPerpetualId, address _oracleAddr) external;

    function setBrokerTiers(uint256[] calldata _tiers, uint16[] calldata _feesTbps) external;

    function setTraderTiers(uint256[] calldata _tiers, uint16[] calldata _feesTbps) external;

    function setTraderVolumeTiers(uint256[] calldata _tiers, uint16[] calldata _feesTbps) external;

    function setBrokerVolumeTiers(uint256[] calldata _tiers, uint16[] calldata _feesTbps) external;

    function setUtilityTokenAddr(address tokenAddr) external;

    function getBrokerInducedFee(uint8 _poolId, address _brokerAddr)
        external
        view
        returns (uint16);

    function getBrokerDesignation(uint8 _poolId, address _brokerAddr)
        external
        view
        returns (uint32);

    function getFeeForBrokerDesignation(uint32 _brokerDesignation) external view returns (uint16);

    function getFeeForBrokerStake(address brokerAddr) external view returns (uint16);

    function getFeeForTraderStake(address traderAddr) external view returns (uint16);

    function getCurrentTraderVolume(uint8 _poolId, address _traderAddr)
        external
        view
        returns (int128);

    function getCurrentBrokerVolume(uint8 _poolId, address _brokerAddr)
        external
        view
        returns (int128);

    function transferBrokerLots(
        uint8 _poolId,
        address _transferToAddr,
        uint32 _lots
    ) external;

    function transferBrokerOwnership(uint8 _poolId, address _transferToAddr) external;

    function setInitialVolumeForFee(
        uint8 _poolId,
        address _brokerAddr,
        uint16 _feeTbps
    ) external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "../core/PerpStorage.sol";
import "../../interface/IShareTokenFactory.sol";

interface IPerpetualGetter {
    function getAMMPerpLogic() external view returns (address);

    function getShareTokenFactory() external view returns (IShareTokenFactory);

    function getOracleFactory() external view returns (address);

    function getTreasuryAddress() external view returns (address);

    function getOrderBookFactoryAddress() external view returns (address);

    function getOrderBookAddress(uint24 _perpetualId) external view returns (address);

    function isPerpMarketClosed(uint24 _perpetualId) external view returns (bool isClosed);

    function getOracleUpdateTime(uint24 _perpetualId) external view returns (uint256);

    function isDelegate(address _trader, address _delegate) external view returns (bool);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "./IPerpetualOrder.sol";

interface IPerpetualMarginViewLogic is IPerpetualOrder {
    function calcMarginForTargetLeverage(
        uint24 _iPerpetualId,
        int128 _fTraderPos,
        int128 _fPrice,
        int128 _fTradeAmountBC,
        int128 _fTargetLeverage,
        address _traderAddr,
        bool _ignorePosBalance
    ) external view returns (int128);

    function getMarginBalance(uint24 _iPerpetualId, address _traderAddr)
        external
        view
        returns (int128);

    function isMaintenanceMarginSafe(uint24 _iPerpetualId, address _traderAddr)
        external
        view
        returns (bool);

    function getAvailableMargin(
        uint24 _iPerpetualId,
        address _traderAddr,
        bool _isInitialMargin
    ) external view returns (int128);

    function isInitialMarginSafe(uint24 _iPerpetualId, address _traderAddr)
        external
        view
        returns (bool);

    function getInitialMargin(uint24 _iPerpetualId, address _traderAddr)
        external
        view
        returns (int128);

    function getMaintenanceMargin(uint24 _iPerpetualId, address _traderAddr)
        external
        view
        returns (int128);

    function isMarginSafe(uint24 _iPerpetualId, address _traderAddr) external view returns (bool);

    function getAvailableCash(uint24 _iPerpetualId, address _traderAddr)
        external
        view
        returns (int128);
}

File 43 of 48 : IPerpetualOrder.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IPerpetualOrder {
    struct Order {
        uint16 leverageTDR; // 12.43x leverage is represented by 1243 (two-digit integer representation); 0 if deposit and trade separate
        uint16 brokerFeeTbps; // broker can set their own fee
        uint24 iPerpetualId; // global id for perpetual
        address traderAddr; // address of trader
        uint32 executionTimestamp; // normally set to current timestamp; order will not be executed prior to this timestamp.
        address brokerAddr; // address of the broker or zero
        uint32 submittedTimestamp;
        uint32 flags; // order flags
        uint32 iDeadline; //deadline for price (seconds timestamp)
        address executorAddr; // address of the executor set by contract
        int128 fAmount; // amount in base currency to be traded
        int128 fLimitPrice; // limit price
        int128 fTriggerPrice; //trigger price. Non-zero for stop orders.
        bytes brokerSignature; //signature of broker (or 0)
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IPerpetualRebalanceLogic {
    function rebalance(uint24 _iPerpetualId) external;

    function decreasePoolCash(uint8 _iPoolIdx, int128 _fAmount) external;

    function increasePoolCash(uint8 _iPoolIdx, int128 _fAmount) external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IPerpetualSetter {
    function setPerpetualOracles(
        uint24 _iPerpetualId,
        bytes4[2] calldata _baseQuoteS2,
        bytes4[2] calldata _baseQuoteS3
    ) external;

    function setPerpetualBaseParams(uint24 _iPerpetualId, int128[7] calldata _baseParams) external;

    function setPerpetualRiskParams(
        uint24 _iPerpetualId,
        int128[5] calldata _underlyingRiskParams,
        int128[12] calldata _defaultFundRiskParams
    ) external;

    function setPerpetualParam(
        uint24 _iPerpetualId,
        string memory _varName,
        int128 _value
    ) external;

    function setPerpetualParamPair(
        uint24 _iPerpetualId,
        string memory _name,
        int128 _value1,
        int128 _value2
    ) external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import "../interfaces/IPerpetualOrder.sol";

interface IPerpetualTradeLogic {
    function executeTrade(
        uint24 _iPerpetualId,
        address _traderAddr,
        int128 _fTraderPos,
        int128 _fTradeAmount,
        int128 _fPrice,
        bool _isClose
    ) external returns (int128);

    function preTrade(IPerpetualOrder.Order memory _order) external returns (int128, int128);

    function distributeFeesLiquidation(
        uint24 _iPerpetualId,
        address _traderAddr,
        int128 _fDeltaPositionBC,
        uint16 _protocolFeeTbps
    ) external returns (int128);

    function distributeFees(
        IPerpetualOrder.Order memory _order,
        uint16 _brkrFeeTbps,
        uint16 _protocolFeeTbps,
        bool _hasOpened
    ) external returns (int128);

    function validateStopPrice(
        bool _isLong,
        int128 _fMarkPrice,
        int128 _fTriggerPrice
    ) external pure;

    function getMaxSignedOpenTradeSizeForPos(
        uint24 _perpetualId,
        int128 _fCurrentTraderPos,
        bool _isBuy
    ) external view returns (int128);

    function queryPerpetualPrice(
        uint24 _iPerpetualId,
        int128 _fTradeAmountBC,
        int128[2] calldata _fIndexPrice
    ) external view returns (int128);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "../core/PerpStorage.sol";

interface IPerpetualTreasury {
    function addLiquidity(uint8 _iPoolIndex, uint256 _tokenAmount) external;

    function pauseLiquidityProvision(uint8 _poolId, bool _pauseOn) external;

    function withdrawLiquidity(uint8 _iPoolIndex, uint256 _shareAmount) external;

    function executeLiquidityWithdrawal(uint8 _poolId, address _lpAddr) external;

    function getCollateralTokenAmountForPricing(uint8 _poolId) external view returns (int128);

    function getShareTokenPriceD18(uint8 _poolId) external view returns (uint256 price);

    function getTokenAmountToReturn(
        uint8 _poolId,
        uint256 _shareAmount
    ) external view returns (uint256);

    function getWithdrawRequests(
        uint8 poolId,
        uint256 _fromIdx,
        uint256 numRequests
    ) external view returns (PerpStorage.WithdrawRequest[] memory);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IPerpetualUpdateLogic {
    function updateAMMTargetFundSize(uint24 _iPerpetualId) external;

    function updateDefaultFundTargetSizeRandom(uint8 _iPoolIndex) external;

    function updateDefaultFundTargetSize(uint24 _iPerpetualId) external;

    function updateFundingAndPricesBefore(uint24 _iPerpetualId, bool _revertIfClosed) external;

    function updateFundingAndPricesAfter(uint24 _iPerpetualId) external;

    function setNormalState(uint24 _iPerpetualId) external;

    /**
     * Set emergency state
     * @param _iPerpetualId Perpetual id
     */
    function setEmergencyState(uint24 _iPerpetualId) external;

    /**
     * @notice Set external treasury (DAO)
     * @param _treasury treasury address
     */
    function setTreasury(address _treasury) external;

    /**
     * @notice Set order book factory (DAO)
     * @param _orderBookFactory order book factory address
     */
    function setOrderBookFactory(address _orderBookFactory) external;

    /**
     * @notice Set oracle factory (DAO)
     * @param _oracleFactory oracle factory address
     */
    function setOracleFactory(address _oracleFactory) external;

    /**
     * @notice Set delay for trades to be executed
     * @param _delay    delay in number of blocks
     */
    function setBlockDelay(uint8 _delay) external;

    /**
     * @notice Submits price updates to the feeds used by a given perpetual.
     * @dev Reverts if the submission does not match the perpetual or
     * if the feed rejects it for a reason other than being unnecessary.
     * If this function returns false, sender is not charged msg.value.
     * @param _perpetualId Perpetual Id
     * @param _updateData Data to send to price feeds
     * @param _publishTimes Publish timestamps
     * @param _maxAcceptableFeedAge Maximum age of update in seconds
     */
    function updatePriceFeeds(
        uint24 _perpetualId,
        bytes[] calldata _updateData,
        uint64[] calldata _publishTimes,
        uint256 _maxAcceptableFeedAge
    ) external payable;

    /**
     * @notice Links the message sender to a delegate to manage orders on their behalf.
     * @param delegate Address of delegate
     * @param index Index to emit with event. A value of zero removes the current delegate.
     */
    function setDelegate(address delegate, uint256 index) external;
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 100000000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"poolId","type":"uint8"},{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"},{"indexed":false,"internalType":"uint32","name":"numLots","type":"uint32"}],"name":"BrokerLotsTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"poolId","type":"uint8"},{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"},{"indexed":false,"internalType":"int128","name":"fVolume","type":"int128"}],"name":"BrokerVolumeTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":true,"internalType":"address","name":"trader","type":"address"}],"name":"Clear","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"poolId","type":"uint8"},{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int128","name":"protocolFeeCC","type":"int128"},{"indexed":false,"internalType":"int128","name":"participationFundFeeCC","type":"int128"}],"name":"DistributeFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousGovernance","type":"address"},{"indexed":true,"internalType":"address","name":"newGovernance","type":"address"}],"name":"GovernanceTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":true,"internalType":"address","name":"liquidator","type":"address"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int128","name":"amountLiquidatedBC","type":"int128"},{"indexed":false,"internalType":"int128","name":"liquidationPrice","type":"int128"},{"indexed":false,"internalType":"int128","name":"newPositionSizeBC","type":"int128"},{"indexed":false,"internalType":"int128","name":"fFeeCC","type":"int128"},{"indexed":false,"internalType":"int128","name":"fPnlCC","type":"int128"}],"name":"Liquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"poolId","type":"uint8"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shareAmount","type":"uint256"}],"name":"LiquidityAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"id","type":"uint8"},{"indexed":false,"internalType":"address","name":"marginTokenAddress","type":"address"},{"indexed":false,"internalType":"address","name":"shareTokenAddress","type":"address"},{"indexed":false,"internalType":"uint16","name":"iTargetPoolSizeUpdateTime","type":"uint16"},{"indexed":false,"internalType":"int128","name":"fBrokerCollateralLotSize","type":"int128"}],"name":"LiquidityPoolCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"pauseOn","type":"bool"},{"indexed":false,"internalType":"uint8","name":"poolId","type":"uint8"}],"name":"LiquidityProvisionPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"poolId","type":"uint8"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shareAmount","type":"uint256"}],"name":"LiquidityRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"poolId","type":"uint8"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"shareAmount","type":"uint256"}],"name":"LiquidityWithdrawalInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousMaintainer","type":"address"},{"indexed":true,"internalType":"address","name":"newMaintainer","type":"address"}],"name":"MaintainerTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"poolId","type":"uint8"},{"indexed":false,"internalType":"uint24","name":"id","type":"uint24"},{"indexed":false,"internalType":"int128[7]","name":"baseParams","type":"int128[7]"},{"indexed":false,"internalType":"int128[5]","name":"underlyingRiskParams","type":"int128[5]"},{"indexed":false,"internalType":"int128[12]","name":"defaultFundRiskParams","type":"int128[12]"},{"indexed":false,"internalType":"uint256","name":"eCollateralCurrency","type":"uint256"}],"name":"PerpetualCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":true,"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"name":"PerpetualLimitOrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"_liqPoolID","type":"uint8"}],"name":"RunLiquidityPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"delay","type":"uint8"}],"name":"SetBlockDelay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32[]","name":"designations","type":"uint32[]"},{"indexed":false,"internalType":"uint16[]","name":"fees","type":"uint16[]"}],"name":"SetBrokerDesignations","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"tiers","type":"uint256[]"},{"indexed":false,"internalType":"uint16[]","name":"feesTbps","type":"uint16[]"}],"name":"SetBrokerTiers","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"tiers","type":"uint256[]"},{"indexed":false,"internalType":"uint16[]","name":"feesTbps","type":"uint16[]"}],"name":"SetBrokerVolumeTiers","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"}],"name":"SetClearedState","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"SetDelegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":false,"internalType":"int128","name":"fSettlementMarkPremiumRate","type":"int128"},{"indexed":false,"internalType":"int128","name":"fSettlementS2Price","type":"int128"},{"indexed":false,"internalType":"int128","name":"fSettlementS3Price","type":"int128"}],"name":"SetEmergencyState","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"}],"name":"SetNormalState","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":false,"internalType":"bytes4[2]","name":"baseQuoteS2","type":"bytes4[2]"},{"indexed":false,"internalType":"bytes4[2]","name":"baseQuoteS3","type":"bytes4[2]"}],"name":"SetOracles","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"int128","name":"value","type":"int128"}],"name":"SetParameter","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"int128","name":"value1","type":"int128"},{"indexed":false,"internalType":"int128","name":"value2","type":"int128"}],"name":"SetParameterPair","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":false,"internalType":"int128[7]","name":"baseParams","type":"int128[7]"}],"name":"SetPerpetualBaseParameters","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":false,"internalType":"int128[5]","name":"underlyingRiskParams","type":"int128[5]"},{"indexed":false,"internalType":"int128[12]","name":"defaultFundRiskParams","type":"int128[12]"}],"name":"SetPerpetualRiskParameters","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"poolId","type":"uint8"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"int128","name":"value","type":"int128"}],"name":"SetPoolParameter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"tiers","type":"uint256[]"},{"indexed":false,"internalType":"uint16[]","name":"feesTbps","type":"uint16[]"}],"name":"SetTraderTiers","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"tiers","type":"uint256[]"},{"indexed":false,"internalType":"uint16[]","name":"feesTbps","type":"uint16[]"}],"name":"SetTraderVolumeTiers","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"tokenAddr","type":"address"}],"name":"SetUtilityToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int256","name":"amount","type":"int256"}],"name":"Settle","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"}],"name":"SettleState","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"}],"name":"SettlementComplete","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int128","name":"amount","type":"int128"}],"name":"TokensDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int128","name":"amount","type":"int128"}],"name":"TokensWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"components":[{"internalType":"uint16","name":"leverageTDR","type":"uint16"},{"internalType":"uint16","name":"brokerFeeTbps","type":"uint16"},{"internalType":"uint24","name":"iPerpetualId","type":"uint24"},{"internalType":"address","name":"traderAddr","type":"address"},{"internalType":"uint32","name":"executionTimestamp","type":"uint32"},{"internalType":"address","name":"brokerAddr","type":"address"},{"internalType":"uint32","name":"submittedTimestamp","type":"uint32"},{"internalType":"uint32","name":"flags","type":"uint32"},{"internalType":"uint32","name":"iDeadline","type":"uint32"},{"internalType":"address","name":"executorAddr","type":"address"},{"internalType":"int128","name":"fAmount","type":"int128"},{"internalType":"int128","name":"fLimitPrice","type":"int128"},{"internalType":"int128","name":"fTriggerPrice","type":"int128"},{"internalType":"bytes","name":"brokerSignature","type":"bytes"}],"indexed":false,"internalType":"struct IPerpetualOrder.Order","name":"order","type":"tuple"},{"indexed":false,"internalType":"bytes32","name":"orderDigest","type":"bytes32"},{"indexed":false,"internalType":"int128","name":"newPositionSizeBC","type":"int128"},{"indexed":false,"internalType":"int128","name":"price","type":"int128"},{"indexed":false,"internalType":"int128","name":"fFeeCC","type":"int128"},{"indexed":false,"internalType":"int128","name":"fPnlCC","type":"int128"},{"indexed":false,"internalType":"int128","name":"fB2C","type":"int128"}],"name":"Trade","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"address","name":"oldOBFactory","type":"address"},{"indexed":false,"internalType":"address","name":"newOBFactory","type":"address"}],"name":"TransferAddressTo","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"poolId","type":"uint8"},{"indexed":false,"internalType":"uint32","name":"iLots","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"iNewBrokerLots","type":"uint32"}],"name":"UpdateBrokerAddedCash","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":false,"internalType":"int128","name":"fFundingRate","type":"int128"}],"name":"UpdateFundingRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int128","name":"fFundingPaymentCC","type":"int128"}],"name":"UpdateMarginAccount","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":false,"internalType":"int128","name":"fMidPricePremium","type":"int128"},{"indexed":false,"internalType":"int128","name":"fMarkPricePremium","type":"int128"},{"indexed":false,"internalType":"int128","name":"fSpotIndexPrice","type":"int128"}],"name":"UpdateMarkPrice","type":"event"},{"inputs":[{"internalType":"uint8","name":"_poolId","type":"uint8"},{"internalType":"uint256","name":"_tokenAmount","type":"uint256"}],"name":"addLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"address","name":"","type":"address"}],"name":"brokerVolumeEMA","outputs":[{"internalType":"int128","name":"fTradingVolumeEMAusd","type":"int128"},{"internalType":"uint64","name":"timestamp","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"brokerVolumeFeesTbps","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"brokerVolumeTiers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"_poolId","type":"uint8"},{"internalType":"address","name":"_lpAddr","type":"address"}],"name":"executeLiquidityWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_poolId","type":"uint8"}],"name":"getCollateralTokenAmountForPricing","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFunctionList","outputs":[{"internalType":"bytes4[]","name":"","type":"bytes4[]"},{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint8","name":"_poolId","type":"uint8"}],"name":"getShareTokenPriceD18","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"_poolId","type":"uint8"},{"internalType":"uint256","name":"_shareAmount","type":"uint256"}],"name":"getTokenAmountToReturn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"_poolId","type":"uint8"},{"internalType":"uint256","name":"_fromIdx","type":"uint256"},{"internalType":"uint256","name":"_numRequests","type":"uint256"}],"name":"getWithdrawRequests","outputs":[{"components":[{"internalType":"address","name":"lp","type":"address"},{"internalType":"uint256","name":"shareTokens","type":"uint256"},{"internalType":"uint64","name":"withdrawTimestamp","type":"uint64"}],"internalType":"struct PerpStorage.WithdrawRequest[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastBaseToUSDUpdateTs","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"}],"name":"liquidityProvisionIsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maintainer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"_poolId","type":"uint8"},{"internalType":"bool","name":"_pauseOn","type":"bool"}],"name":"pauseLiquidityProvision","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"","type":"uint24"}],"name":"perpBaseToUSDOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"","type":"uint24"}],"name":"perpToLastBaseToUSD","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"address","name":"","type":"address"}],"name":"traderVolumeEMA","outputs":[{"internalType":"int128","name":"fTradingVolumeEMAusd","type":"int128"},{"internalType":"uint64","name":"timestamp","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"traderVolumeFeesTbps","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"traderVolumeTiers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newGovernance","type":"address"}],"name":"transferGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newMaintainer","type":"address"}],"name":"transferMaintainer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_poolId","type":"uint8"},{"internalType":"uint256","name":"_shareAmount","type":"uint256"}],"name":"withdrawLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b506200001d336200003e565b62000028336200008e565b6001805460ff60a01b19168155600255620000e0565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f2f3ffaaaad93928855c8700645d1a3643e6ccfdd500efa9fda048a88f557cf019190a35050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f5f56bee8cffbe9a78652a74a60705edede02af10b0bbb888ca44b79a0d42ce8090600090a35050565b6147cb80620000f06000396000f3fe608060405234801561001057600080fd5b50600436106101985760003560e01c80636700c019116100e35780639ecab3f51161008c578063e733241011610066578063e7332410146104b0578063e7e21fde146104dd578063f0064f37146104f057600080fd5b80639ecab3f51461043b578063bffd952a1461048a578063d38bfff41461049d57600080fd5b806388b82528116100bd57806388b82528146103d45780638cd81c001461040a5780639850d32b1461041d57600080fd5b80636700c0191461038857806371fff802146103ae57806378bc212d146103c157600080fd5b806359a39709116101455780635ca8b9721161011f5780635ca8b972146102f05780635fdfecbe14610303578063642d7a941461037557600080fd5b806359a39709146102795780635aa6e6751461028e5780635c975abb146102cd57600080fd5b80633baf4f6f116101765780633baf4f6f1461021d5780633e6b8c0d1461025057806355f1c7d51461026657600080fd5b80631a8d52e11461019d5780632faee618146101c65780633a839d15146101e7575b600080fd5b6101b06101ab366004613f8e565b610503565b6040516101bd9190613fc1565b60405180910390f35b6101d96101d436600461403a565b6106d5565b6040519081526020016101bd565b61020a6101f5366004614055565b601960205260009081526040902054600f0b81565b604051600f9190910b81526020016101bd565b61024061022b36600461403a565b601f6020526000908152604090205460ff1681565b60405190151581526020016101bd565b6102586107ce565b6040516101bd92919061407a565b6101d96102743660046140e4565b610b66565b61028c61028736600461410b565b610b87565b005b60015473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101bd565b60015474010000000000000000000000000000000000000000900460ff16610240565b6101d96102fe3660046140e4565b610ce9565b610352610311366004614166565b601a602090815260009283526040808420909152908252902054600f81900b90700100000000000000000000000000000000900467ffffffffffffffff1682565b60408051600f9390930b835267ffffffffffffffff9091166020830152016101bd565b6101d9610383366004614199565b610cf9565b61039b6103963660046140e4565b610d2a565b60405161ffff90911681526020016101bd565b61020a6103bc36600461403a565b610d62565b61028c6103cf366004614199565b610d7d565b6102a86103e2366004614055565b60186020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b61028c610418366004614199565b6113d4565b60005473ffffffffffffffffffffffffffffffffffffffff166102a8565b610352610449366004614166565b601b602090815260009283526040808420909152908252902054600f81900b90700100000000000000000000000000000000900467ffffffffffffffff1682565b61028c6104983660046141c3565b6118e5565b61028c6104ab3660046141c3565b6119ef565b601c546104c49067ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016101bd565b61039b6104eb3660046140e4565b611af6565b61028c6104fe366004614166565b611b06565b60ff83166000908152601e602052604081206060919061052290612289565b905060008367ffffffffffffffff81111561053f5761053f6141de565b6040519080825280602002602001820160405280156105a857816020015b60408051606081018252600080825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90920191018161055d5790505b5090506000855b8351811080156105be57508582105b156106c7576000601d60008684815181106105db576105db61420d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff9081168352828201939093526040918201600090812060ff8e16825282528290208251606081018452815490941684526001810154918401919091526002015467ffffffffffffffff169082015285519091508590839081106106615761066161420d565b602090810291909101015173ffffffffffffffffffffffffffffffffffffffff1681528351819085908590811061069a5761069a61420d565b602002602001018190525082806106b09061426b565b9350505080806106bf9061426b565b9150506105af565b5090925050505b9392505050565b60ff81166000908152600560209081526040808320600281015482517f18160ddd0000000000000000000000000000000000000000000000000000000081529251919385936107889373ffffffffffffffffffffffffffffffffffffffff909316926318160ddd926004808401939192918290030181865afa15801561075f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078391906142a3565b61229f565b9050600081600f0b13156107bb5760038201546107b4906107ac90600f0b83612370565b600f0b61249e565b92506107c7565b670de0b6b3a764000092505b5050919050565b60606000806108116040518060400160405280601181526020017f50657270657475616c5472656173757279000000000000000000000000000000815250612516565b6040805160088082526101208201909252919250600091906020820161010080368337019050509050638cd81c0060e01b816000815181106108555761085561420d565b7fffffffff000000000000000000000000000000000000000000000000000000009092166020928302919091019091015280517f78bc212d0000000000000000000000000000000000000000000000000000000090829060019081106108bd576108bd61420d565b7fffffffff000000000000000000000000000000000000000000000000000000009092166020928302919091019091015280517ff0064f370000000000000000000000000000000000000000000000000000000090829060029081106109255761092561420d565b7fffffffff000000000000000000000000000000000000000000000000000000009092166020928302919091019091015280517f642d7a9400000000000000000000000000000000000000000000000000000000908290600390811061098d5761098d61420d565b7fffffffff000000000000000000000000000000000000000000000000000000009092166020928302919091019091015280517f71fff8020000000000000000000000000000000000000000000000000000000090829060049081106109f5576109f561420d565b7fffffffff000000000000000000000000000000000000000000000000000000009092166020928302919091019091015280517f59a39709000000000000000000000000000000000000000000000000000000009082906005908110610a5d57610a5d61420d565b7fffffffff000000000000000000000000000000000000000000000000000000009092166020928302919091019091015280517f2faee618000000000000000000000000000000000000000000000000000000009082906006908110610ac557610ac561420d565b7fffffffff000000000000000000000000000000000000000000000000000000009092166020928302919091019091015280517f1a8d52e1000000000000000000000000000000000000000000000000000000009082906007908110610b2d57610b2d61420d565b7fffffffff0000000000000000000000000000000000000000000000000000000090921660209283029190910190910152939092509050565b60148181548110610b7657600080fd5b600091825260209091200154905081565b33610ba760005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614610c29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6f6e6c79206d61696e7461696e6572000000000000000000000000000000000060448201526064015b60405180910390fd5b8015610c6f5760ff82166000908152601f6020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610ca8565b60ff82166000908152601f6020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555b60408051821515815260ff841660208201527fdd50900dcfecd1d3006ae75c734e1ea17e5f8a020b58d16fa488b8efb29257d3910160405180910390a15050565b60158181548110610b7657600080fd5b60ff82166000908152600560205260408120610d206107ac82610d1b8661229f565b612535565b9150505b92915050565b60168181548110610d3a57600080fd5b9060005260206000209060109182820401919006600202915054906101000a900461ffff1681565b60ff811660009081526005602052604081206106ce816125ed565b610d856126ba565b610d8d61272b565b60ff8083166000908152601f60205260409020541615610e09576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f4c502070617573656400000000000000000000000000000000000000000000006044820152606401610c20565b336000818152601d6020908152604080832060ff8716845290915290206001015415610e91576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f72656d6f76616c20696e207175657565000000000000000000000000000000006044820152606401610c20565b6000610e9c846127b2565b9050306040517fec7232ba00000000000000000000000000000000000000000000000000000000815262ffffff831660048201526000602482015273ffffffffffffffffffffffffffffffffffffffff919091169063ec7232ba90604401600060405180830381600087803b158015610f1457600080fd5b505af1158015610f28573d6000803e3d6000fd5b50505050610f36848461291c565b60ff84166000908152600560205260409020610f5181612a08565b610f5a81612ca6565b60028101546040517f80860c0300000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff9091169081906380860c0390602401600060405180830381600087803b158015610fc957600080fd5b505af1158015610fdd573d6000803e3d6000fd5b505050600583015460009150610ff7908490600f0b612df9565b905060006110146107ac600f84900b678000000000000000612e9c565b905080871015611022578096505b61102e82600f0b61249e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff88811660048301528516906370a0823190602401602060405180830381865afa15801561109a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110be91906142a3565b108061115857506040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87811660048301528416906370a0823190602401602060405180830381865afa158015611131573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115591906142a3565b87115b156111f0576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87811660048301528416906370a0823190602401602060405180830381865afa1580156111c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ed91906142a3565b96505b6000871161125a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f6e6f7468696e6720746f207769746864726177000000000000000000000000006044820152606401610c20565b6112648488612f5d565b73ffffffffffffffffffffffffffffffffffffffff86166000908152601d6020908152604080832060ff8c1684528252808320600181018b905560020180547fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000164267ffffffffffffffff16179055601e90915290206112e4908761302a565b508573ffffffffffffffffffffffffffffffffffffffff168860ff167f0b2f30d3b41486fa81fea9500c5df3129823d0861877d546ef5039c06317037a8960405161133191815260200190565b60405180910390a3306040517f16240e3f00000000000000000000000000000000000000000000000000000000815262ffffff8716600482015273ffffffffffffffffffffffffffffffffffffffff91909116906316240e3f90602401600060405180830381600087803b1580156113a857600080fd5b505af11580156113bc573d6000803e3d6000fd5b505050505050505050506113d06001600255565b5050565b6113dc6126ba565b6113e461272b565b60ff8083166000908152601f60205260409020541615611460576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f4c502070617573656400000000000000000000000000000000000000000000006044820152606401610c20565b600061146b836127b2565b9050306040517fec7232ba00000000000000000000000000000000000000000000000000000000815262ffffff831660048201526000602482015273ffffffffffffffffffffffffffffffffffffffff919091169063ec7232ba90604401600060405180830381600087803b1580156114e357600080fd5b505af11580156114f7573d6000803e3d6000fd5b50505050611505838361291c565b60ff8084166000908152600560205260408120805490926115349186916701000000000000009091041661304c565b6005830154909150600f90810b9082900b12156115ad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f616d7420746f6f20736d616c6c000000000000000000000000000000000000006044820152606401610c20565b6115b68261311f565b6115bf82612ca6565b6115ca823383613278565b60006115d68383612df9565b600284015490915073ffffffffffffffffffffffffffffffffffffffff166340c10f1933611607600f85900b61249e565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff90921660048301526024820152604401600060405180830381600087803b15801561167257600080fd5b505af1158015611686573d6000803e3d6000fd5b5050506002840154604080517f18160ddd000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff90921692506318160ddd9160048083019260209291908290030181865afa1580156116fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061171f91906142a3565b6006840180546fffffffffffffffffffffffffffffffff92831670010000000000000000000000000000000002921691909117905561175b3090565b83546040517edb5b220000000000000000000000000000000000000000000000000000000081526201000090910460ff166004820152600f84900b602482015273ffffffffffffffffffffffffffffffffffffffff919091169062db5b2290604401600060405180830381600087803b1580156117d757600080fd5b505af11580156117eb573d6000803e3d6000fd5b50505050611804836117ff83600f0b61249e565b6132df565b3360ff87167f705802d30f8755af1254af5bc1228e9adf8132d61c2f0e82496341f611616b8a87611838600f86900b61249e565b6040805192835260208301919091520160405180910390a3306040517f16240e3f00000000000000000000000000000000000000000000000000000000815262ffffff8616600482015273ffffffffffffffffffffffffffffffffffffffff91909116906316240e3f90602401600060405180830381600087803b1580156118bf57600080fd5b505af11580156118d3573d6000803e3d6000fd5b50505050505050506113d06001600255565b60015473ffffffffffffffffffffffffffffffffffffffff163314611966576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6f6e6c7920676f7665726e616e636500000000000000000000000000000000006044820152606401610c20565b73ffffffffffffffffffffffffffffffffffffffff81166119e3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f7a65726f206164647265737300000000000000000000000000000000000000006044820152606401610c20565b6119ec81613364565b50565b60015473ffffffffffffffffffffffffffffffffffffffff163314611a70576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6f6e6c7920676f7665726e616e636500000000000000000000000000000000006044820152606401610c20565b73ffffffffffffffffffffffffffffffffffffffff8116611aed576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f7a65726f206164647265737300000000000000000000000000000000000000006044820152606401610c20565b6119ec816133d9565b60178181548110610d3a57600080fd5b611b0e6126ba565b611b1661272b565b60ff8083166000908152601f60205260409020541615611b92576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f4c502070617573656400000000000000000000000000000000000000000000006044820152606401610c20565b73ffffffffffffffffffffffffffffffffffffffff81166000908152601d6020908152604080832060ff86168452909152902060010154611c2f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f696e697420776974686472776c000000000000000000000000000000000000006044820152606401610c20565b6201518073ffffffffffffffffffffffffffffffffffffffff82166000908152601d6020908152604080832060ff87168452909152902060020154611c7e919067ffffffffffffffff166142bc565b67ffffffffffffffff16421015611cf1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f746f6f206561726c7900000000000000000000000000000000000000000000006044820152606401610c20565b6000611cfc836127b2565b9050306040517fec7232ba00000000000000000000000000000000000000000000000000000000815262ffffff831660048201526000602482015273ffffffffffffffffffffffffffffffffffffffff919091169063ec7232ba90604401600060405180830381600087803b158015611d7457600080fd5b505af1158015611d88573d6000803e3d6000fd5b5050505060ff83166000908152600560205260409020611da781612a08565b73ffffffffffffffffffffffffffffffffffffffff83166000908152601d6020908152604080832060ff8816845290915281206001015490611dec83610d1b8461229f565b60028401546040517f9dc29fac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff888116600483015260248201869052929350911690639dc29fac90604401600060405180830381600087803b158015611e6457600080fd5b505af1158015611e78573d6000803e3d6000fd5b5050506002840154604080517f18160ddd000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff90921692506318160ddd9160048083019260209291908290030181865afa158015611eed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f1191906142a3565b6006840180546fffffffffffffffffffffffffffffffff928316700100000000000000000000000000000000029216919091179055611f4d3090565b83546040517f733e88160000000000000000000000000000000000000000000000000000000081526201000090910460ff166004820152600f83900b602482015273ffffffffffffffffffffffffffffffffffffffff919091169063733e881690604401600060405180830381600087803b158015611fcb57600080fd5b505af1158015611fdf573d6000803e3d6000fd5b5050503373ffffffffffffffffffffffffffffffffffffffff8716148015915061206b57506120126201518060026142dd565b73ffffffffffffffffffffffffffffffffffffffff86166000908152601d6020908152604080832060ff8b16845290915290206002015461205d919067ffffffffffffffff166142bc565b67ffffffffffffffff164210155b156120c6576000612088600f83900b67028f5c28f5c28f5c612e9c565b6005850154909150600f90810b9082900b13156120a957506005830154600f0b5b6120b7600f83900b82613450565b91506120c484338361350d565b505b6120d183868361350d565b60ff86166000908152601e602052604090206120ed9086613580565b5073ffffffffffffffffffffffffffffffffffffffff85166000908152601d6020908152604080832060ff808b1685529252822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001681556001810183905560020180547fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000169055845461219491600f85900b916701000000000000009004166135a2565b90508573ffffffffffffffffffffffffffffffffffffffff168760ff167f03b900fa70bdd68edd081a8e571176f5d5326f90697ec727f08ddd67b16981f383866040516121eb929190918252602082015260400190565b60405180910390a3306040517f16240e3f00000000000000000000000000000000000000000000000000000000815262ffffff8716600482015273ffffffffffffffffffffffffffffffffffffffff91909116906316240e3f90602401600060405180830381600087803b15801561226257600080fd5b505af1158015612276573d6000803e3d6000fd5b5050505050505050506113d06001600255565b6060610d2482600061229a8561363e565b613648565b600080670de0b6b3a76400006122be6801000000000000000085614309565b6122c89190614384565b90507fffffffffffffffffffffffffffffffff80000000000000000000000000000000831280159061230a57506f7fffffffffffffffffffffffffffffff8313155b610d24576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f726573756c74206f7574206f662072616e6765000000000000000000000000006044820152606401610c20565b600081600f0b6000036123df576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f4142444b2e6469762d31000000000000000000000000000000000000000000006044820152606401610c20565b60006123f6600f84900b604086901b60170b614384565b90507fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061243857506f7fffffffffffffffffffffffffffffff8113155b6106ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f4142444b2e6469762d32000000000000000000000000000000000000000000006044820152606401610c20565b60008082600f0b121561250d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f6e656761746976652076616c75650000000000000000000000000000000000006044820152606401610c20565b610d24826137c1565b80516000908290820361252c5750600092915050565b50506020015190565b6000806125aa8460020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561075f573d6000803e3d6000fd5b6003850154909150600090600f0b811215610d205760038501546125e49083906125db90600f88810b91900b612e9c565b600f0b90612370565b95945050505050565b600681015460009070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16810361262957506000919050565b60038201546006830154600f9190910b9060009070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1661266c856137ed565b612690906fffffffffffffffffffffffffffffffff16670de0b6b3a76400006143ec565b61269a9190614403565b90506126b2826126a98361229f565b600f0b90612e9c565b949350505050565b6002805403612725576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610c20565b60028055565b60015474010000000000000000000000000000000000000000900460ff16156127b0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610c20565b565b6000808260ff1611612820576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f706f6f6c206e6f7420666f756e640000000000000000000000000000000000006044820152606401610c20565b60ff808316600090815260056020526040902080549091610100909104166128a4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6e6f207065727020696e20706f6f6c00000000000000000000000000000000006044820152606401610c20565b80546000906128bb90610100900460ff1642614417565b825462010000900460ff166000908152600e6020526040902080549192509061ffff83169081106128ee576128ee61420d565b90600052602060002090600a91828204019190066003029054906101000a900462ffffff1692505050919050565b60008260ff16118015612938575060035460ff90811690831611155b61299e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f706f6f6c20696e646578206f7574206f662072616e67650000000000000000006044820152606401610c20565b600081116113d0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f696e76616c696420616d6f756e740000000000000000000000000000000000006044820152606401610c20565b805460ff16612a73576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f6e6f742072756e6e696e670000000000000000000000000000000000000000006044820152606401610c20565b805462010000900460ff166000908152600e602052604081205490805b82811015612c3957835462010000900460ff166000908152600e60205260408120805483908110612ac357612ac361420d565b60009182526020808320600a80840490910154895460ff62010000909104168552600f8352604080862062ffffff60039490960684026101000a9092049490941680865292529190922091925081547801000000000000000000000000000000000000000000000000900460ff166005811115612b4257612b4261442b565b03612ba9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6e6f20776974686472617720696e20656d657267656e637900000000000000006044820152606401610c20565b600281547801000000000000000000000000000000000000000000000000900460ff166005811115612bdd57612bdd61442b565b1480612c1a5750600581547801000000000000000000000000000000000000000000000000900460ff166005811115612c1857612c1861442b565b145b15612c2457600193505b50508080612c319061426b565b915050612a90565b5080612ca1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f6e6f206163746976652070657270657475616c000000000000000000000000006044820152606401610c20565b505050565b8054610100900460ff166000600a8211612cc1576000612cd6565b612ccc60098361445a565b612cd69043614417565b90506000600a8311612ce85782612cf3565b612cf382600a61446d565b9050815b81811015612df257845462010000900460ff166000908152600e60205260408120805483908110612d2a57612d2a61420d565b90600052602060002090600a91828204019190066003029054906101000a900462ffffff169050612d583090565b6040517fa25d6a2800000000000000000000000000000000000000000000000000000000815262ffffff8316600482015273ffffffffffffffffffffffffffffffffffffffff919091169063a25d6a2890602401600060405180830381600087803b158015612dc657600080fd5b505af1158015612dda573d6000803e3d6000fd5b50505050508080612dea9061426b565b915050612cf7565b5050505050565b600080612e6e8460020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561075f573d6000803e3d6000fd5b9050600081600f0b600003612e84575082610d20565b60038501546125e490600f90810b906125db9087900b855b600080604083600f0b85600f0b612eb39190614309565b901d90507fffffffffffffffffffffffffffffffff800000000000000000000000000000008112801590612ef757506f7fffffffffffffffffffffffffffffff8113155b6106ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f4142444b2e6d756c0000000000000000000000000000000000000000000000006044820152606401610c20565b612f66826137ed565b6005830180546fffffffffffffffffffffffffffffffff9283167001000000000000000000000000000000000290831617905560018301805467ffffffffffffffff42167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009091161790556006830154612fe291839116614480565b60069290920180547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff9093169290921790915550565b60006106ce8373ffffffffffffffffffffffffffffffffffffffff84166139d6565b60008061305a83600a6145c9565b61306d6801000000000000000086614309565b6130779190614384565b90507fffffffffffffffffffffffffffffffff8000000000000000000000000000000084128015906130b957506f7fffffffffffffffffffffffffffffff8413155b6106ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f726573756c74206f7574206f662072616e6765000000000000000000000000006044820152606401610c20565b805460ff1661318a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f6e6f742072756e6e696e670000000000000000000000000000000000000000006044820152606401610c20565b805462010000900460ff166000908152600e602052604081205490805b82811015612c3957835462010000900460ff166000908152600e602052604081208054839081106131da576131da61420d565b60009182526020808320600a80840490910154895462010000900460ff168552600f83526040808620929094066003026101000a900462ffffff1680855291529120909150600281547801000000000000000000000000000000000000000000000000900460ff1660058111156132535761325361442b565b0361326357600193505050612c39565b505080806132709061426b565b9150506131a7565b600081600f0b1361328857505050565b82546000906132aa90600f84900b90670100000000000000900460ff166135a2565b84549091506a0100000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16612df281853085613a25565b6132e8826137ed565b6005830180546fffffffffffffffffffffffffffffffff9283167001000000000000000000000000000000000290831617905560018301805467ffffffffffffffff42167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009091161790556006830154612fe2918391166145d8565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f2f3ffaaaad93928855c8700645d1a3643e6ccfdd500efa9fda048a88f557cf019190a35050565b6001805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f5f56bee8cffbe9a78652a74a60705edede02af10b0bbb888ca44b79a0d42ce8090600090a35050565b60008082600f0b84600f0b6134659190614601565b90507fffffffffffffffffffffffffffffffff8000000000000000000000000000000081128015906134a757506f7fffffffffffffffffffffffffffffff8113155b6106ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f4142444b2e7375620000000000000000000000000000000000000000000000006044820152606401610c20565b600081600f0b1361351d57505050565b825460009061353f90600f84900b90670100000000000000900460ff166135a2565b90508060000361354f5750505050565b83546a0100000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16612df2818584613b07565b60006106ce8373ffffffffffffffffffffffffffffffffffffffff8416613b5d565b60008083600f0b1215613611576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f6e656761746976652076616c75650000000000000000000000000000000000006044820152606401610c20565b6801000000000000000061362683600a6145c9565b84600f0b6136349190614309565b6106ce9190614384565b6000610d24825490565b60606000613656838561446d565b9050838110156136c2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f6164646974696f6e206f766572666c6f770000000000000000000000000000006044820152606401610c20565b60006136cd8661363e565b90508181106136dc57816136de565b805b91508115806136ed5750818510155b156136f95750506106ce565b613703858361445a565b67ffffffffffffffff81111561371b5761371b6141de565b604051908082528060200260200182016040528015613744578160200160208202803683370190505b50925060005b613754868461445a565b8110156137b75761376e87613769888461446d565b613c57565b8482815181106137805761378061420d565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910190910152806137af8161426b565b91505061374a565b5050509392505050565b6000680100000000000000006137e3670de0b6b3a7640000600f85900b614309565b610d249190614384565b60004262015180600184015461380d919067ffffffffffffffff166142bc565b67ffffffffffffffff168167ffffffffffffffff1610613842575050600601546fffffffffffffffffffffffffffffffff1690565b60006201518060018501546138619067ffffffffffffffff1684614621565b61386c9060646142dd565b6138769190614642565b6005850154600686015467ffffffffffffffff9290921692506fffffffffffffffffffffffffffffffff700100000000000000000000000000000000909104811691161161394d57600684015460058501546064916138ff916fffffffffffffffffffffffffffffffff9182169170010000000000000000000000000000000090910416614480565b6139099083614669565b6139139190614695565b6005850154613948919070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16614480565b6126b2565b6005840154600685015460649161398d916fffffffffffffffffffffffffffffffff70010000000000000000000000000000000090920482169116614480565b6139979083614669565b6139a19190614695565b60058501546126b2919070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff166145d8565b6000818152600183016020526040812054613a1d57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610d24565b506000610d24565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052613b019085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613c63565b50505050565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052612ca19084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401613a7f565b60008181526001830160205260408120548015613c46576000613b8160018361445a565b8554909150600090613b959060019061445a565b90506000866000018281548110613bae57613bae61420d565b9060005260206000200154905080876000018481548110613bd157613bd161420d565b600091825260209091200155613be883600161446d565b60008281526001890160205260409020558654879080613c0a57613c0a6146b8565b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610d24565b6000915050610d24565b5092915050565b60006106ce8383613d72565b6000613cc5826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16613e079092919063ffffffff16565b9050805160001480613ce6575080806020019051810190613ce691906146e7565b612ca1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610c20565b81546000908210613ddf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f456e756d657261626c655365743a20696478206f7574206f6620626f756e64736044820152606401610c20565b826000018281548110613df457613df461420d565b9060005260206000200154905092915050565b60606126b28484600085856000808673ffffffffffffffffffffffffffffffffffffffff168587604051613e3b9190614728565b60006040518083038185875af1925050503d8060008114613e78576040519150601f19603f3d011682016040523d82523d6000602084013e613e7d565b606091505b5091509150613e8e87838387613e99565b979650505050505050565b60608315613f2f578251600003613f285773ffffffffffffffffffffffffffffffffffffffff85163b613f28576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610c20565b50816126b2565b6126b28383815115613f445781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c209190614744565b803560ff81168114613f8957600080fd5b919050565b600080600060608486031215613fa357600080fd5b613fac84613f78565b95602085013595506040909401359392505050565b602080825282518282018190526000919060409081850190868401855b8281101561402d578151805173ffffffffffffffffffffffffffffffffffffffff168552868101518786015285015167ffffffffffffffff168585015260609093019290850190600101613fde565b5091979650505050505050565b60006020828403121561404c57600080fd5b6106ce82613f78565b60006020828403121561406757600080fd5b813562ffffff811681146106ce57600080fd5b604080825283519082018190526000906020906060840190828701845b828110156140d55781517fffffffff000000000000000000000000000000000000000000000000000000001684529284019290840190600101614097565b50505092019290925292915050565b6000602082840312156140f657600080fd5b5035919050565b80151581146119ec57600080fd5b6000806040838503121561411e57600080fd5b61412783613f78565b91506020830135614137816140fd565b809150509250929050565b803573ffffffffffffffffffffffffffffffffffffffff81168114613f8957600080fd5b6000806040838503121561417957600080fd5b61418283613f78565b915061419060208401614142565b90509250929050565b600080604083850312156141ac57600080fd5b6141b583613f78565b946020939093013593505050565b6000602082840312156141d557600080fd5b6106ce82614142565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361429c5761429c61423c565b5060010190565b6000602082840312156142b557600080fd5b5051919050565b67ffffffffffffffff818116838216019080821115613c5057613c5061423c565b67ffffffffffffffff8181168382160280821691908281146143015761430161423c565b505092915050565b808202600082127f8000000000000000000000000000000000000000000000000000000000000000841416156143415761434161423c565b8181058314821517610d2457610d2461423c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008261439357614393614355565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f8000000000000000000000000000000000000000000000000000000000000000831416156143e7576143e761423c565b500590565b8082028115828204841417610d2457610d2461423c565b60008261441257614412614355565b500490565b60008261442657614426614355565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b81810381811115610d2457610d2461423c565b80820180821115610d2457610d2461423c565b6fffffffffffffffffffffffffffffffff828116828216039080821115613c5057613c5061423c565b600181815b8085111561450257817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156144e8576144e861423c565b808516156144f557918102915b93841c93908002906144ae565b509250929050565b60008261451957506001610d24565b8161452657506000610d24565b816001811461453c576002811461454657614562565b6001915050610d24565b60ff8411156145575761455761423c565b50506001821b610d24565b5060208310610133831016604e8410600b8410161715614585575081810a610d24565b61458f83836144a9565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156145c1576145c161423c565b029392505050565b60006106ce60ff84168361450a565b6fffffffffffffffffffffffffffffffff818116838216019080821115613c5057613c5061423c565b8181036000831280158383131683831282161715613c5057613c5061423c565b67ffffffffffffffff828116828216039080821115613c5057613c5061423c565b600067ffffffffffffffff8084168061465d5761465d614355565b92169190910492915050565b6fffffffffffffffffffffffffffffffff8181168382160280821691908281146143015761430161423c565b60006fffffffffffffffffffffffffffffffff8084168061465d5761465d614355565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6000602082840312156146f957600080fd5b81516106ce816140fd565b60005b8381101561471f578181015183820152602001614707565b50506000910152565b6000825161473a818460208701614704565b9190910192915050565b6020815260008251806020840152614763816040850160208701614704565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fea2646970667358221220f14a06595827a2da4c501ba15230324d853e7d13751ff4ab0fa7b67dd45401e964736f6c63430008150033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101985760003560e01c80636700c019116100e35780639ecab3f51161008c578063e733241011610066578063e7332410146104b0578063e7e21fde146104dd578063f0064f37146104f057600080fd5b80639ecab3f51461043b578063bffd952a1461048a578063d38bfff41461049d57600080fd5b806388b82528116100bd57806388b82528146103d45780638cd81c001461040a5780639850d32b1461041d57600080fd5b80636700c0191461038857806371fff802146103ae57806378bc212d146103c157600080fd5b806359a39709116101455780635ca8b9721161011f5780635ca8b972146102f05780635fdfecbe14610303578063642d7a941461037557600080fd5b806359a39709146102795780635aa6e6751461028e5780635c975abb146102cd57600080fd5b80633baf4f6f116101765780633baf4f6f1461021d5780633e6b8c0d1461025057806355f1c7d51461026657600080fd5b80631a8d52e11461019d5780632faee618146101c65780633a839d15146101e7575b600080fd5b6101b06101ab366004613f8e565b610503565b6040516101bd9190613fc1565b60405180910390f35b6101d96101d436600461403a565b6106d5565b6040519081526020016101bd565b61020a6101f5366004614055565b601960205260009081526040902054600f0b81565b604051600f9190910b81526020016101bd565b61024061022b36600461403a565b601f6020526000908152604090205460ff1681565b60405190151581526020016101bd565b6102586107ce565b6040516101bd92919061407a565b6101d96102743660046140e4565b610b66565b61028c61028736600461410b565b610b87565b005b60015473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101bd565b60015474010000000000000000000000000000000000000000900460ff16610240565b6101d96102fe3660046140e4565b610ce9565b610352610311366004614166565b601a602090815260009283526040808420909152908252902054600f81900b90700100000000000000000000000000000000900467ffffffffffffffff1682565b60408051600f9390930b835267ffffffffffffffff9091166020830152016101bd565b6101d9610383366004614199565b610cf9565b61039b6103963660046140e4565b610d2a565b60405161ffff90911681526020016101bd565b61020a6103bc36600461403a565b610d62565b61028c6103cf366004614199565b610d7d565b6102a86103e2366004614055565b60186020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b61028c610418366004614199565b6113d4565b60005473ffffffffffffffffffffffffffffffffffffffff166102a8565b610352610449366004614166565b601b602090815260009283526040808420909152908252902054600f81900b90700100000000000000000000000000000000900467ffffffffffffffff1682565b61028c6104983660046141c3565b6118e5565b61028c6104ab3660046141c3565b6119ef565b601c546104c49067ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016101bd565b61039b6104eb3660046140e4565b611af6565b61028c6104fe366004614166565b611b06565b60ff83166000908152601e602052604081206060919061052290612289565b905060008367ffffffffffffffff81111561053f5761053f6141de565b6040519080825280602002602001820160405280156105a857816020015b60408051606081018252600080825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90920191018161055d5790505b5090506000855b8351811080156105be57508582105b156106c7576000601d60008684815181106105db576105db61420d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff9081168352828201939093526040918201600090812060ff8e16825282528290208251606081018452815490941684526001810154918401919091526002015467ffffffffffffffff169082015285519091508590839081106106615761066161420d565b602090810291909101015173ffffffffffffffffffffffffffffffffffffffff1681528351819085908590811061069a5761069a61420d565b602002602001018190525082806106b09061426b565b9350505080806106bf9061426b565b9150506105af565b5090925050505b9392505050565b60ff81166000908152600560209081526040808320600281015482517f18160ddd0000000000000000000000000000000000000000000000000000000081529251919385936107889373ffffffffffffffffffffffffffffffffffffffff909316926318160ddd926004808401939192918290030181865afa15801561075f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078391906142a3565b61229f565b9050600081600f0b13156107bb5760038201546107b4906107ac90600f0b83612370565b600f0b61249e565b92506107c7565b670de0b6b3a764000092505b5050919050565b60606000806108116040518060400160405280601181526020017f50657270657475616c5472656173757279000000000000000000000000000000815250612516565b6040805160088082526101208201909252919250600091906020820161010080368337019050509050638cd81c0060e01b816000815181106108555761085561420d565b7fffffffff000000000000000000000000000000000000000000000000000000009092166020928302919091019091015280517f78bc212d0000000000000000000000000000000000000000000000000000000090829060019081106108bd576108bd61420d565b7fffffffff000000000000000000000000000000000000000000000000000000009092166020928302919091019091015280517ff0064f370000000000000000000000000000000000000000000000000000000090829060029081106109255761092561420d565b7fffffffff000000000000000000000000000000000000000000000000000000009092166020928302919091019091015280517f642d7a9400000000000000000000000000000000000000000000000000000000908290600390811061098d5761098d61420d565b7fffffffff000000000000000000000000000000000000000000000000000000009092166020928302919091019091015280517f71fff8020000000000000000000000000000000000000000000000000000000090829060049081106109f5576109f561420d565b7fffffffff000000000000000000000000000000000000000000000000000000009092166020928302919091019091015280517f59a39709000000000000000000000000000000000000000000000000000000009082906005908110610a5d57610a5d61420d565b7fffffffff000000000000000000000000000000000000000000000000000000009092166020928302919091019091015280517f2faee618000000000000000000000000000000000000000000000000000000009082906006908110610ac557610ac561420d565b7fffffffff000000000000000000000000000000000000000000000000000000009092166020928302919091019091015280517f1a8d52e1000000000000000000000000000000000000000000000000000000009082906007908110610b2d57610b2d61420d565b7fffffffff0000000000000000000000000000000000000000000000000000000090921660209283029190910190910152939092509050565b60148181548110610b7657600080fd5b600091825260209091200154905081565b33610ba760005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1614610c29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6f6e6c79206d61696e7461696e6572000000000000000000000000000000000060448201526064015b60405180910390fd5b8015610c6f5760ff82166000908152601f6020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610ca8565b60ff82166000908152601f6020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555b60408051821515815260ff841660208201527fdd50900dcfecd1d3006ae75c734e1ea17e5f8a020b58d16fa488b8efb29257d3910160405180910390a15050565b60158181548110610b7657600080fd5b60ff82166000908152600560205260408120610d206107ac82610d1b8661229f565b612535565b9150505b92915050565b60168181548110610d3a57600080fd5b9060005260206000209060109182820401919006600202915054906101000a900461ffff1681565b60ff811660009081526005602052604081206106ce816125ed565b610d856126ba565b610d8d61272b565b60ff8083166000908152601f60205260409020541615610e09576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f4c502070617573656400000000000000000000000000000000000000000000006044820152606401610c20565b336000818152601d6020908152604080832060ff8716845290915290206001015415610e91576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f72656d6f76616c20696e207175657565000000000000000000000000000000006044820152606401610c20565b6000610e9c846127b2565b9050306040517fec7232ba00000000000000000000000000000000000000000000000000000000815262ffffff831660048201526000602482015273ffffffffffffffffffffffffffffffffffffffff919091169063ec7232ba90604401600060405180830381600087803b158015610f1457600080fd5b505af1158015610f28573d6000803e3d6000fd5b50505050610f36848461291c565b60ff84166000908152600560205260409020610f5181612a08565b610f5a81612ca6565b60028101546040517f80860c0300000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff9091169081906380860c0390602401600060405180830381600087803b158015610fc957600080fd5b505af1158015610fdd573d6000803e3d6000fd5b505050600583015460009150610ff7908490600f0b612df9565b905060006110146107ac600f84900b678000000000000000612e9c565b905080871015611022578096505b61102e82600f0b61249e565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff88811660048301528516906370a0823190602401602060405180830381865afa15801561109a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110be91906142a3565b108061115857506040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87811660048301528416906370a0823190602401602060405180830381865afa158015611131573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115591906142a3565b87115b156111f0576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87811660048301528416906370a0823190602401602060405180830381865afa1580156111c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ed91906142a3565b96505b6000871161125a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f6e6f7468696e6720746f207769746864726177000000000000000000000000006044820152606401610c20565b6112648488612f5d565b73ffffffffffffffffffffffffffffffffffffffff86166000908152601d6020908152604080832060ff8c1684528252808320600181018b905560020180547fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000164267ffffffffffffffff16179055601e90915290206112e4908761302a565b508573ffffffffffffffffffffffffffffffffffffffff168860ff167f0b2f30d3b41486fa81fea9500c5df3129823d0861877d546ef5039c06317037a8960405161133191815260200190565b60405180910390a3306040517f16240e3f00000000000000000000000000000000000000000000000000000000815262ffffff8716600482015273ffffffffffffffffffffffffffffffffffffffff91909116906316240e3f90602401600060405180830381600087803b1580156113a857600080fd5b505af11580156113bc573d6000803e3d6000fd5b505050505050505050506113d06001600255565b5050565b6113dc6126ba565b6113e461272b565b60ff8083166000908152601f60205260409020541615611460576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f4c502070617573656400000000000000000000000000000000000000000000006044820152606401610c20565b600061146b836127b2565b9050306040517fec7232ba00000000000000000000000000000000000000000000000000000000815262ffffff831660048201526000602482015273ffffffffffffffffffffffffffffffffffffffff919091169063ec7232ba90604401600060405180830381600087803b1580156114e357600080fd5b505af11580156114f7573d6000803e3d6000fd5b50505050611505838361291c565b60ff8084166000908152600560205260408120805490926115349186916701000000000000009091041661304c565b6005830154909150600f90810b9082900b12156115ad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f616d7420746f6f20736d616c6c000000000000000000000000000000000000006044820152606401610c20565b6115b68261311f565b6115bf82612ca6565b6115ca823383613278565b60006115d68383612df9565b600284015490915073ffffffffffffffffffffffffffffffffffffffff166340c10f1933611607600f85900b61249e565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff90921660048301526024820152604401600060405180830381600087803b15801561167257600080fd5b505af1158015611686573d6000803e3d6000fd5b5050506002840154604080517f18160ddd000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff90921692506318160ddd9160048083019260209291908290030181865afa1580156116fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061171f91906142a3565b6006840180546fffffffffffffffffffffffffffffffff92831670010000000000000000000000000000000002921691909117905561175b3090565b83546040517edb5b220000000000000000000000000000000000000000000000000000000081526201000090910460ff166004820152600f84900b602482015273ffffffffffffffffffffffffffffffffffffffff919091169062db5b2290604401600060405180830381600087803b1580156117d757600080fd5b505af11580156117eb573d6000803e3d6000fd5b50505050611804836117ff83600f0b61249e565b6132df565b3360ff87167f705802d30f8755af1254af5bc1228e9adf8132d61c2f0e82496341f611616b8a87611838600f86900b61249e565b6040805192835260208301919091520160405180910390a3306040517f16240e3f00000000000000000000000000000000000000000000000000000000815262ffffff8616600482015273ffffffffffffffffffffffffffffffffffffffff91909116906316240e3f90602401600060405180830381600087803b1580156118bf57600080fd5b505af11580156118d3573d6000803e3d6000fd5b50505050505050506113d06001600255565b60015473ffffffffffffffffffffffffffffffffffffffff163314611966576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6f6e6c7920676f7665726e616e636500000000000000000000000000000000006044820152606401610c20565b73ffffffffffffffffffffffffffffffffffffffff81166119e3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f7a65726f206164647265737300000000000000000000000000000000000000006044820152606401610c20565b6119ec81613364565b50565b60015473ffffffffffffffffffffffffffffffffffffffff163314611a70576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6f6e6c7920676f7665726e616e636500000000000000000000000000000000006044820152606401610c20565b73ffffffffffffffffffffffffffffffffffffffff8116611aed576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f7a65726f206164647265737300000000000000000000000000000000000000006044820152606401610c20565b6119ec816133d9565b60178181548110610d3a57600080fd5b611b0e6126ba565b611b1661272b565b60ff8083166000908152601f60205260409020541615611b92576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f4c502070617573656400000000000000000000000000000000000000000000006044820152606401610c20565b73ffffffffffffffffffffffffffffffffffffffff81166000908152601d6020908152604080832060ff86168452909152902060010154611c2f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f696e697420776974686472776c000000000000000000000000000000000000006044820152606401610c20565b6201518073ffffffffffffffffffffffffffffffffffffffff82166000908152601d6020908152604080832060ff87168452909152902060020154611c7e919067ffffffffffffffff166142bc565b67ffffffffffffffff16421015611cf1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f746f6f206561726c7900000000000000000000000000000000000000000000006044820152606401610c20565b6000611cfc836127b2565b9050306040517fec7232ba00000000000000000000000000000000000000000000000000000000815262ffffff831660048201526000602482015273ffffffffffffffffffffffffffffffffffffffff919091169063ec7232ba90604401600060405180830381600087803b158015611d7457600080fd5b505af1158015611d88573d6000803e3d6000fd5b5050505060ff83166000908152600560205260409020611da781612a08565b73ffffffffffffffffffffffffffffffffffffffff83166000908152601d6020908152604080832060ff8816845290915281206001015490611dec83610d1b8461229f565b60028401546040517f9dc29fac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff888116600483015260248201869052929350911690639dc29fac90604401600060405180830381600087803b158015611e6457600080fd5b505af1158015611e78573d6000803e3d6000fd5b5050506002840154604080517f18160ddd000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff90921692506318160ddd9160048083019260209291908290030181865afa158015611eed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f1191906142a3565b6006840180546fffffffffffffffffffffffffffffffff928316700100000000000000000000000000000000029216919091179055611f4d3090565b83546040517f733e88160000000000000000000000000000000000000000000000000000000081526201000090910460ff166004820152600f83900b602482015273ffffffffffffffffffffffffffffffffffffffff919091169063733e881690604401600060405180830381600087803b158015611fcb57600080fd5b505af1158015611fdf573d6000803e3d6000fd5b5050503373ffffffffffffffffffffffffffffffffffffffff8716148015915061206b57506120126201518060026142dd565b73ffffffffffffffffffffffffffffffffffffffff86166000908152601d6020908152604080832060ff8b16845290915290206002015461205d919067ffffffffffffffff166142bc565b67ffffffffffffffff164210155b156120c6576000612088600f83900b67028f5c28f5c28f5c612e9c565b6005850154909150600f90810b9082900b13156120a957506005830154600f0b5b6120b7600f83900b82613450565b91506120c484338361350d565b505b6120d183868361350d565b60ff86166000908152601e602052604090206120ed9086613580565b5073ffffffffffffffffffffffffffffffffffffffff85166000908152601d6020908152604080832060ff808b1685529252822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001681556001810183905560020180547fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000169055845461219491600f85900b916701000000000000009004166135a2565b90508573ffffffffffffffffffffffffffffffffffffffff168760ff167f03b900fa70bdd68edd081a8e571176f5d5326f90697ec727f08ddd67b16981f383866040516121eb929190918252602082015260400190565b60405180910390a3306040517f16240e3f00000000000000000000000000000000000000000000000000000000815262ffffff8716600482015273ffffffffffffffffffffffffffffffffffffffff91909116906316240e3f90602401600060405180830381600087803b15801561226257600080fd5b505af1158015612276573d6000803e3d6000fd5b5050505050505050506113d06001600255565b6060610d2482600061229a8561363e565b613648565b600080670de0b6b3a76400006122be6801000000000000000085614309565b6122c89190614384565b90507fffffffffffffffffffffffffffffffff80000000000000000000000000000000831280159061230a57506f7fffffffffffffffffffffffffffffff8313155b610d24576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f726573756c74206f7574206f662072616e6765000000000000000000000000006044820152606401610c20565b600081600f0b6000036123df576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f4142444b2e6469762d31000000000000000000000000000000000000000000006044820152606401610c20565b60006123f6600f84900b604086901b60170b614384565b90507fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061243857506f7fffffffffffffffffffffffffffffff8113155b6106ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f4142444b2e6469762d32000000000000000000000000000000000000000000006044820152606401610c20565b60008082600f0b121561250d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f6e656761746976652076616c75650000000000000000000000000000000000006044820152606401610c20565b610d24826137c1565b80516000908290820361252c5750600092915050565b50506020015190565b6000806125aa8460020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561075f573d6000803e3d6000fd5b6003850154909150600090600f0b811215610d205760038501546125e49083906125db90600f88810b91900b612e9c565b600f0b90612370565b95945050505050565b600681015460009070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16810361262957506000919050565b60038201546006830154600f9190910b9060009070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1661266c856137ed565b612690906fffffffffffffffffffffffffffffffff16670de0b6b3a76400006143ec565b61269a9190614403565b90506126b2826126a98361229f565b600f0b90612e9c565b949350505050565b6002805403612725576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610c20565b60028055565b60015474010000000000000000000000000000000000000000900460ff16156127b0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610c20565b565b6000808260ff1611612820576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f706f6f6c206e6f7420666f756e640000000000000000000000000000000000006044820152606401610c20565b60ff808316600090815260056020526040902080549091610100909104166128a4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6e6f207065727020696e20706f6f6c00000000000000000000000000000000006044820152606401610c20565b80546000906128bb90610100900460ff1642614417565b825462010000900460ff166000908152600e6020526040902080549192509061ffff83169081106128ee576128ee61420d565b90600052602060002090600a91828204019190066003029054906101000a900462ffffff1692505050919050565b60008260ff16118015612938575060035460ff90811690831611155b61299e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f706f6f6c20696e646578206f7574206f662072616e67650000000000000000006044820152606401610c20565b600081116113d0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f696e76616c696420616d6f756e740000000000000000000000000000000000006044820152606401610c20565b805460ff16612a73576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f6e6f742072756e6e696e670000000000000000000000000000000000000000006044820152606401610c20565b805462010000900460ff166000908152600e602052604081205490805b82811015612c3957835462010000900460ff166000908152600e60205260408120805483908110612ac357612ac361420d565b60009182526020808320600a80840490910154895460ff62010000909104168552600f8352604080862062ffffff60039490960684026101000a9092049490941680865292529190922091925081547801000000000000000000000000000000000000000000000000900460ff166005811115612b4257612b4261442b565b03612ba9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6e6f20776974686472617720696e20656d657267656e637900000000000000006044820152606401610c20565b600281547801000000000000000000000000000000000000000000000000900460ff166005811115612bdd57612bdd61442b565b1480612c1a5750600581547801000000000000000000000000000000000000000000000000900460ff166005811115612c1857612c1861442b565b145b15612c2457600193505b50508080612c319061426b565b915050612a90565b5080612ca1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f6e6f206163746976652070657270657475616c000000000000000000000000006044820152606401610c20565b505050565b8054610100900460ff166000600a8211612cc1576000612cd6565b612ccc60098361445a565b612cd69043614417565b90506000600a8311612ce85782612cf3565b612cf382600a61446d565b9050815b81811015612df257845462010000900460ff166000908152600e60205260408120805483908110612d2a57612d2a61420d565b90600052602060002090600a91828204019190066003029054906101000a900462ffffff169050612d583090565b6040517fa25d6a2800000000000000000000000000000000000000000000000000000000815262ffffff8316600482015273ffffffffffffffffffffffffffffffffffffffff919091169063a25d6a2890602401600060405180830381600087803b158015612dc657600080fd5b505af1158015612dda573d6000803e3d6000fd5b50505050508080612dea9061426b565b915050612cf7565b5050505050565b600080612e6e8460020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561075f573d6000803e3d6000fd5b9050600081600f0b600003612e84575082610d20565b60038501546125e490600f90810b906125db9087900b855b600080604083600f0b85600f0b612eb39190614309565b901d90507fffffffffffffffffffffffffffffffff800000000000000000000000000000008112801590612ef757506f7fffffffffffffffffffffffffffffff8113155b6106ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f4142444b2e6d756c0000000000000000000000000000000000000000000000006044820152606401610c20565b612f66826137ed565b6005830180546fffffffffffffffffffffffffffffffff9283167001000000000000000000000000000000000290831617905560018301805467ffffffffffffffff42167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009091161790556006830154612fe291839116614480565b60069290920180547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff9093169290921790915550565b60006106ce8373ffffffffffffffffffffffffffffffffffffffff84166139d6565b60008061305a83600a6145c9565b61306d6801000000000000000086614309565b6130779190614384565b90507fffffffffffffffffffffffffffffffff8000000000000000000000000000000084128015906130b957506f7fffffffffffffffffffffffffffffff8413155b6106ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f726573756c74206f7574206f662072616e6765000000000000000000000000006044820152606401610c20565b805460ff1661318a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f6e6f742072756e6e696e670000000000000000000000000000000000000000006044820152606401610c20565b805462010000900460ff166000908152600e602052604081205490805b82811015612c3957835462010000900460ff166000908152600e602052604081208054839081106131da576131da61420d565b60009182526020808320600a80840490910154895462010000900460ff168552600f83526040808620929094066003026101000a900462ffffff1680855291529120909150600281547801000000000000000000000000000000000000000000000000900460ff1660058111156132535761325361442b565b0361326357600193505050612c39565b505080806132709061426b565b9150506131a7565b600081600f0b1361328857505050565b82546000906132aa90600f84900b90670100000000000000900460ff166135a2565b84549091506a0100000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16612df281853085613a25565b6132e8826137ed565b6005830180546fffffffffffffffffffffffffffffffff9283167001000000000000000000000000000000000290831617905560018301805467ffffffffffffffff42167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009091161790556006830154612fe2918391166145d8565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f2f3ffaaaad93928855c8700645d1a3643e6ccfdd500efa9fda048a88f557cf019190a35050565b6001805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f5f56bee8cffbe9a78652a74a60705edede02af10b0bbb888ca44b79a0d42ce8090600090a35050565b60008082600f0b84600f0b6134659190614601565b90507fffffffffffffffffffffffffffffffff8000000000000000000000000000000081128015906134a757506f7fffffffffffffffffffffffffffffff8113155b6106ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f4142444b2e7375620000000000000000000000000000000000000000000000006044820152606401610c20565b600081600f0b1361351d57505050565b825460009061353f90600f84900b90670100000000000000900460ff166135a2565b90508060000361354f5750505050565b83546a0100000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16612df2818584613b07565b60006106ce8373ffffffffffffffffffffffffffffffffffffffff8416613b5d565b60008083600f0b1215613611576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f6e656761746976652076616c75650000000000000000000000000000000000006044820152606401610c20565b6801000000000000000061362683600a6145c9565b84600f0b6136349190614309565b6106ce9190614384565b6000610d24825490565b60606000613656838561446d565b9050838110156136c2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f6164646974696f6e206f766572666c6f770000000000000000000000000000006044820152606401610c20565b60006136cd8661363e565b90508181106136dc57816136de565b805b91508115806136ed5750818510155b156136f95750506106ce565b613703858361445a565b67ffffffffffffffff81111561371b5761371b6141de565b604051908082528060200260200182016040528015613744578160200160208202803683370190505b50925060005b613754868461445a565b8110156137b75761376e87613769888461446d565b613c57565b8482815181106137805761378061420d565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910190910152806137af8161426b565b91505061374a565b5050509392505050565b6000680100000000000000006137e3670de0b6b3a7640000600f85900b614309565b610d249190614384565b60004262015180600184015461380d919067ffffffffffffffff166142bc565b67ffffffffffffffff168167ffffffffffffffff1610613842575050600601546fffffffffffffffffffffffffffffffff1690565b60006201518060018501546138619067ffffffffffffffff1684614621565b61386c9060646142dd565b6138769190614642565b6005850154600686015467ffffffffffffffff9290921692506fffffffffffffffffffffffffffffffff700100000000000000000000000000000000909104811691161161394d57600684015460058501546064916138ff916fffffffffffffffffffffffffffffffff9182169170010000000000000000000000000000000090910416614480565b6139099083614669565b6139139190614695565b6005850154613948919070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16614480565b6126b2565b6005840154600685015460649161398d916fffffffffffffffffffffffffffffffff70010000000000000000000000000000000090920482169116614480565b6139979083614669565b6139a19190614695565b60058501546126b2919070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff166145d8565b6000818152600183016020526040812054613a1d57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610d24565b506000610d24565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052613b019085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613c63565b50505050565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052612ca19084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401613a7f565b60008181526001830160205260408120548015613c46576000613b8160018361445a565b8554909150600090613b959060019061445a565b90506000866000018281548110613bae57613bae61420d565b9060005260206000200154905080876000018481548110613bd157613bd161420d565b600091825260209091200155613be883600161446d565b60008281526001890160205260409020558654879080613c0a57613c0a6146b8565b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610d24565b6000915050610d24565b5092915050565b60006106ce8383613d72565b6000613cc5826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16613e079092919063ffffffff16565b9050805160001480613ce6575080806020019051810190613ce691906146e7565b612ca1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610c20565b81546000908210613ddf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f456e756d657261626c655365743a20696478206f7574206f6620626f756e64736044820152606401610c20565b826000018281548110613df457613df461420d565b9060005260206000200154905092915050565b60606126b28484600085856000808673ffffffffffffffffffffffffffffffffffffffff168587604051613e3b9190614728565b60006040518083038185875af1925050503d8060008114613e78576040519150601f19603f3d011682016040523d82523d6000602084013e613e7d565b606091505b5091509150613e8e87838387613e99565b979650505050505050565b60608315613f2f578251600003613f285773ffffffffffffffffffffffffffffffffffffffff85163b613f28576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610c20565b50816126b2565b6126b28383815115613f445781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c209190614744565b803560ff81168114613f8957600080fd5b919050565b600080600060608486031215613fa357600080fd5b613fac84613f78565b95602085013595506040909401359392505050565b602080825282518282018190526000919060409081850190868401855b8281101561402d578151805173ffffffffffffffffffffffffffffffffffffffff168552868101518786015285015167ffffffffffffffff168585015260609093019290850190600101613fde565b5091979650505050505050565b60006020828403121561404c57600080fd5b6106ce82613f78565b60006020828403121561406757600080fd5b813562ffffff811681146106ce57600080fd5b604080825283519082018190526000906020906060840190828701845b828110156140d55781517fffffffff000000000000000000000000000000000000000000000000000000001684529284019290840190600101614097565b50505092019290925292915050565b6000602082840312156140f657600080fd5b5035919050565b80151581146119ec57600080fd5b6000806040838503121561411e57600080fd5b61412783613f78565b91506020830135614137816140fd565b809150509250929050565b803573ffffffffffffffffffffffffffffffffffffffff81168114613f8957600080fd5b6000806040838503121561417957600080fd5b61418283613f78565b915061419060208401614142565b90509250929050565b600080604083850312156141ac57600080fd5b6141b583613f78565b946020939093013593505050565b6000602082840312156141d557600080fd5b6106ce82614142565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361429c5761429c61423c565b5060010190565b6000602082840312156142b557600080fd5b5051919050565b67ffffffffffffffff818116838216019080821115613c5057613c5061423c565b67ffffffffffffffff8181168382160280821691908281146143015761430161423c565b505092915050565b808202600082127f8000000000000000000000000000000000000000000000000000000000000000841416156143415761434161423c565b8181058314821517610d2457610d2461423c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008261439357614393614355565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f8000000000000000000000000000000000000000000000000000000000000000831416156143e7576143e761423c565b500590565b8082028115828204841417610d2457610d2461423c565b60008261441257614412614355565b500490565b60008261442657614426614355565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b81810381811115610d2457610d2461423c565b80820180821115610d2457610d2461423c565b6fffffffffffffffffffffffffffffffff828116828216039080821115613c5057613c5061423c565b600181815b8085111561450257817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156144e8576144e861423c565b808516156144f557918102915b93841c93908002906144ae565b509250929050565b60008261451957506001610d24565b8161452657506000610d24565b816001811461453c576002811461454657614562565b6001915050610d24565b60ff8411156145575761455761423c565b50506001821b610d24565b5060208310610133831016604e8410600b8410161715614585575081810a610d24565b61458f83836144a9565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156145c1576145c161423c565b029392505050565b60006106ce60ff84168361450a565b6fffffffffffffffffffffffffffffffff818116838216019080821115613c5057613c5061423c565b8181036000831280158383131683831282161715613c5057613c5061423c565b67ffffffffffffffff828116828216039080821115613c5057613c5061423c565b600067ffffffffffffffff8084168061465d5761465d614355565b92169190910492915050565b6fffffffffffffffffffffffffffffffff8181168382160280821691908281146143015761430161423c565b60006fffffffffffffffffffffffffffffffff8084168061465d5761465d614355565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6000602082840312156146f957600080fd5b81516106ce816140fd565b60005b8381101561471f578181015183820152602001614707565b50506000910152565b6000825161473a818460208701614704565b9190910192915050565b6020815260008251806020840152614763816040850160208701614704565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fea2646970667358221220f14a06595827a2da4c501ba15230324d853e7d13751ff4ab0fa7b67dd45401e964736f6c63430008150033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.