Contract 0xa229148590c49e9284aa72cf0e5536f60d00b6cd

 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xc1b4f0a6fea0170d28fe0676f00af8bd86b2c3af49fa348f4b7a525a5a5c1a020x60806040171093302022-07-06 8:30:59154 days 18 hrs ago0x44cbfc3ce762fc0fee9ddd6372804b7b660176bc IN  Create: LiquidityPoolHop10 ETH0.021915476788 ETH
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xabcfc1eb7410fc0f49ec6a4c73139bed0f86ec029a7be54f1d1758f2a051b29b231911832022-09-06 15:46:2292 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0x6a6885d4ac2aacc71ee64bffd50ee87272d595dd40e1f986f61ae203c3fa5d99231908472022-09-06 15:44:0792 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0x6a6885d4ac2aacc71ee64bffd50ee87272d595dd40e1f986f61ae203c3fa5d99231908472022-09-06 15:44:0792 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0x6a6885d4ac2aacc71ee64bffd50ee87272d595dd40e1f986f61ae203c3fa5d99231908472022-09-06 15:44:0792 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0xc03b8b0582638bf1e103e0f7d802500c1a3bb864beb3946330110cd287032eb5231908452022-09-06 15:44:0792 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0xa38818a0d15ad0c8edb7c067d29e634ddb544bc6767a69356ae3465fd35ac25d231907782022-09-06 15:43:3792 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0xa38818a0d15ad0c8edb7c067d29e634ddb544bc6767a69356ae3465fd35ac25d231907782022-09-06 15:43:3792 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0xa38818a0d15ad0c8edb7c067d29e634ddb544bc6767a69356ae3465fd35ac25d231907782022-09-06 15:43:3792 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0x421a5314ae7b32bb6d22f69dcb200849962afd4b04a204c76586caedb76e7de8231907742022-09-06 15:43:3692 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0xce75d0c4ad46d9903f76b426bf6f94cddd0e4a066a3a0aeabe45e2f994d0a949231897092022-09-06 15:37:1692 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0xce75d0c4ad46d9903f76b426bf6f94cddd0e4a066a3a0aeabe45e2f994d0a949231897092022-09-06 15:37:1692 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0xce75d0c4ad46d9903f76b426bf6f94cddd0e4a066a3a0aeabe45e2f994d0a949231897092022-09-06 15:37:1692 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0x2a924a2b43c9eb5c36407ec258fc8e5d04a49ec17d84819c316d206f59bc37f8231861942022-09-06 15:16:4192 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0x2a924a2b43c9eb5c36407ec258fc8e5d04a49ec17d84819c316d206f59bc37f8231861942022-09-06 15:16:4192 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0x2a924a2b43c9eb5c36407ec258fc8e5d04a49ec17d84819c316d206f59bc37f8231861942022-09-06 15:16:4192 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0x98143f8b3b81741cef8d1c99861b5b9c17b19d6092561e4141d69f7b4f122ac8231818862022-09-06 15:01:2992 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0x98143f8b3b81741cef8d1c99861b5b9c17b19d6092561e4141d69f7b4f122ac8231818862022-09-06 15:01:2992 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0x98143f8b3b81741cef8d1c99861b5b9c17b19d6092561e4141d69f7b4f122ac8231818862022-09-06 15:01:2992 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0x362ae25ad61a6cef405ba119ece4f71d54f341a7a031bcc9056ec6246ca0f9c5231818762022-09-06 15:01:2792 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0xa2b5a1bf75e2efa4ca62bb46eaf823e9a812ed9f9962dee0e9d85b3b37fe7064231810492022-09-06 14:57:3092 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0xa2b5a1bf75e2efa4ca62bb46eaf823e9a812ed9f9962dee0e9d85b3b37fe7064231810492022-09-06 14:57:3092 days 11 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0x4d34fd1968192f3aa00740d90cd3f7cacd460ab8093f8c4c09b1fb5590f72187231785402022-09-06 14:47:0092 days 12 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0x4d34fd1968192f3aa00740d90cd3f7cacd460ab8093f8c4c09b1fb5590f72187231785402022-09-06 14:47:0092 days 12 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0x4d34fd1968192f3aa00740d90cd3f7cacd460ab8093f8c4c09b1fb5590f72187231785402022-09-06 14:47:0092 days 12 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
0x91a6991e8826c41391d700f85263e2b308713d87121fb4e0f083c44a5b80e85c231785142022-09-06 14:46:5292 days 12 hrs ago 0x3e0199792ce69dc29a0a36146bfa68bd7c8d6633 0xa229148590c49e9284aa72cf0e5536f60d00b6cd0 ETH
[ Download CSV Export 
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.

Contract Source Code Verified (Exact Match)

Contract Name:
LiquidityPoolHop1

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 24 : LiquidityPoolHop1.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "./Storage.sol";
import "./Trade.sol";
import "./Getter.sol";
import "./Admin.sol";
import "../libraries/LibChainedProxy.sol";

contract LiquidityPoolHop1 is Storage, Trade, Getter, Proxy {
    event UpgradeChainedProxy(address prevNextHop, address nextHop);

    function initialize(
        address nextHop,
        address mlp,
        address orderBook,
        address liquidityManager,
        address weth,
        address nativeUnwrapper,
        address vault
    ) external initializer {
        __SafeOwnable_init();

        ChainedProxy.replace(nextHop);
        _storage.mlp = mlp;
        _storage.orderBook = orderBook;
        _storage.liquidityManager = liquidityManager;
        _storage.weth = weth;
        _storage.nativeUnwrapper = nativeUnwrapper;
        _storage.vault = vault;
    }

    /**
     * @dev     Upgrade LiquidityPool.
     *
     * @param   nextHop Hop2 address
     */
    function upgradeChainedProxy(address nextHop) external onlyOwner {
        emit UpgradeChainedProxy(_implementation(), nextHop);
        ChainedProxy.replace(nextHop);
    }

    /**
     * @dev     Forward unrecognized functions to the next hop
     */
    function _implementation() internal view virtual override returns (address) {
        return ChainedProxy.next();
    }
}

File 2 of 24 : Storage.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

import "../components/SafeOwnableUpgradeable.sol";
import "../libraries/LibSubAccount.sol";
import "../libraries/LibAsset.sol";
import "./Types.sol";
import "./Events.sol";

contract Storage is Initializable, SafeOwnableUpgradeable, Events {
    using LibAsset for Asset;

    LiquidityPoolStorage internal _storage;

    modifier onlyOrderBook() {
        require(_msgSender() == _storage.orderBook, "BOK"); // can only be called by order BOoK
        _;
    }

    modifier onlyLiquidityManager() {
        require(_msgSender() == _storage.liquidityManager, "LQM"); // can only be called by LiQuidity Manager
        _;
    }

    function _updateSequence() internal {
        unchecked {
            _storage.sequence += 1;
        }
        emit UpdateSequence(_storage.sequence);
    }

    function _updateBrokerTransactions() internal {
        unchecked {
            _storage.brokerTransactions += 1;
        }
    }

    function _blockTimestamp() internal view virtual returns (uint32) {
        return uint32(block.timestamp);
    }

    function _hasAsset(uint8 assetId) internal view returns (bool) {
        return assetId < _storage.assets.length;
    }

    function _isStable(uint8 tokenId) internal view returns (bool) {
        return _storage.assets[tokenId].isStable();
    }

    bytes32[50] internal _gap;
}

File 3 of 24 : Trade.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "../libraries/LibAsset.sol";
import "../libraries/LibSubAccount.sol";
import "../libraries/LibMath.sol";
import "../libraries/LibReferenceOracle.sol";
import "./Account.sol";
import "./Storage.sol";

contract Trade is Storage, Account {
    using LibAsset for Asset;
    using LibMath for uint256;
    using LibSubAccount for bytes32;

    function openPosition(
        bytes32 subAccountId,
        uint96 amount,
        uint96 collateralPrice,
        uint96 assetPrice
    ) external onlyOrderBook returns (uint96) {
        LibSubAccount.DecodedSubAccountId memory decoded = subAccountId.decodeSubAccountId();
        require(decoded.account != address(0), "T=0"); // Trader address is zero
        require(_hasAsset(decoded.collateralId), "LST"); // the asset is not LiSTed
        require(_hasAsset(decoded.assetId), "LST"); // the asset is not LiSTed
        require(amount != 0, "A=0"); // Amount Is Zero

        Asset storage asset = _storage.assets[decoded.assetId];
        Asset storage collateral = _storage.assets[decoded.collateralId];
        SubAccount storage subAccount = _storage.accounts[subAccountId];
        require(asset.isOpenable(), "OPN"); // the asset is not OPeNable
        require(!asset.isStable(), "STB"); // can not trade a STaBle coin
        require(asset.isTradable(), "TRD"); // the asset is not TRaDable
        require(asset.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(collateral.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(decoded.isLong || asset.isShortable(), "SHT"); // can not SHorT this asset
        assetPrice = LibReferenceOracle.checkPriceWithSpread(
            _storage,
            asset,
            assetPrice,
            decoded.isLong ? SpreadType.Ask : SpreadType.Bid
        );
        collateralPrice = LibReferenceOracle.checkPrice(_storage, collateral, collateralPrice);

        // fee & funding
        uint96 feeUsd = _getFeeUsd(subAccount, asset, decoded.isLong, amount, assetPrice);
        _updateEntryFunding(subAccount, asset, decoded.isLong);
        {
            uint96 feeCollateral = uint256(feeUsd).wdiv(collateralPrice).safeUint96();
            require(subAccount.collateral >= feeCollateral, "FEE"); // collateral can not pay Fee
            subAccount.collateral -= feeCollateral;
            collateral.collectedFee += feeCollateral;
            collateral.spotLiquidity += feeCollateral;
            emit CollectedFee(decoded.collateralId, feeCollateral);
        }

        // position
        {
            (, uint96 pnlUsd) = _positionPnlUsd(asset, subAccount, decoded.isLong, subAccount.size, assetPrice);
            uint96 newSize = subAccount.size + amount;
            if (pnlUsd == 0) {
                subAccount.entryPrice = assetPrice;
            } else {
                subAccount.entryPrice = ((uint256(subAccount.entryPrice) *
                    uint256(subAccount.size) +
                    uint256(assetPrice) *
                    uint256(amount)) / newSize).safeUint96();
            }
            subAccount.size = newSize;
        }
        subAccount.lastIncreasedTime = _blockTimestamp();
        {
            OpenPositionArgs memory args = OpenPositionArgs({
                subAccountId: subAccountId,
                collateralId: decoded.collateralId,
                isLong: decoded.isLong,
                amount: amount,
                assetPrice: assetPrice,
                collateralPrice: collateralPrice,
                newEntryPrice: subAccount.entryPrice,
                feeUsd: feeUsd,
                remainPosition: subAccount.size,
                remainCollateral: subAccount.collateral
            });
            emit OpenPosition(decoded.account, decoded.assetId, args);
        }
        // total
        _increaseTotalSize(asset, decoded.isLong, amount, assetPrice);
        // post check
        require(_isAccountImSafe(subAccount, decoded.assetId, decoded.isLong, collateralPrice, assetPrice), "!IM");
        _updateSequence();
        _updateBrokerTransactions();
        return assetPrice;
    }

    struct ClosePositionContext {
        LibSubAccount.DecodedSubAccountId id;
        uint96 totalFeeUsd;
        uint96 paidFeeUsd;
    }

    /**
     * @notice Close a position
     *
     * @param  subAccountId     check LibSubAccount.decodeSubAccountId for detail.
     * @param  amount           position size.
     * @param  profitAssetId    for long position (unless asset.useStable is true), ignore this argument;
     *                          for short position, the profit asset should be one of the stable coin.
     * @param  collateralPrice  price of subAccount.collateral.
     * @param  assetPrice       price of subAccount.asset.
     * @param  profitAssetPrice price of profitAssetId. ignore this argument if profitAssetId is ignored.
     */
    function closePosition(
        bytes32 subAccountId,
        uint96 amount,
        uint8 profitAssetId, // only used when !isLong
        uint96 collateralPrice,
        uint96 assetPrice,
        uint96 profitAssetPrice // only used when !isLong
    ) external onlyOrderBook returns (uint96) {
        ClosePositionContext memory ctx;
        ctx.id = subAccountId.decodeSubAccountId();
        require(ctx.id.account != address(0), "T=0"); // Trader address is zero
        require(_hasAsset(ctx.id.collateralId), "LST"); // the asset is not LiSTed
        require(_hasAsset(ctx.id.assetId), "LST"); // the asset is not LiSTed
        require(amount != 0, "A=0"); // Amount Is Zero

        Asset storage asset = _storage.assets[ctx.id.assetId];
        Asset storage collateral = _storage.assets[ctx.id.collateralId];
        SubAccount storage subAccount = _storage.accounts[subAccountId];
        require(!asset.isStable(), "STB"); // can not trade a STaBle coin
        require(asset.isTradable(), "TRD"); // the asset is not TRaDable
        require(asset.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(collateral.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(ctx.id.isLong || asset.isShortable(), "SHT"); // can not SHorT this asset
        require(amount <= subAccount.size, "A>S"); // close Amount is Larger than position Size
        assetPrice = LibReferenceOracle.checkPriceWithSpread(
            _storage,
            asset,
            assetPrice,
            ctx.id.isLong ? SpreadType.Bid : SpreadType.Ask
        );
        collateralPrice = LibReferenceOracle.checkPrice(_storage, collateral, collateralPrice);
        if (ctx.id.isLong && !asset.useStableTokenForProfit()) {
            profitAssetId = ctx.id.assetId;
            profitAssetPrice = assetPrice;
        } else {
            require(_isStable(profitAssetId), "STB"); // profit asset should be a STaBle coin
            profitAssetPrice = LibReferenceOracle.checkPrice(
                _storage,
                _storage.assets[profitAssetId],
                profitAssetPrice
            );
        }
        require(_storage.assets[profitAssetId].isEnabled(), "ENA"); // the token is temporarily not ENAbled

        // total
        _decreaseTotalSize(asset, ctx.id.isLong, amount, subAccount.entryPrice);
        // fee & funding
        ctx.totalFeeUsd = _getFeeUsd(subAccount, asset, ctx.id.isLong, amount, assetPrice);
        _updateEntryFunding(subAccount, asset, ctx.id.isLong);
        // realize pnl
        (bool hasProfit, uint96 pnlUsd) = _positionPnlUsd(asset, subAccount, ctx.id.isLong, amount, assetPrice);
        if (hasProfit) {
            ctx.paidFeeUsd = _realizeProfit(
                ctx.id.account,
                pnlUsd,
                ctx.totalFeeUsd,
                _storage.assets[profitAssetId],
                profitAssetPrice
            );
        } else {
            _realizeLoss(subAccount, collateral, collateralPrice, pnlUsd, true);
        }
        subAccount.size -= amount;
        if (subAccount.size == 0) {
            subAccount.entryPrice = 0;
            subAccount.entryFunding = 0;
            subAccount.lastIncreasedTime = 0;
        }
        // ignore fees if can not afford
        if (ctx.totalFeeUsd > ctx.paidFeeUsd) {
            uint96 feeCollateral = uint256(ctx.totalFeeUsd - ctx.paidFeeUsd).wdiv(collateralPrice).safeUint96();
            feeCollateral = LibMath.min(feeCollateral, subAccount.collateral);
            subAccount.collateral -= feeCollateral;
            collateral.collectedFee += feeCollateral;
            collateral.spotLiquidity += feeCollateral;
            emit CollectedFee(ctx.id.collateralId, feeCollateral);
            ctx.paidFeeUsd += uint256(feeCollateral).wmul(collateralPrice).safeUint96();
        }
        {
            ClosePositionArgs memory args = ClosePositionArgs({
                subAccountId: subAccountId,
                collateralId: ctx.id.collateralId,
                profitAssetId: profitAssetId,
                isLong: ctx.id.isLong,
                amount: amount,
                assetPrice: assetPrice,
                collateralPrice: collateralPrice,
                profitAssetPrice: profitAssetPrice,
                feeUsd: ctx.paidFeeUsd,
                hasProfit: hasProfit,
                pnlUsd: pnlUsd,
                remainPosition: subAccount.size,
                remainCollateral: subAccount.collateral
            });
            emit ClosePosition(ctx.id.account, ctx.id.assetId, args);
        }
        // post check
        require(_isAccountMmSafe(subAccount, ctx.id.assetId, ctx.id.isLong, collateralPrice, assetPrice), "!MM");
        _updateSequence();
        _updateBrokerTransactions();
        return assetPrice;
    }

    struct LiquidateContext {
        LibSubAccount.DecodedSubAccountId id;
        uint96 totalFeeUsd;
        uint96 paidFeeUsd;
        uint96 oldPositionSize;
    }

    function liquidate(
        bytes32 subAccountId,
        uint8 profitAssetId, // only used when !isLong
        uint96 collateralPrice,
        uint96 assetPrice,
        uint96 profitAssetPrice // only used when !isLong
    ) external onlyOrderBook returns (uint96) {
        LiquidateContext memory ctx;
        ctx.id = subAccountId.decodeSubAccountId();
        require(ctx.id.account != address(0), "T=0"); // Trader address is zero
        require(_hasAsset(ctx.id.collateralId), "LST"); // the asset is not LiSTed
        require(_hasAsset(ctx.id.assetId), "LST"); // the asset is not LiSTed

        Asset storage asset = _storage.assets[ctx.id.assetId];
        Asset storage collateral = _storage.assets[ctx.id.collateralId];
        SubAccount storage subAccount = _storage.accounts[subAccountId];
        require(!asset.isStable(), "STB"); // can not trade a STaBle coin
        require(asset.isTradable(), "TRD"); // the asset is not TRaDable
        require(asset.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(collateral.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(ctx.id.isLong || asset.isShortable(), "SHT"); // can not SHorT this asset
        require(subAccount.size > 0, "S=0"); // position Size Is Zero
        assetPrice = LibReferenceOracle.checkPriceWithSpread(
            _storage,
            asset,
            assetPrice,
            ctx.id.isLong ? SpreadType.Bid : SpreadType.Ask
        );
        collateralPrice = LibReferenceOracle.checkPrice(_storage, collateral, collateralPrice);
        if (ctx.id.isLong && !asset.useStableTokenForProfit()) {
            profitAssetId = ctx.id.assetId;
            profitAssetPrice = assetPrice;
        } else {
            require(_isStable(profitAssetId), "STB"); // profit asset should be a STaBle coin
            profitAssetPrice = LibReferenceOracle.checkPrice(
                _storage,
                _storage.assets[profitAssetId],
                profitAssetPrice
            );
        }
        require(_storage.assets[profitAssetId].isEnabled(), "ENA"); // the token is temporarily not ENAbled

        // total
        _decreaseTotalSize(asset, ctx.id.isLong, subAccount.size, subAccount.entryPrice);
        // fee & funding
        bool hasProfit;
        uint96 pnlUsd;
        {
            uint96 fundingFee = _getFundingFeeUsd(subAccount, asset, ctx.id.isLong, assetPrice);
            {
                uint96 positionFee = _getPositionFeeUsd(asset, subAccount.size, assetPrice);
                ctx.totalFeeUsd = fundingFee + positionFee;
            }
            // should mm unsafe
            (hasProfit, pnlUsd) = _positionPnlUsd(asset, subAccount, ctx.id.isLong, subAccount.size, assetPrice);
            require(
                !_isAccountSafe(
                    subAccount,
                    collateralPrice,
                    assetPrice,
                    asset.maintenanceMarginRate,
                    hasProfit,
                    pnlUsd,
                    fundingFee
                ),
                "MMS"
            ); // Maintenance Margin Safe
        }
        // realize pnl
        ctx.oldPositionSize = subAccount.size;
        if (hasProfit) {
            // this case is impossible unless MMRate changes
            ctx.paidFeeUsd = _realizeProfit(
                ctx.id.account,
                pnlUsd,
                ctx.totalFeeUsd,
                _storage.assets[profitAssetId],
                profitAssetPrice
            );
        } else {
            _realizeLoss(subAccount, collateral, collateralPrice, pnlUsd, false);
        }
        subAccount.size = 0;
        subAccount.entryPrice = 0;
        subAccount.entryFunding = 0;
        subAccount.lastIncreasedTime = 0;
        // ignore fees if can not afford
        if (ctx.totalFeeUsd > ctx.paidFeeUsd) {
            uint96 feeCollateral = uint256(ctx.totalFeeUsd - ctx.paidFeeUsd).wdiv(collateralPrice).safeUint96();
            feeCollateral = LibMath.min(feeCollateral, subAccount.collateral);
            subAccount.collateral -= feeCollateral;
            collateral.collectedFee += feeCollateral;
            collateral.spotLiquidity += feeCollateral;
            emit CollectedFee(ctx.id.collateralId, feeCollateral);
            ctx.paidFeeUsd += uint256(feeCollateral).wmul(collateralPrice).safeUint96();
        }
        {
            LiquidateArgs memory args = LiquidateArgs({
                subAccountId: subAccountId,
                collateralId: ctx.id.collateralId,
                profitAssetId: profitAssetId,
                isLong: ctx.id.isLong,
                amount: ctx.oldPositionSize,
                assetPrice: assetPrice,
                collateralPrice: collateralPrice,
                profitAssetPrice: profitAssetPrice,
                feeUsd: ctx.paidFeeUsd,
                hasProfit: hasProfit,
                pnlUsd: pnlUsd,
                remainCollateral: subAccount.collateral
            });
            emit Liquidate(ctx.id.account, ctx.id.assetId, args);
        }
        _updateSequence();
        _updateBrokerTransactions();
        return assetPrice;
    }

    struct WithdrawProfitContext {
        LibSubAccount.DecodedSubAccountId id;
    }

    /**
     *  long : (exit - entry) size = (exit - entry') size + withdrawUSD
     *  short: (entry - exit) size = (entry' - exit) size + withdrawUSD
     */
    function withdrawProfit(
        bytes32 subAccountId,
        uint256 rawAmount,
        uint8 profitAssetId, // only used when !isLong
        uint96 collateralPrice,
        uint96 assetPrice,
        uint96 profitAssetPrice // only used when !isLong
    ) external onlyOrderBook {
        require(rawAmount != 0, "A=0"); // Amount Is Zero
        WithdrawProfitContext memory ctx;
        ctx.id = subAccountId.decodeSubAccountId();
        require(ctx.id.account != address(0), "T=0"); // Trader address is zero
        require(_hasAsset(ctx.id.collateralId), "LST"); // the asset is not LiSTed
        require(_hasAsset(ctx.id.assetId), "LST"); // the asset is not LiSTed

        Asset storage asset = _storage.assets[ctx.id.assetId];
        Asset storage collateral = _storage.assets[ctx.id.collateralId];
        SubAccount storage subAccount = _storage.accounts[subAccountId];
        require(!asset.isStable(), "STB"); // can not trade a STaBle coin
        require(asset.isTradable(), "TRD"); // the asset is not TRaDable
        require(asset.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(collateral.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(ctx.id.isLong || asset.isShortable(), "SHT"); // can not SHorT this asset
        require(subAccount.size > 0, "S=0"); // position Size Is Zero
        assetPrice = LibReferenceOracle.checkPriceWithSpread(
            _storage,
            asset,
            assetPrice,
            ctx.id.isLong ? SpreadType.Bid : SpreadType.Ask
        );
        collateralPrice = LibReferenceOracle.checkPrice(_storage, collateral, collateralPrice);
        if (ctx.id.isLong && !asset.useStableTokenForProfit()) {
            profitAssetId = ctx.id.assetId;
            profitAssetPrice = assetPrice;
        } else {
            require(_isStable(profitAssetId), "STB"); // profit asset should be a STaBle coin
            profitAssetPrice = LibReferenceOracle.checkPrice(
                _storage,
                _storage.assets[profitAssetId],
                profitAssetPrice
            );
        }
        require(_storage.assets[profitAssetId].isEnabled(), "ENA"); // the token is temporarily not ENAbled

        // fee & funding
        uint96 totalFeeUsd = _getFundingFeeUsd(subAccount, asset, ctx.id.isLong, assetPrice);
        _updateEntryFunding(subAccount, asset, ctx.id.isLong);
        // withdraw
        uint96 deltaUsd = _storage.assets[profitAssetId].toWad(rawAmount);
        deltaUsd = uint256(deltaUsd).wmul(profitAssetPrice).safeUint96();
        deltaUsd += totalFeeUsd;
        // profit
        {
            (bool hasProfit, uint96 pnlUsd) = _positionPnlUsd(
                asset,
                subAccount,
                ctx.id.isLong,
                subAccount.size,
                assetPrice
            );
            require(hasProfit, "U<0"); // profitUsd is negative
            require(pnlUsd >= deltaUsd, "U<W"); // profitUsd can not pay fee or is less than the amount requested for Withdrawal
        }
        _realizeProfit(ctx.id.account, deltaUsd, totalFeeUsd, _storage.assets[profitAssetId], profitAssetPrice);
        // new entry price
        if (ctx.id.isLong) {
            subAccount.entryPrice += uint256(deltaUsd).wdiv(subAccount.size).safeUint96();
            asset.averageLongPrice += uint256(deltaUsd).wdiv(asset.totalLongPosition).safeUint96();
        } else {
            subAccount.entryPrice -= uint256(deltaUsd).wdiv(subAccount.size).safeUint96();
            asset.averageShortPrice -= uint256(deltaUsd).wdiv(asset.totalShortPosition).safeUint96();
        }
        require(_isAccountImSafe(subAccount, ctx.id.assetId, ctx.id.isLong, collateralPrice, assetPrice), "!IM");
        {
            WithdrawProfitArgs memory args = WithdrawProfitArgs({
                subAccountId: subAccountId,
                collateralId: ctx.id.collateralId,
                profitAssetId: profitAssetId,
                isLong: ctx.id.isLong,
                withdrawRawAmount: rawAmount,
                assetPrice: assetPrice,
                collateralPrice: collateralPrice,
                profitAssetPrice: profitAssetPrice,
                entryPrice: subAccount.entryPrice,
                feeUsd: totalFeeUsd
            });
            emit WithdrawProfit(ctx.id.account, ctx.id.assetId, args);
        }
        _updateSequence();
        _updateBrokerTransactions();
    }

    function _increaseTotalSize(
        Asset storage asset,
        bool isLong,
        uint96 amount,
        uint96 price
    ) internal {
        if (isLong) {
            uint96 newPosition = asset.totalLongPosition + amount;
            asset.averageLongPrice = ((uint256(asset.averageLongPrice) *
                uint256(asset.totalLongPosition) +
                uint256(price) *
                uint256(amount)) / uint256(newPosition)).safeUint96();
            asset.totalLongPosition = newPosition;
        } else {
            uint96 newPosition = asset.totalShortPosition + amount;
            asset.averageShortPrice = ((uint256(asset.averageShortPrice) *
                uint256(asset.totalShortPosition) +
                uint256(price) *
                uint256(amount)) / uint256(newPosition)).safeUint96();
            asset.totalShortPosition = newPosition;
        }
    }

    function _decreaseTotalSize(
        Asset storage asset,
        bool isLong,
        uint96 amount,
        uint96 oldEntryPrice
    ) internal {
        if (isLong) {
            uint96 newPosition = asset.totalLongPosition - amount;
            if (newPosition == 0) {
                asset.averageLongPrice = 0;
            } else {
                asset.averageLongPrice = ((uint256(asset.averageLongPrice) *
                    uint256(asset.totalLongPosition) -
                    uint256(oldEntryPrice) *
                    uint256(amount)) / uint256(newPosition)).safeUint96();
            }
            asset.totalLongPosition = newPosition;
        } else {
            uint96 newPosition = asset.totalShortPosition - amount;
            if (newPosition == 0) {
                asset.averageShortPrice = 0;
            } else {
                asset.averageShortPrice = ((uint256(asset.averageShortPrice) *
                    uint256(asset.totalShortPosition) -
                    uint256(oldEntryPrice) *
                    uint256(amount)) / uint256(newPosition)).safeUint96();
            }
            asset.totalShortPosition = newPosition;
        }
    }

    function _realizeProfit(
        address trader,
        uint96 pnlUsd,
        uint96 feeUsd,
        Asset storage profitAsset,
        uint96 profitAssetPrice
    ) internal returns (uint96 paidFeeUsd) {
        paidFeeUsd = LibMath.min(feeUsd, pnlUsd);
        // pnl
        pnlUsd -= paidFeeUsd;
        if (pnlUsd > 0) {
            uint96 profitCollateral = uint256(pnlUsd).wdiv(profitAssetPrice).safeUint96();
            // transfer profit token
            uint96 spot = LibMath.min(profitCollateral, profitAsset.spotLiquidity);
            if (spot > 0) {
                profitAsset.spotLiquidity -= spot; // already deduct fee
                uint256 rawAmount = profitAsset.toRaw(spot);
                profitAsset.transferOut(trader, rawAmount, _storage.weth, _storage.nativeUnwrapper);
            }
            // debt
            {
                uint96 muxTokenAmount = profitCollateral - spot;
                if (muxTokenAmount > 0) {
                    profitAsset.issueMuxToken(trader, uint256(muxTokenAmount));
                    emit IssueMuxToken(profitAsset.isStable() ? 0 : profitAsset.id, profitAsset.isStable(), muxTokenAmount);
                }
            }
        }
        // fee
        if (paidFeeUsd > 0) {
            uint96 paidFeeCollateral = uint256(paidFeeUsd).wdiv(profitAssetPrice).safeUint96();
            profitAsset.collectedFee += paidFeeCollateral; // spotLiquidity was modified above
            emit CollectedFee(profitAsset.id, paidFeeCollateral);
        }
    }

    function _realizeLoss(
        SubAccount storage subAccount,
        Asset storage collateral,
        uint96 collateralPrice,
        uint96 pnlUsd,
        bool isThrowBankrupt
    ) internal {
        if (pnlUsd == 0) {
            return;
        }
        uint96 pnlCollateral = uint256(pnlUsd).wdiv(collateralPrice).safeUint96();
        if (isThrowBankrupt) {
            require(subAccount.collateral >= pnlCollateral, "M=0"); // Margin balance Is Zero. the account is bankrupt
        } else {
            pnlCollateral = LibMath.min(pnlCollateral, subAccount.collateral);
        }
        subAccount.collateral -= pnlCollateral;
        collateral.spotLiquidity += pnlCollateral;
    }
}

File 4 of 24 : Getter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "../libraries/LibSubAccount.sol";
import "./Storage.sol";

contract Getter is Storage {
    using LibSubAccount for bytes32;

    function getAssetInfo(uint8 assetId) external view returns (Asset memory) {
        require(assetId < _storage.assets.length, "LST"); // the asset is not LiSTed
        return _storage.assets[assetId];
    }

    function getAllAssetInfo() external view returns (Asset[] memory) {
        return _storage.assets;
    }

    function getAssetAddress(uint8 assetId) external view returns (address) {
        require(assetId < _storage.assets.length, "LST"); // the asset is not LiSTed
        return _storage.assets[assetId].tokenAddress;
    }

    function getLiquidityPoolStorage()
        external
        view
        returns (
            // [0] shortFundingBaseRate8H
            // [1] shortFundingLimitRate8H
            // [2] lastFundingTime
            // [3] fundingInterval
            // [4] liquidityBaseFeeRate
            // [5] liquidityDynamicFeeRate
            // [6] sequence. note: will be 0 after 0xffffffff
            // [7] strictStableDeviation
            uint32[8] memory u32s,
            // [0] mlpPriceLowerBound
            // [1] mlpPriceUpperBound
            uint96[2] memory u96s
        )
    {
        u32s[0] = _storage.shortFundingBaseRate8H;
        u32s[1] = _storage.shortFundingLimitRate8H;
        u32s[2] = _storage.lastFundingTime;
        u32s[3] = _storage.fundingInterval;
        u32s[4] = _storage.liquidityBaseFeeRate;
        u32s[5] = _storage.liquidityDynamicFeeRate;
        u32s[6] = _storage.sequence;
        u32s[7] = _storage.strictStableDeviation;
        u96s[0] = _storage.mlpPriceLowerBound;
        u96s[1] = _storage.mlpPriceUpperBound;
    }

    function getSubAccount(bytes32 subAccountId)
        external
        view
        returns (
            uint96 collateral,
            uint96 size,
            uint32 lastIncreasedTime,
            uint96 entryPrice,
            uint128 entryFunding
        )
    {
        SubAccount storage subAccount = _storage.accounts[subAccountId];
        collateral = subAccount.collateral;
        size = subAccount.size;
        lastIncreasedTime = subAccount.lastIncreasedTime;
        entryPrice = subAccount.entryPrice;
        entryFunding = subAccount.entryFunding;
    }
}

File 5 of 24 : Admin.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";

import "./Storage.sol";
import "../libraries/LibAsset.sol";
import "../libraries/LibMath.sol";
import "../libraries/LibReferenceOracle.sol";
import "../core/Types.sol";

contract Admin is Storage {
    using LibAsset for Asset;
    using LibMath for uint256;
    using SafeERC20Upgradeable for IERC20Upgradeable;

    function addAsset(
        uint8 assetId,
        bytes32 symbol,
        uint8 decimals,
        bool isStable,
        address tokenAddress,
        address muxTokenAddress
    ) external onlyOwner {
        require(decimals <= 18, "DCM"); // invalid DeCiMals
        require(assetId == _storage.assets.length, "AID"); // invalid AssetID
        require(assetId < 0xFF, "FLL"); // assets list is FuLL
        require(symbol != "", "SYM"); // invalid SYMbol

        _storage.assets.push();
        Asset storage asset = _storage.assets[assetId];
        asset.symbol = symbol;
        asset.id = assetId;
        asset.decimals = decimals;
        asset.flags = (asset.flags & (~ASSET_IS_STABLE)) | (isStable ? ASSET_IS_STABLE : 0);
        asset.tokenAddress = tokenAddress;
        asset.muxTokenAddress = muxTokenAddress;
        emit AddAsset(assetId, symbol, decimals, isStable, tokenAddress, muxTokenAddress);
        _updateSequence();
    }

    function setAssetSymbol(uint8 assetId, bytes32 symbol) external onlyOwner {
        require(_hasAsset(assetId), "LST"); // the asset is not LiSTed
        require(symbol != "", "SYM"); // invalid SYMbol

        Asset storage asset = _storage.assets[assetId];
        require(asset.symbol != symbol, "CHG"); // setting is not CHanGed
        asset.symbol = symbol;
        emit SetAssetSymbol(assetId, symbol);
        _updateSequence();
    }

    function setAssetParams(
        uint8 assetId,
        uint32 newInitialMarginRate, // 1e5
        uint32 newMaintenanceMarginRate, // 1e5
        uint32 newPositionFeeRate, // 1e5
        uint32 newMinProfitRate, // 1e5
        uint32 newMinProfitTime, // 1e0
        uint96 newMaxLongPositionSize,
        uint96 newMaxShortPositionSize,
        uint32 newSpotWeight,
        uint32 newHalfSpread
    ) external onlyOwner {
        require(_hasAsset(assetId), "LST"); // the asset is not LiSTed
        Asset storage asset = _storage.assets[assetId];
        require(asset.initialMarginRate == 0 || newInitialMarginRate <= asset.initialMarginRate, "IMR"); // Initial Margin Raised
        require(asset.maintenanceMarginRate == 0 || newMaintenanceMarginRate <= asset.maintenanceMarginRate, "MMR"); // Maintenance Margin Raised
        asset.initialMarginRate = newInitialMarginRate;
        asset.maintenanceMarginRate = newMaintenanceMarginRate;
        asset.positionFeeRate = newPositionFeeRate;
        asset.minProfitRate = newMinProfitRate;
        asset.minProfitTime = newMinProfitTime;
        asset.maxLongPositionSize = newMaxLongPositionSize;
        asset.maxShortPositionSize = newMaxShortPositionSize;
        asset.spotWeight = newSpotWeight;
        asset.halfSpread = newHalfSpread;
        emit SetAssetParams(
            assetId,
            newInitialMarginRate,
            newMaintenanceMarginRate,
            newPositionFeeRate,
            newMinProfitRate,
            newMinProfitTime,
            newMaxLongPositionSize,
            newMaxShortPositionSize,
            newSpotWeight,
            newHalfSpread
        );
        _updateSequence();
    }

    function setAssetFlags(
        uint8 assetId,
        bool isTradable,
        bool isOpenable,
        bool isShortable,
        bool useStableTokenForProfit,
        bool isEnabled,
        bool isStrictStable,
        bool canAddRemoveLiquidity
    ) external onlyOwner {
        require(_hasAsset(assetId), "LST"); // the asset is not LiSTed
        Asset storage asset = _storage.assets[assetId];
        if (!asset.isStable()) {
            require(!isStrictStable, "STB"); // the asset is impossible to be a strict STaBle coin
        }
        uint56 newFlags = asset.flags;
        newFlags = (newFlags & (~ASSET_IS_TRADABLE)) | (isTradable ? ASSET_IS_TRADABLE : 0);
        newFlags = (newFlags & (~ASSET_IS_OPENABLE)) | (isOpenable ? ASSET_IS_OPENABLE : 0);
        newFlags = (newFlags & (~ASSET_IS_SHORTABLE)) | (isShortable ? ASSET_IS_SHORTABLE : 0);
        newFlags =
            (newFlags & (~ASSET_USE_STABLE_TOKEN_FOR_PROFIT)) |
            (useStableTokenForProfit ? ASSET_USE_STABLE_TOKEN_FOR_PROFIT : 0);
        newFlags = (newFlags & (~ASSET_IS_ENABLED)) | (isEnabled ? ASSET_IS_ENABLED : 0);
        newFlags = (newFlags & (~ASSET_IS_STRICT_STABLE)) | (isStrictStable ? ASSET_IS_STRICT_STABLE : 0);
        newFlags =
            (newFlags & (~ASSET_CAN_ADD_REMOVE_LIQUIDITY)) |
            (canAddRemoveLiquidity ? ASSET_CAN_ADD_REMOVE_LIQUIDITY : 0);
        emit SetAssetFlags(assetId, asset.flags, newFlags);
        asset.flags = newFlags;
        _updateSequence();
    }

    function setFundingParams(
        uint8 assetId,
        uint32 newBaseRate8H,
        uint32 newLimitRate8H
    ) external onlyOwner {
        require(_hasAsset(assetId), "LST"); // the asset is not LiSTed
        if (_storage.assets[assetId].isStable()) {
            _storage.shortFundingBaseRate8H = newBaseRate8H;
            _storage.shortFundingLimitRate8H = newLimitRate8H;
        } else {
            Asset storage asset = _storage.assets[assetId];
            asset.longFundingBaseRate8H = newBaseRate8H;
            asset.longFundingLimitRate8H = newLimitRate8H;
        }
        emit SetFundingParams(assetId, newBaseRate8H, newLimitRate8H);
        _updateSequence();
    }

    function setReferenceOracle(
        uint8 assetId,
        ReferenceOracleType referenceOracleType,
        address referenceOracle,
        uint32 referenceDeviation // 1e5
    ) external onlyOwner {
        LibReferenceOracle.checkParameters(referenceOracleType, referenceOracle, referenceDeviation);
        require(_hasAsset(assetId), "LST"); // the asset is not LiSTed
        Asset storage asset = _storage.assets[assetId];
        asset.referenceOracleType = uint8(referenceOracleType);
        asset.referenceOracle = referenceOracle;
        asset.referenceDeviation = referenceDeviation;
        emit SetReferenceOracle(assetId, uint8(referenceOracleType), referenceOracle, referenceDeviation);
        _updateSequence();
    }

    function setNumbers(
        uint32 newFundingInterval,
        uint96 newMlpPriceLowerBound,
        uint96 newMlpPriceUpperBound,
        uint32 newLiquidityBaseFeeRate, // 1e5
        uint32 newLiquidityDynamicFeeRate, // 1e5
        uint32 newStrictStableDeviation, // 1e5
        uint96 newBrokerGasRebate
    ) external onlyOwner {
        require(newLiquidityBaseFeeRate < 1e5, "F>1"); // %fee > 100%
        require(newLiquidityDynamicFeeRate < 1e5, "F>1"); // %fee > 100%
        require(newStrictStableDeviation < 1e5, "D>1"); // %deviation > 100%
        if (_storage.fundingInterval != newFundingInterval) {
            emit SetFundingInterval(_storage.fundingInterval, newFundingInterval);
            _storage.fundingInterval = newFundingInterval;
        }
        if (
            _storage.mlpPriceLowerBound != newMlpPriceLowerBound || _storage.mlpPriceUpperBound != newMlpPriceUpperBound
        ) {
            _storage.mlpPriceLowerBound = newMlpPriceLowerBound;
            _storage.mlpPriceUpperBound = newMlpPriceUpperBound;
            emit SetMlpPriceRange(newMlpPriceLowerBound, newMlpPriceUpperBound);
        }
        if (
            _storage.liquidityBaseFeeRate != newLiquidityBaseFeeRate ||
            _storage.liquidityDynamicFeeRate != newLiquidityDynamicFeeRate
        ) {
            _storage.liquidityBaseFeeRate = newLiquidityBaseFeeRate;
            _storage.liquidityDynamicFeeRate = newLiquidityDynamicFeeRate;
            emit SetLiquidityFee(newLiquidityBaseFeeRate, newLiquidityDynamicFeeRate);
        }
        if (_storage.strictStableDeviation != newStrictStableDeviation) {
            _storage.strictStableDeviation = newStrictStableDeviation;
            emit SetStrictStableDeviation(newStrictStableDeviation);
        }
        if (_storage.brokerGasRebate != newBrokerGasRebate) {
            _storage.brokerGasRebate = newBrokerGasRebate;
            emit SetBrokerGasRebate(newBrokerGasRebate);
        }
        _updateSequence();
    }

    function transferLiquidityOut(uint8[] memory assetIds, uint256[] memory rawAmounts) external onlyLiquidityManager {
        uint256 length = assetIds.length;
        require(length > 0, "MTY"); // argument array is eMpTY
        require(assetIds.length == rawAmounts.length, "LEN"); // LENgth of 2 arguments does not match
        for (uint256 i = 0; i < length; i++) {
            Asset storage asset = _storage.assets[assetIds[i]];
            IERC20Upgradeable(asset.tokenAddress).transfer(msg.sender, rawAmounts[i]);
            uint96 wadAmount = asset.toWad(rawAmounts[i]);
            require(asset.spotLiquidity >= wadAmount, "NLT"); // not enough liquidity
            asset.spotLiquidity -= wadAmount;
            emit TransferLiquidity(address(this), msg.sender, assetIds[i], rawAmounts[i]);
        }
        _updateSequence();
    }

    function transferLiquidityIn(uint8[] memory assetIds, uint256[] memory rawAmounts) external onlyLiquidityManager {
        uint256 length = assetIds.length;
        require(length > 0, "MTY"); // argument array is eMpTY
        require(assetIds.length == rawAmounts.length, "LEN"); // LENgth of 2 arguments does not match
        for (uint256 i = 0; i < length; i++) {
            Asset storage asset = _storage.assets[assetIds[i]];
            asset.spotLiquidity += asset.toWad(rawAmounts[i]);
            emit TransferLiquidity(msg.sender, address(this), assetIds[i], rawAmounts[i]);
        }
        _updateSequence();
    }
}

File 6 of 24 : LibChainedProxy.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "@openzeppelin/contracts/proxy/Proxy.sol";

struct ChainedProxyStorage {
    address next;
}

/**
 * @dev ChainedProxy is a chained version of EIP1967 proxy
 *
 * The ChainedProxy uses Transparent Proxy as the storage layer and all logic layers
 * use Proxy pattern which call the function if the logic contract has it or delegatecall
 * the next logic hop.
 */
library ChainedProxy {
    /**
     * @dev The storage slot of the ChainedProxy contract which defines the implementation for this proxy.
     * This is bytes32(uint256(keccak256('eip1967.chain.storage')) - 1)) and is validated in the constructor.
     */
    bytes32 internal constant CHAINED_PROXY_STORAGE_SLOT =
        0x7d64d9a819609fbacde989007c1c053753b68c3b56ddef912de84ba0f732e0a9;

    function chainedProxyStorage() internal pure returns (ChainedProxyStorage storage ds) {
        bytes32 slot = CHAINED_PROXY_STORAGE_SLOT;
        assembly {
            ds.slot := slot
        }
    }

    function next() internal view returns (address) {
        ChainedProxyStorage storage ds = chainedProxyStorage();
        return ds.next;
    }

    function replace(address newNextAddress) internal {
        ChainedProxyStorage storage ds = chainedProxyStorage();
        ds.next = newNextAddress;
    }
}

File 7 of 24 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.0;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
 * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() initializer {}
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        // If the contract is initializing we ignore whether _initialized is set in order to support multiple
        // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
        // contract may have been reentered.
        require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} modifier, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    function _isConstructor() private view returns (bool) {
        return !AddressUpgradeable.isContract(address(this));
    }
}

File 8 of 24 : SafeOwnableUpgradeable.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract SafeOwnableUpgradeable is OwnableUpgradeable {
    address internal _pendingOwner;

    event PrepareToTransferOwnership(address indexed pendingOwner);

    function __SafeOwnable_init() internal onlyInitializing {
        __Ownable_init();
    }

    function transferOwnership(address newOwner) public virtual override onlyOwner {
        require(newOwner != address(0), "O=0"); // Owner Is Zero
        require(newOwner != owner(), "O=O"); // Owner is the same as the old Owner
        _pendingOwner = newOwner;
        emit PrepareToTransferOwnership(_pendingOwner);
    }

    function takeOwnership() public virtual {
        require(_msgSender() == _pendingOwner, "SND"); // SeNDer is not authorized
        _transferOwnership(_pendingOwner);
        _pendingOwner = address(0);
    }

    function renounceOwnership() public virtual override onlyOwner {
        _pendingOwner = address(0);
        _transferOwnership(address(0));
    }
}

File 9 of 24 : LibSubAccount.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "../core/Types.sol";

/**
 * SubAccountId
 *         96             88        80       72        0
 * +---------+--------------+---------+--------+--------+
 * | Account | collateralId | assetId | isLong | unused |
 * +---------+--------------+---------+--------+--------+
 */
library LibSubAccount {
    bytes32 constant SUB_ACCOUNT_ID_FORBIDDEN_BITS = bytes32(uint256(0xffffffffffffffffff));

    function getSubAccountOwner(bytes32 subAccountId) internal pure returns (address account) {
        account = address(uint160(uint256(subAccountId) >> 96));
    }

    function getSubAccountCollateralId(bytes32 subAccountId) internal pure returns (uint8) {
        return uint8(uint256(subAccountId) >> 88);
    }

    function isLong(bytes32 subAccountId) internal pure returns (bool) {
        return uint8((uint256(subAccountId) >> 72)) > 0;
    }

    struct DecodedSubAccountId {
        address account;
        uint8 collateralId;
        uint8 assetId;
        bool isLong;
    }

    function decodeSubAccountId(bytes32 subAccountId) internal pure returns (DecodedSubAccountId memory decoded) {
        require((subAccountId & SUB_ACCOUNT_ID_FORBIDDEN_BITS) == 0, "AID"); // bad subAccount ID
        decoded.account = address(uint160(uint256(subAccountId) >> 96));
        decoded.collateralId = uint8(uint256(subAccountId) >> 88);
        decoded.assetId = uint8(uint256(subAccountId) >> 80);
        decoded.isLong = uint8((uint256(subAccountId) >> 72)) > 0;
    }
}

File 10 of 24 : LibAsset.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";

import "../interfaces/IWETH9.sol";
import "../interfaces/INativeUnwrapper.sol";
import "../libraries/LibMath.sol";
import "../core/Types.sol";

library LibAsset {
    using LibMath for uint256;
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using SafeMathUpgradeable for uint256;

    function transferOut(
        Asset storage token,
        address recipient,
        uint256 rawAmount,
        address weth,
        address nativeUnwrapper
    ) internal {
        if (token.tokenAddress == weth) {
            IWETH(weth).transfer(nativeUnwrapper, rawAmount);
            INativeUnwrapper(nativeUnwrapper).unwrap(payable(recipient), rawAmount);
        } else {
            IERC20Upgradeable(token.tokenAddress).safeTransfer(recipient, rawAmount);
        }
    }

    function issueMuxToken(
        Asset storage token,
        address recipient,
        uint256 muxTokenAmount
    ) internal {
        IERC20Upgradeable(token.muxTokenAddress).safeTransfer(recipient, muxTokenAmount);
    }

    function toWad(Asset storage token, uint256 rawAmount) internal view returns (uint96) {
        return (rawAmount * (10**(18 - token.decimals))).safeUint96();
    }

    function toRaw(Asset storage token, uint96 wadAmount) internal view returns (uint256) {
        return uint256(wadAmount) / 10**(18 - token.decimals);
    }

    // is a usdt, usdc, ...
    function isStable(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_IS_STABLE) != 0;
    }

    // can call addLiquidity and removeLiquidity with this token
    function canAddRemoveLiquidity(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_CAN_ADD_REMOVE_LIQUIDITY) != 0;
    }

    // allowed to be assetId
    function isTradable(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_IS_TRADABLE) != 0;
    }

    // can open position
    function isOpenable(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_IS_OPENABLE) != 0;
    }

    // allow shorting this asset
    function isShortable(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_IS_SHORTABLE) != 0;
    }

    // take profit will get stable coin
    function useStableTokenForProfit(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_USE_STABLE_TOKEN_FOR_PROFIT) != 0;
    }

    // allowed to be assetId and collateralId
    function isEnabled(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_IS_ENABLED) != 0;
    }

    // assetPrice is always 1 unless volatility exceeds strictStableDeviation
    function isStrictStable(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_IS_STRICT_STABLE) != 0;
    }
}

File 11 of 24 : Types.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

struct LiquidityPoolStorage {
    // slot
    address orderBook;
    // slot
    address mlp;
    // slot
    address liquidityManager;
    // slot
    address weth;
    // slot
    uint128 _reserved1;
    uint32 shortFundingBaseRate8H; // 1e5
    uint32 shortFundingLimitRate8H; // 1e5
    uint32 fundingInterval; // 1e0
    uint32 lastFundingTime; // 1e0
    // slot
    uint32 _reserved2;
    // slot
    Asset[] assets;
    // slot
    mapping(bytes32 => SubAccount) accounts;
    // slot
    mapping(address => bytes32) _reserved3;
    // slot
    address _reserved4;
    uint96 _reserved5;
    // slot
    uint96 mlpPriceLowerBound; // safeguard against mlp price attacks
    uint96 mlpPriceUpperBound; // safeguard against mlp price attacks
    uint32 liquidityBaseFeeRate; // 1e5
    uint32 liquidityDynamicFeeRate; // 1e5
    // slot
    address nativeUnwrapper;
    // a sequence number that changes when LiquidityPoolStorage updated. this helps to keep track the state of LiquidityPool.
    uint32 sequence; // 1e0. note: will be 0 after 0xffffffff
    uint32 strictStableDeviation; // 1e5. strictStable price is 1.0 if in this damping range
    uint32 brokerTransactions; // transaction count for broker gas rebates
    // slot
    address vault;
    uint96 brokerGasRebate; // the number of native tokens for broker gas rebates per transaction
    bytes32[50] _gap;
}

struct Asset {
    // slot
    // assets with the same symbol in different chains are the same asset. they shares the same muxToken. so debts of the same symbol
    // can be accumulated across chains (see Reader.AssetState.deduct). ex: ERC20(fBNB).symbol should be "BNB", so that BNBs of
    // different chains are the same.
    // since muxToken of all stable coins is the same and is calculated separately (see Reader.ChainState.stableDeduct), stable coin
    // symbol can be different (ex: "USDT", "USDT.e" and "fUSDT").
    bytes32 symbol;
    // slot
    address tokenAddress; // erc20.address
    uint8 id;
    uint8 decimals; // erc20.decimals
    uint56 flags; // a bitset of ASSET_*
    uint24 _flagsPadding;
    // slot
    uint32 initialMarginRate; // 1e5
    uint32 maintenanceMarginRate; // 1e5
    uint32 minProfitRate; // 1e5
    uint32 minProfitTime; // 1e0
    uint32 positionFeeRate; // 1e5
    // note: 96 bits remaining
    // slot
    address referenceOracle;
    uint32 referenceDeviation; // 1e5
    uint8 referenceOracleType;
    uint32 halfSpread; // 1e5
    // note: 24 bits remaining
    // slot
    uint128 _reserved1;
    uint128 _reserved2;
    // slot
    uint96 collectedFee;
    uint32 _reserved3;
    uint96 spotLiquidity;
    // note: 32 bits remaining
    // slot
    uint96 maxLongPositionSize;
    uint96 totalLongPosition;
    // note: 64 bits remaining
    // slot
    uint96 averageLongPrice;
    uint96 maxShortPositionSize;
    // note: 64 bits remaining
    // slot
    uint96 totalShortPosition;
    uint96 averageShortPrice;
    // note: 64 bits remaining
    // slot, less used
    address muxTokenAddress; // muxToken.address. all stable coins share the same muxTokenAddress
    uint32 spotWeight; // 1e0
    uint32 longFundingBaseRate8H; // 1e5
    uint32 longFundingLimitRate8H; // 1e5
    // slot
    uint128 longCumulativeFundingRate; // Σ_t fundingRate_t
    uint128 shortCumulativeFunding; // Σ_t fundingRate_t * indexPrice_t
}

uint32 constant FUNDING_PERIOD = 3600 * 8;

uint56 constant ASSET_IS_STABLE = 0x00000000000001; // is a usdt, usdc, ...
uint56 constant ASSET_CAN_ADD_REMOVE_LIQUIDITY = 0x00000000000002; // can call addLiquidity and removeLiquidity with this token
uint56 constant ASSET_IS_TRADABLE = 0x00000000000100; // allowed to be assetId
uint56 constant ASSET_IS_OPENABLE = 0x00000000010000; // can open position
uint56 constant ASSET_IS_SHORTABLE = 0x00000001000000; // allow shorting this asset
uint56 constant ASSET_USE_STABLE_TOKEN_FOR_PROFIT = 0x00000100000000; // take profit will get stable coin
uint56 constant ASSET_IS_ENABLED = 0x00010000000000; // allowed to be assetId and collateralId
uint56 constant ASSET_IS_STRICT_STABLE = 0x01000000000000; // assetPrice is always 1 unless volatility exceeds strictStableDeviation

struct SubAccount {
    // slot
    uint96 collateral;
    uint96 size;
    uint32 lastIncreasedTime;
    // slot
    uint96 entryPrice;
    uint128 entryFunding; // entry longCumulativeFundingRate for long position. entry shortCumulativeFunding for short position
}

enum ReferenceOracleType {
    None,
    Chainlink
}

File 12 of 24 : Events.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

contract Events {
    event UpdateSequence(uint32 sequence);

    //////////////////////////////////////////////////////////////////////////////////////
    //                                   trade
    struct OpenPositionArgs {
        bytes32 subAccountId;
        uint8 collateralId;
        bool isLong;
        uint96 amount;
        uint96 assetPrice;
        uint96 collateralPrice;
        uint96 newEntryPrice;
        uint96 feeUsd;
        uint96 remainPosition;
        uint96 remainCollateral;
    }
    event OpenPosition(address indexed trader, uint8 indexed assetId, OpenPositionArgs args);
    struct ClosePositionArgs {
        bytes32 subAccountId;
        uint8 collateralId;
        uint8 profitAssetId;
        bool isLong;
        uint96 amount;
        uint96 assetPrice;
        uint96 collateralPrice;
        uint96 profitAssetPrice;
        uint96 feeUsd;
        bool hasProfit;
        uint96 pnlUsd;
        uint96 remainPosition;
        uint96 remainCollateral;
    }
    event ClosePosition(address indexed trader, uint8 indexed assetId, ClosePositionArgs args);
    struct LiquidateArgs {
        bytes32 subAccountId;
        uint8 collateralId;
        uint8 profitAssetId;
        bool isLong;
        uint96 amount;
        uint96 assetPrice;
        uint96 collateralPrice;
        uint96 profitAssetPrice;
        uint96 feeUsd;
        bool hasProfit;
        uint96 pnlUsd;
        uint96 remainCollateral;
    }
    event Liquidate(address indexed trader, uint8 indexed assetId, LiquidateArgs args);
    struct WithdrawProfitArgs {
        bytes32 subAccountId;
        uint8 collateralId;
        uint8 profitAssetId;
        bool isLong;
        uint256 withdrawRawAmount;
        uint96 assetPrice;
        uint96 collateralPrice;
        uint96 profitAssetPrice;
        uint96 entryPrice;
        uint96 feeUsd;
    }
    event WithdrawProfit(address indexed trader, uint8 indexed assetId, WithdrawProfitArgs args);
    event CollectedFee(uint8 tokenId, uint96 fee);
    event ClaimBrokerGasRebate(address indexed receiver, uint32 transactions, uint256 rawAmount);

    //////////////////////////////////////////////////////////////////////////////////////
    //                                   liquidity
    event AddLiquidity(
        address indexed trader,
        uint8 indexed tokenId,
        uint96 tokenPrice,
        uint96 mlpPrice,
        uint96 mlpAmount,
        uint96 fee
    );
    event RemoveLiquidity(
        address indexed trader,
        uint8 indexed tokenId,
        uint96 tokenPrice,
        uint96 mlpPrice,
        uint96 mlpAmount,
        uint96 fee
    );
    event UpdateFundingRate(
        uint8 indexed tokenId,
        uint32 longFundingRate, // 1e5
        uint128 longCumulativeFundingRate, // Σ_t fundingRate_t
        uint32 shortFundingRate, // 1e5
        uint128 shortCumulativeFunding // Σ_t fundingRate_t * indexPrice_t
    );
    event IssueMuxToken(
        uint8 indexed tokenId, // if isStable, tokenId will always be 0
        bool isStable,
        uint96 muxTokenAmount
    );
    event RedeemMuxToken(address trader, uint8 tokenId, uint96 muxTokenAmount);
    event Rebalance(
        address indexed rebalancer,
        uint8 tokenId0,
        uint8 tokenId1,
        uint96 price0,
        uint96 price1,
        uint96 rawAmount0,
        uint96 rawAmount1
    );

    //////////////////////////////////////////////////////////////////////////////////////
    //                                   admin
    event AddAsset(
        uint8 indexed id,
        bytes32 symbol,
        uint8 decimals,
        bool isStable,
        address tokenAddress,
        address muxTokenAddress
    );
    event SetAssetSymbol(uint8 indexed assetId, bytes32 symbol);
    event SetAssetParams(
        uint8 indexed assetId,
        uint32 newInitialMarginRate,
        uint32 newMaintenanceMarginRate,
        uint32 newPositionFeeRate,
        uint32 newMinProfitRate,
        uint32 newMinProfitTime,
        uint96 newMaxLongPositionSize,
        uint96 newMaxShortPositionSize,
        uint32 newSpotWeight,
        uint32 newHalfSpread
    );
    event SetAssetFlags(uint8 indexed assetId, uint56 oldFlags, uint56 newFlags);
    event SetReferenceOracle(
        uint8 indexed assetId,
        uint8 referenceOracleType,
        address referenceOracle,
        uint32 referenceDeviation
    );
    event SetFundingParams(uint8 indexed assetId, uint32 newBaseRate8H, uint32 newLimitRate8H);
    event SetFundingInterval(uint32 oldFundingInterval, uint32 newFundingInterval);
    event SetMlpPriceRange(uint96 newLowerBound, uint96 newUpperBound);
    event SetLiquidityFee(uint32 newLiquidityBaseFeeRate, uint32 newLiquidityDynamicFeeRate);
    event SetStrictStableDeviation(uint32 newStrictStableDeviation);
    event SetBrokerGasRebate(uint96 newBrokerGasRebate);
    event WithdrawCollectedFee(uint8 indexed assetId, uint96 collectedFee);
    event TransferLiquidity(address indexed sender, address indexed recipient, uint8 assetId, uint256 amount);
}

File 13 of 24 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.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
     * ====
     *
     * [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://diligence.consensys.net/posts/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.5.11/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 functionCall(target, data, "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");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(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) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason 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 {
            // 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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 14 of 24 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.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 OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing 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);
    }

    /**
     * This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 15 of 24 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @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 ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

    /**
     * This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 16 of 24 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @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);

    /**
     * @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);
}

File 17 of 24 : SafeERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.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;

    function safeTransfer(
        IERC20Upgradeable token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    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));
    }

    function safeIncreaseAllowance(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    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");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    /**
     * @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");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 18 of 24 : SafeMathUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
library SafeMathUpgradeable {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

File 19 of 24 : IWETH9.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

interface IWETH {
    function deposit() external payable;

    function transfer(address to, uint256 value) external returns (bool);

    function withdraw(uint256) external;
}

File 20 of 24 : INativeUnwrapper.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

interface INativeUnwrapper {
    function unwrap(address payable to, uint256 rawAmount) external;
}

File 21 of 24 : LibMath.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

library LibMath {
    function min(uint96 a, uint96 b) internal pure returns (uint96) {
        return a <= b ? a : b;
    }

    function min32(uint32 a, uint32 b) internal pure returns (uint32) {
        return a <= b ? a : b;
    }

    function max32(uint32 a, uint32 b) internal pure returns (uint32) {
        return a >= b ? a : b;
    }

    function wmul(uint256 a, uint256 b) internal pure returns (uint256) {
        return (a * b) / 1e18;
    }

    function rmul(uint256 a, uint256 b) internal pure returns (uint256) {
        return (a * b) / 1e5;
    }

    function wdiv(uint256 a, uint256 b) internal pure returns (uint256) {
        return (a * 1e18) / b;
    }

    function safeUint32(uint256 n) internal pure returns (uint32) {
        require(n <= type(uint32).max, "O32"); // uint32 Overflow
        return uint32(n);
    }

    function safeUint96(uint256 n) internal pure returns (uint96) {
        require(n <= type(uint96).max, "O96"); // uint96 Overflow
        return uint96(n);
    }

    function safeUint128(uint256 n) internal pure returns (uint128) {
        require(n <= type(uint128).max, "O12"); // uint128 Overflow
        return uint128(n);
    }
}

File 22 of 24 : LibReferenceOracle.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "../core/Types.sol";
import "./LibMath.sol";
import "./LibAsset.sol";

interface IChainlink {
    function latestAnswer() external view returns (int256);

    function latestTimestamp() external view returns (uint256);

    function latestRound() external view returns (uint256);

    function getAnswer(uint256 roundId) external view returns (int256);

    function getTimestamp(uint256 roundId) external view returns (uint256);

    event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);
    event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
}

interface IChainlinkV3 {
    function decimals() external view returns (uint8);

    function description() external view returns (string memory);

    function version() external view returns (uint256);

    function getRoundData(uint80 _roundId)
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        );

    function latestRoundData()
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        );
}

interface IChainlinkV2V3 is IChainlink, IChainlinkV3 {}

enum SpreadType {
    Ask,
    Bid
}

library LibReferenceOracle {
    using LibMath for uint256;
    using LibMath for uint96;
    using LibAsset for Asset;

    // indicate that the asset price is too far away from reference oracle
    event AssetPriceOutOfRange(uint8 assetId, uint96 price, uint96 referencePrice, uint32 deviation);

    /**
     * @dev Check oracle parameters before set
     */
    function checkParameters(
        ReferenceOracleType referenceOracleType,
        address referenceOracle,
        uint32 referenceDeviation
    ) internal view {
        require(referenceDeviation <= 1e5, "D>1"); // %deviation > 100%
        if (referenceOracleType == ReferenceOracleType.Chainlink) {
            IChainlinkV2V3 o = IChainlinkV2V3(referenceOracle);
            require(o.decimals() == 8, "!D8"); // we only support decimals = 8
            require(o.latestAnswer() > 0, "P=0"); // oracle Price <= 0
        }
    }

    /**
     * @dev Truncate price if the error is too large
     */
    function checkPrice(
        LiquidityPoolStorage storage pool,
        Asset storage asset,
        uint96 price
    ) internal returns (uint96) {
        require(price != 0, "P=0"); // broker price = 0

        // truncate price if the error is too large
        if (ReferenceOracleType(asset.referenceOracleType) == ReferenceOracleType.Chainlink) {
            uint96 ref = _readChainlink(asset.referenceOracle);
            price = _truncatePrice(asset, price, ref);
        }

        // strict stable dampener
        if (asset.isStrictStable()) {
            uint256 delta = price > 1e18 ? price - 1e18 : 1e18 - price;
            uint256 dampener = uint256(pool.strictStableDeviation) * 1e13; // 1e5 => 1e18
            if (delta <= dampener) {
                price = 1e18;
            }
        }

        return price;
    }

    /**
     * @dev check price and add spread, where spreadType should be:
     *
     *      subAccount.isLong   openPosition   closePosition   addLiquidity   removeLiquidity
     *      long                ask            bid
     *      short               bid            ask
     *      N/A                                                bid            ask
     */
    function checkPriceWithSpread(
        LiquidityPoolStorage storage pool,
        Asset storage asset,
        uint96 price,
        SpreadType spreadType
    ) internal returns (uint96) {
        price = checkPrice(pool, asset, price);
        price = _addSpread(asset, price, spreadType);
        return price;
    }

    function _readChainlink(address referenceOracle) internal view returns (uint96) {
        int256 ref = IChainlinkV2V3(referenceOracle).latestAnswer();
        require(ref > 0, "P=0"); // oracle Price <= 0
        ref *= 1e10; // decimals 8 => 18
        return uint256(ref).safeUint96();
    }

    function _truncatePrice(
        Asset storage asset,
        uint96 price,
        uint96 ref
    ) private returns (uint96) {
        if (asset.referenceDeviation == 0) {
            return ref;
        }
        uint256 deviation = uint256(ref).rmul(asset.referenceDeviation);
        uint96 bound = (uint256(ref) - deviation).safeUint96();
        if (price < bound) {
            emit AssetPriceOutOfRange(asset.id, price, ref, asset.referenceDeviation);
            price = bound;
        }
        bound = (uint256(ref) + deviation).safeUint96();
        if (price > bound) {
            emit AssetPriceOutOfRange(asset.id, price, ref, asset.referenceDeviation);
            price = bound;
        }
        return price;
    }

    function _addSpread(
        Asset storage asset,
        uint96 price,
        SpreadType spreadType
    ) private view returns (uint96) {
        if (asset.halfSpread == 0) {
            return price;
        }
        uint96 halfSpread = uint256(price).rmul(asset.halfSpread).safeUint96();
        if (spreadType == SpreadType.Bid) {
            require(price > halfSpread, "P=0"); // Price - halfSpread = 0. impossible
            return price - halfSpread;
        } else {
            return price + halfSpread;
        }
    }
}

File 23 of 24 : Account.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";

import "../libraries/LibSubAccount.sol";
import "../libraries/LibMath.sol";
import "../libraries/LibAsset.sol";
import "../libraries/LibReferenceOracle.sol";
import "./Storage.sol";

contract Account is Storage {
    using LibMath for uint256;
    using LibSubAccount for bytes32;
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using SafeMathUpgradeable for uint256;
    using LibAsset for Asset;

    event DepositCollateral(
        bytes32 indexed subAccountId,
        address indexed trader,
        uint8 collateralId,
        uint256 rawAmount,
        uint96 wadAmount
    );
    event WithdrawCollateral(
        bytes32 indexed subAccountId,
        address indexed trader,
        uint8 collateralId,
        uint256 rawAmount,
        uint96 wadAmount
    );

    function depositCollateral(
        bytes32 subAccountId,
        uint256 rawAmount // NOTE: OrderBook SHOULD transfer rawAmount collateral to LiquidityPool
    ) external onlyOrderBook {
        LibSubAccount.DecodedSubAccountId memory decoded = subAccountId.decodeSubAccountId();
        require(decoded.account != address(0), "T=0"); // Trader address is zero
        require(_hasAsset(decoded.collateralId), "LST"); // the asset is not LiSTed
        require(_hasAsset(decoded.assetId), "LST"); // the asset is not LiSTed
        require(rawAmount != 0, "A=0"); // Amount Is Zero

        SubAccount storage subAccount = _storage.accounts[subAccountId];
        Asset storage asset = _storage.assets[decoded.assetId];
        Asset storage collateral = _storage.assets[decoded.collateralId];
        require(asset.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(collateral.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        uint96 wadAmount = collateral.toWad(rawAmount);
        subAccount.collateral += wadAmount;

        emit DepositCollateral(subAccountId, decoded.account, decoded.collateralId, rawAmount, wadAmount);
        _updateSequence();
    }

    function withdrawCollateral(
        bytes32 subAccountId,
        uint256 rawAmount,
        uint96 collateralPrice,
        uint96 assetPrice
    ) external onlyOrderBook {
        require(rawAmount != 0, "A=0"); // Amount Is Zero
        LibSubAccount.DecodedSubAccountId memory decoded = subAccountId.decodeSubAccountId();
        require(decoded.account != address(0), "T=0"); // Trader address is zero
        require(_hasAsset(decoded.collateralId), "LST"); // the asset is not LiSTed
        require(_hasAsset(decoded.assetId), "LST"); // the asset is not LiSTed

        Asset storage asset = _storage.assets[decoded.assetId];
        Asset storage collateral = _storage.assets[decoded.collateralId];
        require(asset.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(collateral.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        SubAccount storage subAccount = _storage.accounts[subAccountId];
        assetPrice = LibReferenceOracle.checkPrice(_storage, asset, assetPrice);
        collateralPrice = LibReferenceOracle.checkPrice(_storage, collateral, collateralPrice);

        // fee & funding
        uint96 feeUsd = _getFundingFeeUsd(subAccount, asset, decoded.isLong, assetPrice);
        if (subAccount.size > 0) {
            _updateEntryFunding(subAccount, asset, decoded.isLong);
        }
        {
            uint96 feeCollateral = uint256(feeUsd).wdiv(collateralPrice).safeUint96();
            require(subAccount.collateral >= feeCollateral, "FEE"); // remaining collateral can not pay FEE
            subAccount.collateral -= feeCollateral;
            collateral.collectedFee += feeCollateral;
            collateral.spotLiquidity += feeCollateral;
            emit CollectedFee(decoded.collateralId, feeCollateral);
        }
        // withdraw
        uint96 wadAmount = collateral.toWad(rawAmount);
        require(subAccount.collateral >= wadAmount, "C<W"); // Collateral can not pay fee or is less than the amount requested for Withdrawal
        subAccount.collateral = subAccount.collateral - wadAmount;
        collateral.transferOut(decoded.account, rawAmount, _storage.weth, _storage.nativeUnwrapper);
        require(_isAccountImSafe(subAccount, decoded.assetId, decoded.isLong, collateralPrice, assetPrice), "!IM");

        emit WithdrawCollateral(subAccountId, decoded.account, decoded.collateralId, rawAmount, wadAmount);
        _updateSequence();
    }

    function withdrawAllCollateral(bytes32 subAccountId) external onlyOrderBook {
        LibSubAccount.DecodedSubAccountId memory decoded = subAccountId.decodeSubAccountId();
        SubAccount storage subAccount = _storage.accounts[subAccountId];
        require(subAccount.size == 0, "S>0"); // position Size should be Zero
        require(subAccount.collateral > 0, "C=0"); // Collateral Is Zero

        Asset storage asset = _storage.assets[decoded.assetId];
        Asset storage collateral = _storage.assets[decoded.collateralId];
        require(asset.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(collateral.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        uint96 wadAmount = subAccount.collateral;
        uint256 rawAmount = collateral.toRaw(wadAmount);
        subAccount.collateral = 0;
        collateral.transferOut(decoded.account, rawAmount, _storage.weth, _storage.nativeUnwrapper);
        emit WithdrawCollateral(subAccountId, decoded.account, decoded.collateralId, rawAmount, wadAmount);
        _updateSequence();
    }

    function _positionPnlUsd(
        Asset storage asset,
        SubAccount storage subAccount,
        bool isLong,
        uint96 amount,
        uint96 assetPrice
    ) internal view returns (bool hasProfit, uint96 pnlUsd) {
        if (amount == 0) {
            return (false, 0);
        }
        require(assetPrice > 0, "P=0"); // Price Is Zero
        hasProfit = isLong ? assetPrice > subAccount.entryPrice : assetPrice < subAccount.entryPrice;
        uint96 priceDelta = assetPrice >= subAccount.entryPrice
            ? assetPrice - subAccount.entryPrice
            : subAccount.entryPrice - assetPrice;
        if (
            hasProfit &&
            _blockTimestamp() < subAccount.lastIncreasedTime + asset.minProfitTime &&
            priceDelta < uint256(subAccount.entryPrice).rmul(asset.minProfitRate).safeUint96()
        ) {
            hasProfit = false;
            return (false, 0);
        }
        pnlUsd = uint256(priceDelta).wmul(amount).safeUint96();
    }

    // NOTE: settle funding by modify subAccount.collateral before this function
    function _isAccountImSafe(
        SubAccount storage subAccount,
        uint32 assetId,
        bool isLong,
        uint96 collateralPrice,
        uint96 assetPrice
    ) internal view returns (bool) {
        Asset storage asset = _storage.assets[assetId];
        (bool hasProfit, uint96 pnlUsd) = _positionPnlUsd(asset, subAccount, isLong, subAccount.size, assetPrice);
        return _isAccountSafe(subAccount, collateralPrice, assetPrice, asset.initialMarginRate, hasProfit, pnlUsd, 0);
    }

    // NOTE: settle funding by modify subAccount.collateral before this function
    function _isAccountMmSafe(
        SubAccount storage subAccount,
        uint32 assetId,
        bool isLong,
        uint96 collateralPrice,
        uint96 assetPrice
    ) internal view returns (bool) {
        Asset storage asset = _storage.assets[assetId];
        (bool hasProfit, uint96 pnlUsd) = _positionPnlUsd(asset, subAccount, isLong, subAccount.size, assetPrice);
        return
            _isAccountSafe(subAccount, collateralPrice, assetPrice, asset.maintenanceMarginRate, hasProfit, pnlUsd, 0);
    }

    function _isAccountSafe(
        SubAccount storage subAccount,
        uint96 collateralPrice,
        uint96 assetPrice,
        uint32 marginRate,
        bool hasProfit,
        uint96 pnlUsd,
        uint96 fundingFee // fundingFee = 0 if subAccount.collateral was modified
    ) internal view returns (bool) {
        uint256 thresholdUsd = (uint256(subAccount.size) * uint256(assetPrice) * uint256(marginRate)) / 1e18 / 1e5;
        thresholdUsd += fundingFee;
        uint256 collateralUsd = uint256(subAccount.collateral).wmul(collateralPrice);
        // break down "collateralUsd +/- pnlUsd >= thresholdUsd >= 0"
        if (hasProfit) {
            return collateralUsd + pnlUsd >= thresholdUsd;
        } else {
            return collateralUsd >= thresholdUsd + pnlUsd;
        }
    }

    function _getFeeUsd(
        SubAccount storage subAccount,
        Asset storage asset,
        bool isLong,
        uint96 amount,
        uint96 assetPrice
    ) internal view returns (uint96) {
        return _getFundingFeeUsd(subAccount, asset, isLong, assetPrice) + _getPositionFeeUsd(asset, amount, assetPrice);
    }

    function _getFundingFeeUsd(
        SubAccount storage subAccount,
        Asset storage asset,
        bool isLong,
        uint96 assetPrice
    ) internal view returns (uint96) {
        if (subAccount.size == 0) {
            return 0;
        }
        uint256 cumulativeFunding;
        if (isLong) {
            cumulativeFunding = asset.longCumulativeFundingRate - subAccount.entryFunding;
            cumulativeFunding = cumulativeFunding.wmul(assetPrice);
        } else {
            cumulativeFunding = asset.shortCumulativeFunding - subAccount.entryFunding;
        }
        return cumulativeFunding.wmul(subAccount.size).safeUint96();
    }

    function _getPositionFeeUsd(
        Asset storage asset,
        uint96 amount,
        uint96 assetPrice
    ) internal view returns (uint96) {
        uint256 feeUsd = ((uint256(assetPrice) * uint256(asset.positionFeeRate)) * uint256(amount)) / 1e5 / 1e18;
        return feeUsd.safeUint96();
    }

    // note: you can skip this function if newPositionSize > 0
    function _updateEntryFunding(
        SubAccount storage subAccount,
        Asset storage asset,
        bool isLong
    ) internal {
        if (isLong) {
            subAccount.entryFunding = asset.longCumulativeFundingRate;
        } else {
            subAccount.entryFunding = asset.shortCumulativeFunding;
        }
    }
}

File 24 of 24 : Proxy.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/Proxy.sol)

pragma solidity ^0.8.0;

/**
 * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
 * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
 * be specified by overriding the virtual {_implementation} function.
 *
 * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
 * different contract through the {_delegate} function.
 *
 * The success and return data of the delegated call will be returned back to the caller of the proxy.
 */
abstract contract Proxy {
    /**
     * @dev Delegates the current call to `implementation`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _delegate(address implementation) internal virtual {
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }

    /**
     * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
     * and {_fallback} should delegate.
     */
    function _implementation() internal view virtual returns (address);

    /**
     * @dev Delegates the current call to the address returned by `_implementation()`.
     *
     * This function does not return to its internall call site, it will return directly to the external caller.
     */
    function _fallback() internal virtual {
        _beforeFallback();
        _delegate(_implementation());
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
     * function in the contract matches the call data.
     */
    fallback() external payable virtual {
        _fallback();
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
     * is empty.
     */
    receive() external payable virtual {
        _fallback();
    }

    /**
     * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
     * call, or as part of the Solidity `fallback` or `receive` functions.
     *
     * If overriden should call `super._beforeFallback()`.
     */
    function _beforeFallback() internal virtual {}
}

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

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"id","type":"uint8"},{"indexed":false,"internalType":"bytes32","name":"symbol","type":"bytes32"},{"indexed":false,"internalType":"uint8","name":"decimals","type":"uint8"},{"indexed":false,"internalType":"bool","name":"isStable","type":"bool"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"address","name":"muxTokenAddress","type":"address"}],"name":"AddAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint8","name":"tokenId","type":"uint8"},{"indexed":false,"internalType":"uint96","name":"tokenPrice","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"mlpPrice","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"mlpAmount","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"fee","type":"uint96"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint32","name":"transactions","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"rawAmount","type":"uint256"}],"name":"ClaimBrokerGasRebate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"components":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint8","name":"collateralId","type":"uint8"},{"internalType":"uint8","name":"profitAssetId","type":"uint8"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"uint96","name":"assetPrice","type":"uint96"},{"internalType":"uint96","name":"collateralPrice","type":"uint96"},{"internalType":"uint96","name":"profitAssetPrice","type":"uint96"},{"internalType":"uint96","name":"feeUsd","type":"uint96"},{"internalType":"bool","name":"hasProfit","type":"bool"},{"internalType":"uint96","name":"pnlUsd","type":"uint96"},{"internalType":"uint96","name":"remainPosition","type":"uint96"},{"internalType":"uint96","name":"remainCollateral","type":"uint96"}],"indexed":false,"internalType":"struct Events.ClosePositionArgs","name":"args","type":"tuple"}],"name":"ClosePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"tokenId","type":"uint8"},{"indexed":false,"internalType":"uint96","name":"fee","type":"uint96"}],"name":"CollectedFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"uint8","name":"collateralId","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"rawAmount","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"wadAmount","type":"uint96"}],"name":"DepositCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"tokenId","type":"uint8"},{"indexed":false,"internalType":"bool","name":"isStable","type":"bool"},{"indexed":false,"internalType":"uint96","name":"muxTokenAmount","type":"uint96"}],"name":"IssueMuxToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"components":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint8","name":"collateralId","type":"uint8"},{"internalType":"uint8","name":"profitAssetId","type":"uint8"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"uint96","name":"assetPrice","type":"uint96"},{"internalType":"uint96","name":"collateralPrice","type":"uint96"},{"internalType":"uint96","name":"profitAssetPrice","type":"uint96"},{"internalType":"uint96","name":"feeUsd","type":"uint96"},{"internalType":"bool","name":"hasProfit","type":"bool"},{"internalType":"uint96","name":"pnlUsd","type":"uint96"},{"internalType":"uint96","name":"remainCollateral","type":"uint96"}],"indexed":false,"internalType":"struct Events.LiquidateArgs","name":"args","type":"tuple"}],"name":"Liquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"components":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint8","name":"collateralId","type":"uint8"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"uint96","name":"assetPrice","type":"uint96"},{"internalType":"uint96","name":"collateralPrice","type":"uint96"},{"internalType":"uint96","name":"newEntryPrice","type":"uint96"},{"internalType":"uint96","name":"feeUsd","type":"uint96"},{"internalType":"uint96","name":"remainPosition","type":"uint96"},{"internalType":"uint96","name":"remainCollateral","type":"uint96"}],"indexed":false,"internalType":"struct Events.OpenPositionArgs","name":"args","type":"tuple"}],"name":"OpenPosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"PrepareToTransferOwnership","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rebalancer","type":"address"},{"indexed":false,"internalType":"uint8","name":"tokenId0","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"tokenId1","type":"uint8"},{"indexed":false,"internalType":"uint96","name":"price0","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"price1","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"rawAmount0","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"rawAmount1","type":"uint96"}],"name":"Rebalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"uint8","name":"tokenId","type":"uint8"},{"indexed":false,"internalType":"uint96","name":"muxTokenAmount","type":"uint96"}],"name":"RedeemMuxToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint8","name":"tokenId","type":"uint8"},{"indexed":false,"internalType":"uint96","name":"tokenPrice","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"mlpPrice","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"mlpAmount","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"fee","type":"uint96"}],"name":"RemoveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"indexed":false,"internalType":"uint56","name":"oldFlags","type":"uint56"},{"indexed":false,"internalType":"uint56","name":"newFlags","type":"uint56"}],"name":"SetAssetFlags","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"indexed":false,"internalType":"uint32","name":"newInitialMarginRate","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newMaintenanceMarginRate","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newPositionFeeRate","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newMinProfitRate","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newMinProfitTime","type":"uint32"},{"indexed":false,"internalType":"uint96","name":"newMaxLongPositionSize","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"newMaxShortPositionSize","type":"uint96"},{"indexed":false,"internalType":"uint32","name":"newSpotWeight","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newHalfSpread","type":"uint32"}],"name":"SetAssetParams","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"indexed":false,"internalType":"bytes32","name":"symbol","type":"bytes32"}],"name":"SetAssetSymbol","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint96","name":"newBrokerGasRebate","type":"uint96"}],"name":"SetBrokerGasRebate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"oldFundingInterval","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newFundingInterval","type":"uint32"}],"name":"SetFundingInterval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"indexed":false,"internalType":"uint32","name":"newBaseRate8H","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newLimitRate8H","type":"uint32"}],"name":"SetFundingParams","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"newLiquidityBaseFeeRate","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newLiquidityDynamicFeeRate","type":"uint32"}],"name":"SetLiquidityFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint96","name":"newLowerBound","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"newUpperBound","type":"uint96"}],"name":"SetMlpPriceRange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"referenceOracleType","type":"uint8"},{"indexed":false,"internalType":"address","name":"referenceOracle","type":"address"},{"indexed":false,"internalType":"uint32","name":"referenceDeviation","type":"uint32"}],"name":"SetReferenceOracle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"newStrictStableDeviation","type":"uint32"}],"name":"SetStrictStableDeviation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint8","name":"assetId","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"tokenId","type":"uint8"},{"indexed":false,"internalType":"uint32","name":"longFundingRate","type":"uint32"},{"indexed":false,"internalType":"uint128","name":"longCumulativeFundingRate","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"shortFundingRate","type":"uint32"},{"indexed":false,"internalType":"uint128","name":"shortCumulativeFunding","type":"uint128"}],"name":"UpdateFundingRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"sequence","type":"uint32"}],"name":"UpdateSequence","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"prevNextHop","type":"address"},{"indexed":false,"internalType":"address","name":"nextHop","type":"address"}],"name":"UpgradeChainedProxy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"uint8","name":"collateralId","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"rawAmount","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"wadAmount","type":"uint96"}],"name":"WithdrawCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"indexed":false,"internalType":"uint96","name":"collectedFee","type":"uint96"}],"name":"WithdrawCollectedFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"components":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint8","name":"collateralId","type":"uint8"},{"internalType":"uint8","name":"profitAssetId","type":"uint8"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint256","name":"withdrawRawAmount","type":"uint256"},{"internalType":"uint96","name":"assetPrice","type":"uint96"},{"internalType":"uint96","name":"collateralPrice","type":"uint96"},{"internalType":"uint96","name":"profitAssetPrice","type":"uint96"},{"internalType":"uint96","name":"entryPrice","type":"uint96"},{"internalType":"uint96","name":"feeUsd","type":"uint96"}],"indexed":false,"internalType":"struct Events.WithdrawProfitArgs","name":"args","type":"tuple"}],"name":"WithdrawProfit","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"uint8","name":"profitAssetId","type":"uint8"},{"internalType":"uint96","name":"collateralPrice","type":"uint96"},{"internalType":"uint96","name":"assetPrice","type":"uint96"},{"internalType":"uint96","name":"profitAssetPrice","type":"uint96"}],"name":"closePosition","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint256","name":"rawAmount","type":"uint256"}],"name":"depositCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAllAssetInfo","outputs":[{"components":[{"internalType":"bytes32","name":"symbol","type":"bytes32"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"uint56","name":"flags","type":"uint56"},{"internalType":"uint24","name":"_flagsPadding","type":"uint24"},{"internalType":"uint32","name":"initialMarginRate","type":"uint32"},{"internalType":"uint32","name":"maintenanceMarginRate","type":"uint32"},{"internalType":"uint32","name":"minProfitRate","type":"uint32"},{"internalType":"uint32","name":"minProfitTime","type":"uint32"},{"internalType":"uint32","name":"positionFeeRate","type":"uint32"},{"internalType":"address","name":"referenceOracle","type":"address"},{"internalType":"uint32","name":"referenceDeviation","type":"uint32"},{"internalType":"uint8","name":"referenceOracleType","type":"uint8"},{"internalType":"uint32","name":"halfSpread","type":"uint32"},{"internalType":"uint128","name":"_reserved1","type":"uint128"},{"internalType":"uint128","name":"_reserved2","type":"uint128"},{"internalType":"uint96","name":"collectedFee","type":"uint96"},{"internalType":"uint32","name":"_reserved3","type":"uint32"},{"internalType":"uint96","name":"spotLiquidity","type":"uint96"},{"internalType":"uint96","name":"maxLongPositionSize","type":"uint96"},{"internalType":"uint96","name":"totalLongPosition","type":"uint96"},{"internalType":"uint96","name":"averageLongPrice","type":"uint96"},{"internalType":"uint96","name":"maxShortPositionSize","type":"uint96"},{"internalType":"uint96","name":"totalShortPosition","type":"uint96"},{"internalType":"uint96","name":"averageShortPrice","type":"uint96"},{"internalType":"address","name":"muxTokenAddress","type":"address"},{"internalType":"uint32","name":"spotWeight","type":"uint32"},{"internalType":"uint32","name":"longFundingBaseRate8H","type":"uint32"},{"internalType":"uint32","name":"longFundingLimitRate8H","type":"uint32"},{"internalType":"uint128","name":"longCumulativeFundingRate","type":"uint128"},{"internalType":"uint128","name":"shortCumulativeFunding","type":"uint128"}],"internalType":"struct Asset[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"assetId","type":"uint8"}],"name":"getAssetAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"assetId","type":"uint8"}],"name":"getAssetInfo","outputs":[{"components":[{"internalType":"bytes32","name":"symbol","type":"bytes32"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"uint56","name":"flags","type":"uint56"},{"internalType":"uint24","name":"_flagsPadding","type":"uint24"},{"internalType":"uint32","name":"initialMarginRate","type":"uint32"},{"internalType":"uint32","name":"maintenanceMarginRate","type":"uint32"},{"internalType":"uint32","name":"minProfitRate","type":"uint32"},{"internalType":"uint32","name":"minProfitTime","type":"uint32"},{"internalType":"uint32","name":"positionFeeRate","type":"uint32"},{"internalType":"address","name":"referenceOracle","type":"address"},{"internalType":"uint32","name":"referenceDeviation","type":"uint32"},{"internalType":"uint8","name":"referenceOracleType","type":"uint8"},{"internalType":"uint32","name":"halfSpread","type":"uint32"},{"internalType":"uint128","name":"_reserved1","type":"uint128"},{"internalType":"uint128","name":"_reserved2","type":"uint128"},{"internalType":"uint96","name":"collectedFee","type":"uint96"},{"internalType":"uint32","name":"_reserved3","type":"uint32"},{"internalType":"uint96","name":"spotLiquidity","type":"uint96"},{"internalType":"uint96","name":"maxLongPositionSize","type":"uint96"},{"internalType":"uint96","name":"totalLongPosition","type":"uint96"},{"internalType":"uint96","name":"averageLongPrice","type":"uint96"},{"internalType":"uint96","name":"maxShortPositionSize","type":"uint96"},{"internalType":"uint96","name":"totalShortPosition","type":"uint96"},{"internalType":"uint96","name":"averageShortPrice","type":"uint96"},{"internalType":"address","name":"muxTokenAddress","type":"address"},{"internalType":"uint32","name":"spotWeight","type":"uint32"},{"internalType":"uint32","name":"longFundingBaseRate8H","type":"uint32"},{"internalType":"uint32","name":"longFundingLimitRate8H","type":"uint32"},{"internalType":"uint128","name":"longCumulativeFundingRate","type":"uint128"},{"internalType":"uint128","name":"shortCumulativeFunding","type":"uint128"}],"internalType":"struct Asset","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidityPoolStorage","outputs":[{"internalType":"uint32[8]","name":"u32s","type":"uint32[8]"},{"internalType":"uint96[2]","name":"u96s","type":"uint96[2]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"}],"name":"getSubAccount","outputs":[{"internalType":"uint96","name":"collateral","type":"uint96"},{"internalType":"uint96","name":"size","type":"uint96"},{"internalType":"uint32","name":"lastIncreasedTime","type":"uint32"},{"internalType":"uint96","name":"entryPrice","type":"uint96"},{"internalType":"uint128","name":"entryFunding","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nextHop","type":"address"},{"internalType":"address","name":"mlp","type":"address"},{"internalType":"address","name":"orderBook","type":"address"},{"internalType":"address","name":"liquidityManager","type":"address"},{"internalType":"address","name":"weth","type":"address"},{"internalType":"address","name":"nativeUnwrapper","type":"address"},{"internalType":"address","name":"vault","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint8","name":"profitAssetId","type":"uint8"},{"internalType":"uint96","name":"collateralPrice","type":"uint96"},{"internalType":"uint96","name":"assetPrice","type":"uint96"},{"internalType":"uint96","name":"profitAssetPrice","type":"uint96"}],"name":"liquidate","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"uint96","name":"collateralPrice","type":"uint96"},{"internalType":"uint96","name":"assetPrice","type":"uint96"}],"name":"openPosition","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"takeOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nextHop","type":"address"}],"name":"upgradeChainedProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"}],"name":"withdrawAllCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint256","name":"rawAmount","type":"uint256"},{"internalType":"uint96","name":"collateralPrice","type":"uint96"},{"internalType":"uint96","name":"assetPrice","type":"uint96"}],"name":"withdrawCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint256","name":"rawAmount","type":"uint256"},{"internalType":"uint8","name":"profitAssetId","type":"uint8"},{"internalType":"uint96","name":"collateralPrice","type":"uint96"},{"internalType":"uint96","name":"assetPrice","type":"uint96"},{"internalType":"uint96","name":"profitAssetPrice","type":"uint96"}],"name":"withdrawProfit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

608060405234801561001057600080fd5b50615d5980620000216000396000f3fe60806040526004361061010d5760003560e01c80638e01065211610095578063d3225b8e11610064578063d3225b8e14610380578063dea9b464146103a0578063f2fde38b146103c0578063fb9561fd146103e0578063fe6facc4146104035761011c565b80638e010652146102db5780639a69721f146102fb578063a13a010014610333578063c8c7fe6b146103535761011c565b8063484289c7116100dc578063484289c71461025357806360536172146102735780636b811a3c14610288578063715018a6146102a85780638da5cb5b146102bd5761011c565b806303e937571461012457806312d76ed6146101d95780632d46c6c2146101fb57806335876476146102335761011c565b3661011c5761011a610423565b005b61011a610423565b34801561013057600080fd5b5061018b61013f366004614ed2565b6000908152606d6020526040902080546001909101546001600160601b0380831693600160601b808504831694600160c01b900463ffffffff169392831692046001600160801b031690565b604080516001600160601b039687168152948616602086015263ffffffff909316928401929092529290921660608201526001600160801b03909116608082015260a0015b60405180910390f35b3480156101e557600080fd5b506101ee610435565b6040516101d0919061510c565b34801561020757600080fd5b5061021b610216366004615171565b61066e565b6040516001600160a01b0390911681526020016101d0565b34801561023f57600080fd5b5061011a61024e3660046151a3565b6106d8565b34801561025f57600080fd5b5061011a61026e366004615240565b61080b565b34801561027f57600080fd5b5061011a61101c565b34801561029457600080fd5b5061011a6102a3366004614ed2565b61108c565b3480156102b457600080fd5b5061011a6112cf565b3480156102c957600080fd5b506033546001600160a01b031661021b565b3480156102e757600080fd5b5061011a6102f63660046152a6565b611313565b34801561030757600080fd5b5061031b6103163660046152ec565b6117ce565b6040516001600160601b0390911681526020016101d0565b34801561033f57600080fd5b5061011a61034e366004615323565b612067565b34801561035f57600080fd5b5061037361036e366004615171565b6120e8565b6040516101d0919061533e565b34801561038c57600080fd5b5061031b61039b36600461534d565b612421565b3480156103ac57600080fd5b5061011a6103bb366004615381565b612b56565b3480156103cc57600080fd5b5061011a6103db366004615323565b612d9b565b3480156103ec57600080fd5b506103f5612e8f565b6040516101d09291906153a3565b34801561040f57600080fd5b5061031b61041e366004615410565b612f34565b61043361042e613705565b61373d565b565b60606066600601805480602002602001604051908101604052809291908181526020016000905b828210156106655760008481526020908190206040805161040081018252600b8602909201805483526001808201546001600160a01b0380821686880152600160a01b80830460ff90811696880196909652600160a81b830486166060880152600160b01b830466ffffffffffffff166080880152600160e81b90920462ffffff1660a0870152600284015463ffffffff80821660c08901526401000000008204811660e0890152600160401b82048116610100890152600160601b80830482166101208a0152600160801b9283900482166101408a015260038701548085166101608b015285810483166101808b0152600160c01b8082049099166101a08b0152600160c81b900482166101c08a015260048701546001600160801b038082166101e08c01529084900481166102008b015260058801546001600160601b038082166102208d015283820485166102408d01529085900481166102608c015260068901548082166102808d015283900481166102a08c015260078901548082166102c08d015283900481166102e08c015260088901548082166103008d0152929092049091166103208a015260098701549384166103408a015293830481166103608901529582048616610380880152600160e01b9091049094166103a0860152600a909201548083166103c086015292909204166103e0830152908352909201910161045c565b50505050905090565b606c5460009060ff83161061069e5760405162461bcd60e51b81526004016106959061546e565b60405180910390fd5b606c805460ff84169081106106b5576106b561548b565b600091825260209091206001600b9092020101546001600160a01b031692915050565b600054610100900460ff166106f35760005460ff16156106f7565b303b155b61075a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610695565b600054610100900460ff1615801561077c576000805461ffff19166101011790555b610784613766565b61078d88613795565b606780546001600160a01b03199081166001600160a01b038a811691909117909255606680548216898416179055606880548216888416179055606980548216878416179055607180548216868416179055607280549091169184169190911790558015610801576000805461ff00191690555b5050505050505050565b6066546001600160a01b0316336001600160a01b03161461083e5760405162461bcd60e51b8152600401610695906154a1565b8461085b5760405162461bcd60e51b8152600401610695906154be565b6040805160a08101825260006020820181815292820181905260608201819052608082015290815261088c876137d6565b808252516001600160a01b03166108b55760405162461bcd60e51b8152600401610695906154db565b805160200151606c5460ff909116106108e05760405162461bcd60e51b81526004016106959061546e565b805160400151606c5460ff9091161061090b5760405162461bcd60e51b81526004016106959061546e565b805160400151606c805460009260ff1690811061092a5761092a61548b565b90600052602060002090600b020190506000606660060183600001516020015160ff168154811061095d5761095d61548b565b600091825260208083208c8452606d9091526040909220600b909102909101915061099583600190810154600160b01b900416151590565b156109b25760405162461bcd60e51b8152600401610695906154f8565b6109ca8360010154600160b01b900461010016151590565b6109e65760405162461bcd60e51b815260040161069590615515565b6109ef8361386f565b610a0b5760405162461bcd60e51b815260040161069590615532565b610a148261386f565b610a305760405162461bcd60e51b815260040161069590615532565b83516060015180610a565750610a568360010154600160b01b9004630100000016151590565b610a725760405162461bcd60e51b81526004016106959061554f565b8054600160601b90046001600160601b0316610ab65760405162461bcd60e51b81526020600482015260036024820152620533d360ec1b6044820152606401610695565b610ad860668488876000015160600151610ad1576000613887565b6001613887565b9550610ae6606683896138a1565b8451606001519097508015610b135750610b118360010154600160b01b900464010000000016151590565b155b15610b28578351604001519750859450610b81565b610b31886139e7565b610b4d5760405162461bcd60e51b8152600401610695906154f8565b610b7e6066806006018a60ff1681548110610b6a57610b6a61548b565b90600052602060002090600b0201876138a1565b94505b610bb060666006018960ff1681548110610b9d57610b9d61548b565b90600052602060002090600b020161386f565b610bcc5760405162461bcd60e51b815260040161069590615532565b6000610be282858760000151606001518a613a2c565b9050610bf78285876000015160600151613b06565b6000610c328b60666006018c60ff1681548110610c1657610c1661548b565b90600052602060002090600b0201613b7890919063ffffffff16565b9050610c52610c4d6001600160601b03808416908a16613bae565b613bcd565b9050610c5e8282615582565b86516060015184549192506000918291610c8c9189918891600160601b90046001600160601b03168e613c10565b9150915081610cc35760405162461bcd60e51b81526020600482015260036024820152620553c360ec1b6044820152606401610695565b826001600160601b0316816001600160601b03161015610d0b5760405162461bcd60e51b8152602060048201526003602482015262553c5760e81b6044820152606401610695565b5050610d48866000015160000151828460666006018e60ff1681548110610d3457610d3461548b565b90600052602060002090600b02018b613da5565b5085516060015115610e3d578254610d7790610c4d906001600160601b0384811691600160601b90041661400e565b600184018054600090610d949084906001600160601b0316615582565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550610df7610c4d86600601600c9054906101000a90046001600160601b03166001600160601b0316836001600160601b031661400e90919063ffffffff16565b600786018054600090610e149084906001600160601b0316615582565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550610efe565b8254610e6090610c4d906001600160601b0384811691600160601b90041661400e565b600184018054600090610e7d9084906001600160601b03166155ad565b82546101009290920a6001600160601b038181021990931691831602179091556008870154610eb59250610c4d91848116911661400e565b600886018054600c90610ed9908490600160601b90046001600160601b03166155ad565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505b610f1e8387600001516040015160ff168860000151606001518c8c614023565b610f3a5760405162461bcd60e51b8152600401610695906155d5565b60408051610140810182528d8152875160209081015160ff908116918301919091528c8116828401528851606090810151151590830152608082018e90526001600160601b03808c1660a08401528c811660c08401528a811660e08401526001870154811661010084015285166101208301528851808401519051935192939116916001600160a01b03909116907fb9900d833b774b0a2bd718c7965228ca3d4061946a56c454a6d22503dea3185190610ff59085906155f2565b60405180910390a3506110066140a9565b61100e614112565b505050505050505050505050565b6065546001600160a01b0316336001600160a01b0316146110655760405162461bcd60e51b815260206004820152600360248201526214d39160ea1b6044820152606401610695565b60655461107a906001600160a01b031661413c565b606580546001600160a01b0319169055565b6066546001600160a01b0316336001600160a01b0316146110bf5760405162461bcd60e51b8152600401610695906154a1565b60006110ca826137d6565b6000838152606d60205260409020805491925090600160601b90046001600160601b0316156111215760405162461bcd60e51b81526020600482015260036024820152620533e360ec1b6044820152606401610695565b80546001600160601b031661115e5760405162461bcd60e51b81526020600482015260036024820152620433d360ec1b6044820152606401610695565b60006066600601836040015160ff168154811061117d5761117d61548b565b90600052602060002090600b0201905060006066600601846020015160ff16815481106111ac576111ac61548b565b90600052602060002090600b020190506111c58261386f565b6111e15760405162461bcd60e51b815260040161069590615532565b6111ea8161386f565b6112065760405162461bcd60e51b815260040161069590615532565b82546001600160601b0316600061121d838361418e565b85546001600160601b031916865586516069546071549293506112519286929185916001600160a01b0391821691166141c9565b85600001516001600160a01b0316877f39db80d104f204d9d7117515e240ca09e84cf05a4061c9a9a0877aecd8b2607f886020015184866040516112b69392919060ff93909316835260208301919091526001600160601b0316604082015260600190565b60405180910390a36112c66140a9565b50505050505050565b6033546001600160a01b031633146112f95760405162461bcd60e51b8152600401610695906156ca565b606580546001600160a01b0319169055610433600061413c565b6066546001600160a01b0316336001600160a01b0316146113465760405162461bcd60e51b8152600401610695906154a1565b826113635760405162461bcd60e51b8152600401610695906154be565b600061136e856137d6565b80519091506001600160a01b03166113985760405162461bcd60e51b8152600401610695906154db565b6020810151606c5460ff909116106113c25760405162461bcd60e51b81526004016106959061546e565b6040810151606c5460ff909116106113ec5760405162461bcd60e51b81526004016106959061546e565b60006066600601826040015160ff168154811061140b5761140b61548b565b90600052602060002090600b0201905060006066600601836020015160ff168154811061143a5761143a61548b565b90600052602060002090600b020190506114538261386f565b61146f5760405162461bcd60e51b815260040161069590615532565b6114788161386f565b6114945760405162461bcd60e51b815260040161069590615532565b6000878152606d602052604090206114ae606684876138a1565b94506114bc606683886138a1565b955060006114d08285876060015189613a2c565b8254909150600160601b90046001600160601b0316156114f9576114f982858760600151613b06565b6000611514610c4d6001600160601b03848116908b1661400e565b83549091506001600160601b038083169116101561155a5760405162461bcd60e51b815260206004820152600360248201526246454560e81b6044820152606401610695565b8254819084906000906115779084906001600160601b03166155ad565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808460050160008282829054906101000a90046001600160601b03166115c19190615582565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808460050160108282829054906101000a90046001600160601b031661160b9190615582565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550600080516020615d0483398151915286602001518260405161166992919060ff9290921682526001600160601b0316602082015260400190565b60405180910390a150600061167e848a613b78565b83549091506001600160601b03808316911610156116c45760405162461bcd60e51b8152602060048201526003602482015262433c5760e81b6044820152606401610695565b82546116da9082906001600160601b03166155ad565b83546001600160601b0319166001600160601b0391909116178355855160695460715461171992879290918d916001600160a01b0390811691166141c9565b61173183876040015160ff1688606001518b8b614023565b61174d5760405162461bcd60e51b8152600401610695906155d5565b85600001516001600160a01b03168a7f39db80d104f204d9d7117515e240ca09e84cf05a4061c9a9a0877aecd8b2607f88602001518c856040516117b29392919060ff93909316835260208301919091526001600160601b0316604082015260600190565b60405180910390a36117c26140a9565b50505050505050505050565b6066546000906001600160a01b0316336001600160a01b0316146118045760405162461bcd60e51b8152600401610695906154a1565b6040805160e0810182526000606082018181526080830182905260a0830182905260c0830182905282526020820181905291810191909152611845886137d6565b808252516001600160a01b031661186e5760405162461bcd60e51b8152600401610695906154db565b805160200151606c5460ff909116106118995760405162461bcd60e51b81526004016106959061546e565b805160400151606c5460ff909116106118c45760405162461bcd60e51b81526004016106959061546e565b6001600160601b0387166118ea5760405162461bcd60e51b8152600401610695906154be565b805160400151606c805460009260ff169081106119095761190961548b565b90600052602060002090600b020190506000606660060183600001516020015160ff168154811061193c5761193c61548b565b600091825260208083208d8452606d9091526040909220600b909102909101915061197483600190810154600160b01b900416151590565b156119915760405162461bcd60e51b8152600401610695906154f8565b6119a98360010154600160b01b900461010016151590565b6119c55760405162461bcd60e51b815260040161069590615515565b6119ce8361386f565b6119ea5760405162461bcd60e51b815260040161069590615532565b6119f38261386f565b611a0f5760405162461bcd60e51b815260040161069590615532565b83516060015180611a355750611a358360010154600160b01b9004630100000016151590565b611a515760405162461bcd60e51b81526004016106959061554f565b80546001600160601b03600160601b9091048116908b161115611a9c5760405162461bcd60e51b8152602060048201526003602482015262413e5360e81b6044820152606401610695565b611ab760668489876000015160600151610ad1576000613887565b9650611ac56066838a6138a1565b8451606001519098508015611af25750611af08360010154600160b01b900464010000000016151590565b155b15611b07578351604001519850869550611b60565b611b10896139e7565b611b2c5760405162461bcd60e51b8152600401610695906154f8565b611b5d6066806006018b60ff1681548110611b4957611b4961548b565b90600052602060002090600b0201886138a1565b95505b611b7c60666006018a60ff1681548110610b9d57610b9d61548b565b611b985760405162461bcd60e51b815260040161069590615532565b8351606001516001820154611bb99185918d906001600160601b03166142dc565b611bce81848660000151606001518d8b6144c1565b6001600160601b03166020850152835160600151611bef9082908590613b06565b600080611c0785848860000151606001518f8d613c10565b915091508115611c6357611c5086600001516000015182886020015160666006018f60ff1681548110611c3c57611c3c61548b565b90600052602060002090600b02018c613da5565b6001600160601b03166040870152611c71565b611c7183858c8460016144e4565b82548c908490600c90611c95908490600160601b90046001600160601b03166155ad565b82546101009290920a6001600160601b038181021990931691831602179091558454600160601b90041615159050611ce8576001830180546001600160e01b0319169055825463ffffffff60c01b191683555b85604001516001600160601b031686602001516001600160601b03161115611eae576000611d3f610c4d8c6001600160601b031689604001518a60200151611d3091906155ad565b6001600160601b03169061400e565b8454909150611d589082906001600160601b031661460e565b845490915081908590600090611d789084906001600160601b03166155ad565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808560050160008282829054906101000a90046001600160601b0316611dc29190615582565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808560050160108282829054906101000a90046001600160601b0316611e0c9190615582565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550600080516020615d0483398151915287600001516020015182604051611e6e92919060ff9290921682526001600160601b0316602082015260400190565b60405180910390a1611e8f610c4d6001600160601b03838116908e16613bae565b87604001818151611ea09190615582565b6001600160601b0316905250505b6000604051806101a001604052808f815260200188600001516020015160ff1681526020018d60ff168152602001886000015160600151151581526020018e6001600160601b031681526020018b6001600160601b031681526020018c6001600160601b031681526020018a6001600160601b0316815260200188604001516001600160601b031681526020018415158152602001836001600160601b0316815260200185600001600c9054906101000a90046001600160601b03166001600160601b031681526020018560000160009054906101000a90046001600160601b03166001600160601b0316815250905086600001516040015160ff168760000151600001516001600160a01b03167f645156066afee3ede009256908a9e96538cc1ad681c46b10114f6ce98ebd060083604051611feb91906156ff565b60405180910390a3506120148387600001516040015160ff168860000151606001518d8d614637565b6120465760405162461bcd60e51b8152602060048201526003602482015262214d4d60e81b6044820152606401610695565b61204e6140a9565b612056614112565b50969b9a5050505050505050505050565b6033546001600160a01b031633146120915760405162461bcd60e51b8152600401610695906156ca565b7fd44215a656753c21b080fa3cb09556758b20e3324b6b42d8b32a766f9fc9a68e6120ba613705565b604080516001600160a01b03928316815291841660208301520160405180910390a16120e581613795565b50565b6040805161040081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081018290526101e08101829052610200810182905261022081018290526102408101829052610260810182905261028081018290526102a081018290526102c081018290526102e08101829052610300810182905261032081018290526103408101829052610360810182905261038081018290526103a081018290526103c081018290526103e0810191909152606c5460ff83161061220d5760405162461bcd60e51b81526004016106959061546e565b606c805460ff84169081106122245761222461548b565b60009182526020918290206040805161040081018252600b9093029091018054835260018101546001600160a01b038082169585019590955260ff600160a01b808304821694860194909452600160a81b82048116606086015266ffffffffffffff600160b01b830416608086015262ffffff600160e81b9092049190911660a0850152600282015463ffffffff80821660c08701526401000000008204811660e0870152600160401b82048116610100870152600160601b8083048216610120880152600160801b92839004821661014088015260038501548089166101608901528681048316610180890152600160c01b8082049095166101a0890152600160c81b900482166101c088015260048501546001600160801b038082166101e08a015290849004811661020089015260058601546001600160601b038082166102208b015283820485166102408b01529085900481166102608a015260068701548082166102808b015283900481166102a08a015260078701548082166102c08b015283900481166102e08a015260088701548082166103008b015292909204909116610320880152600985015497881661034088015294870481166103608701529186048216610380860152600160e01b909504166103a0840152600a01548082166103c0840152929092049091166103e082015292915050565b6066546000906001600160a01b0316336001600160a01b0316146124575760405162461bcd60e51b8152600401610695906154a1565b6000612462866137d6565b80519091506001600160a01b031661248c5760405162461bcd60e51b8152600401610695906154db565b6020810151606c5460ff909116106124b65760405162461bcd60e51b81526004016106959061546e565b6040810151606c5460ff909116106124e05760405162461bcd60e51b81526004016106959061546e565b6001600160601b0385166125065760405162461bcd60e51b8152600401610695906154be565b60006066600601826040015160ff16815481106125255761252561548b565b90600052602060002090600b0201905060006066600601836020015160ff16815481106125545761255461548b565b600091825260208083208b8452606d9091526040909220600b909102909101915061258e8360010154600160b01b90046201000016151590565b6125c05760405162461bcd60e51b815260206004820152600360248201526227a82760e91b6044820152606401610695565b600180840154600160b01b900416156125eb5760405162461bcd60e51b8152600401610695906154f8565b6126038360010154600160b01b900461010016151590565b61261f5760405162461bcd60e51b815260040161069590615515565b6126288361386f565b6126445760405162461bcd60e51b815260040161069590615532565b61264d8261386f565b6126695760405162461bcd60e51b815260040161069590615532565b83606001518061268e575061268e8360010154600160b01b9004630100000016151590565b6126aa5760405162461bcd60e51b81526004016106959061554f565b6126c86066848887606001516126c1576001613887565b6000613887565b95506126d6606683896138a1565b965060006126eb828587606001518c8b6144c1565b90506126fc82858760600151613b06565b6000612717610c4d6001600160601b03848116908c1661400e565b83549091506001600160601b038083169116101561275d5760405162461bcd60e51b815260206004820152600360248201526246454560e81b6044820152606401610695565b82548190849060009061277a9084906001600160601b03166155ad565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808460050160008282829054906101000a90046001600160601b03166127c49190615582565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808460050160108282829054906101000a90046001600160601b031661280e9190615582565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550600080516020615d0483398151915286602001518260405161286c92919060ff9290921682526001600160601b0316602082015260400190565b60405180910390a1506060850151825460009161289d9187918691600160601b90046001600160601b03168c613c10565b8454909250600091506128c1908c90600160601b90046001600160601b0316615582565b90506001600160601b0382166128f3576001840180546001600160601b0319166001600160601b038b16179055612979565b612956816001600160601b03168c6001600160601b03168b6001600160601b031661291e9190615807565b86546001880154612942916001600160601b03600160601b90910481169116615807565b61294c9190615826565b610c4d919061583e565b6001850180546001600160601b0319166001600160601b03929092169190911790555b83546001600160601b03909116600160601b02600160601b600160c01b0319909116178355506129a64290565b8260000160186101000a81548163ffffffff021916908363ffffffff16021790555060006040518061014001604052808c8152602001876020015160ff1681526020018760600151151581526020018b6001600160601b03168152602001896001600160601b031681526020018a6001600160601b031681526020018460010160009054906101000a90046001600160601b03166001600160601b03168152602001836001600160601b0316815260200184600001600c9054906101000a90046001600160601b03166001600160601b031681526020018460000160009054906101000a90046001600160601b03166001600160601b03168152509050856040015160ff1686600001516001600160a01b03167fdb27855d3e94a6c985e1e59c77870a73484ef3c40d29fbfe14bb3e686da86efb83604051612ae89190615860565b60405180910390a350612b018486606001518b8a6146b5565b612b1982866040015160ff1687606001518b8b614023565b612b355760405162461bcd60e51b8152600401610695906155d5565b612b3d6140a9565b612b45614112565b86955050505050505b949350505050565b6066546001600160a01b0316336001600160a01b031614612b895760405162461bcd60e51b8152600401610695906154a1565b6000612b94836137d6565b80519091506001600160a01b0316612bbe5760405162461bcd60e51b8152600401610695906154db565b6020810151606c5460ff90911610612be85760405162461bcd60e51b81526004016106959061546e565b6040810151606c5460ff90911610612c125760405162461bcd60e51b81526004016106959061546e565b81612c2f5760405162461bcd60e51b8152600401610695906154be565b6000838152606d602052604080822090830151606c8054929392909160ff16908110612c5d57612c5d61548b565b90600052602060002090600b0201905060006066600601846020015160ff1681548110612c8c57612c8c61548b565b90600052602060002090600b02019050612ca58261386f565b612cc15760405162461bcd60e51b815260040161069590615532565b612cca8161386f565b612ce65760405162461bcd60e51b815260040161069590615532565b6000612cf28287613b78565b845490915081908590600090612d129084906001600160601b0316615582565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555084600001516001600160a01b0316877f94ab403b9852b1c10901b0928c9b5692d2d229c8e666bae8f77cac8814ead64f876020015189856040516112b69392919060ff93909316835260208301919091526001600160601b0316604082015260600190565b6033546001600160a01b03163314612dc55760405162461bcd60e51b8152600401610695906156ca565b6001600160a01b038116612e015760405162461bcd60e51b815260206004820152600360248201526204f3d360ec1b6044820152606401610695565b6033546001600160a01b0382811691161415612e455760405162461bcd60e51b81526020600482015260036024820152624f3d4f60e81b6044820152606401610695565b606580546001600160a01b0319166001600160a01b0383169081179091556040517fb501f5dc3610d10a179d924f686fc89ddcdd3abb05afd7a1baeead166b2f122c90600090a250565b612e97614e95565b612e9f614eb4565b606a5463ffffffff600160801b820481168452600160a01b8083048216602080870191909152600160e01b80850484166040880152600160c01b948590048416606088015260705485810485166080890152908104841660a0880152607154928304841660c08801529390910490911660e08501526001600160601b038083168452600160601b909204909116908201529091565b6066546000906001600160a01b0316336001600160a01b031614612f6a5760405162461bcd60e51b8152600401610695906154a1565b604080516101008101825260006080820181815260a0830182905260c0830182905260e083018290528252602082018190529181018290526060810191909152612fb3876137d6565b808252516001600160a01b0316612fdc5760405162461bcd60e51b8152600401610695906154db565b805160200151606c5460ff909116106130075760405162461bcd60e51b81526004016106959061546e565b805160400151606c5460ff909116106130325760405162461bcd60e51b81526004016106959061546e565b805160400151606c805460009260ff169081106130515761305161548b565b90600052602060002090600b020190506000606660060183600001516020015160ff16815481106130845761308461548b565b600091825260208083208c8452606d9091526040909220600b90910290910191506130bc83600190810154600160b01b900416151590565b156130d95760405162461bcd60e51b8152600401610695906154f8565b6130f18360010154600160b01b900461010016151590565b61310d5760405162461bcd60e51b815260040161069590615515565b6131168361386f565b6131325760405162461bcd60e51b815260040161069590615532565b61313b8261386f565b6131575760405162461bcd60e51b815260040161069590615532565b8351606001518061317d575061317d8360010154600160b01b9004630100000016151590565b6131995760405162461bcd60e51b81526004016106959061554f565b8054600160601b90046001600160601b03166131dd5760405162461bcd60e51b81526020600482015260036024820152620533d360ec1b6044820152606401610695565b6131f860668489876000015160600151610ad1576000613887565b96506132066066838a6138a1565b845160600151909850801561323357506132318360010154600160b01b900464010000000016151590565b155b1561324857835160400151985086955061328d565b613251896139e7565b61326d5760405162461bcd60e51b8152600401610695906154f8565b61328a6066806006018b60ff1681548110611b4957611b4961548b565b95505b6132a960666006018a60ff1681548110610b9d57610b9d61548b565b6132c55760405162461bcd60e51b815260040161069590615532565b835160600151815460018301546132f392869290916001600160601b03600160601b909204821691166142dc565b600080600061330c84878960000151606001518d613a2c565b8454909150600090613330908890600160601b90046001600160601b03168d61479d565b905061333c8183615582565b6001600160601b0390811660208a0152885160600151865461336c93508992889291600160601b9004168e613c10565b600288015491945092506133969085908d908d90640100000000900463ffffffff1687878761480f565b156133c95760405162461bcd60e51b81526020600482015260036024820152624d4d5360e81b6044820152606401610695565b508254600160601b90046001600160601b0316606087015281156134255761341286600001516000015182886020015160666006018f60ff1681548110611c3c57611c3c61548b565b6001600160601b03166040870152613433565b61343383858c8460006144e4565b82546001840180546001600160e01b0319169055600160601b600160e01b0319168355604086015160208701516001600160601b0391821691161115613608576000613499610c4d8c6001600160601b031689604001518a60200151611d3091906155ad565b84549091506134b29082906001600160601b031661460e565b8454909150819085906000906134d29084906001600160601b03166155ad565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808560050160008282829054906101000a90046001600160601b031661351c9190615582565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808560050160108282829054906101000a90046001600160601b03166135669190615582565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550600080516020615d04833981519152876000015160200151826040516135c892919060ff9290921682526001600160601b0316602082015260400190565b60405180910390a16135e9610c4d6001600160601b03838116908e16613bae565b876040018181516135fa9190615582565b6001600160601b0316905250505b60408051610180810182528d8152875160209081015160ff908116918301919091528d81168284015288516060908101511515818401528901516001600160601b0390811660808401528c811660a08401528d811660c08401528b811660e08401528984015181166101008401528515156101208401528481166101408401528654166101608301528851808401519051935192939116916001600160a01b03909116907fd63e21d9ddaf46f8d28d121f06e7ed33fcc0300af1f8c794e69056dbf37e2d6a906136d99085906158e3565b60405180910390a3506136ea6140a9565b6136f2614112565b8896505050505050505b95945050505050565b60006137387f7d64d9a819609fbacde989007c1c053753b68c3b56ddef912de84ba0f732e0a9546001600160a01b031690565b905090565b3660008037600080366000845af43d6000803e80801561375c573d6000f35b3d6000fd5b505050565b600054610100900460ff1661378d5760405162461bcd60e51b8152600401610695906159de565b6104336148e7565b7f7d64d9a819609fbacde989007c1c053753b68c3b56ddef912de84ba0f732e0a980546001600160a01b0319166001600160a01b0392909216919091179055565b60408051608081018252600080825260208201819052918101829052606081019190915268ffffffffffffffffff8216156138395760405162461bcd60e51b815260206004820152600360248201526210525160ea1b6044820152606401610695565b606082811c825260ff605884901c81166020840152605084901c8116604084015260489390931c90921615159181019190915290565b60010154600160b01b90046501000000000016151590565b60006138948585856138a1565b92506136fc848484614916565b60006001600160601b0382166138c95760405162461bcd60e51b815260040161069590615a29565b60016003840154600160c01b900460ff1660018111156138eb576138eb615a46565b60018111156138fc576138fc615a46565b141561392d57600383015460009061391c906001600160a01b03166149d0565b9050613929848483614a71565b9250505b61394a8360010154600160b01b9004660100000000000016151590565b156139dd576000670de0b6b3a7640000836001600160601b0316116139805761397b83670de0b6b3a76400006155ad565b613992565b613992670de0b6b3a7640000846155ad565b600b8601546001600160601b039190911691506000906139c590600160c01b900463ffffffff166509184e72a000615807565b90508082116139da57670de0b6b3a764000093505b50505b50805b9392505050565b6000613a2660666006018360ff1681548110613a0557613a0561548b565b90600052602060002090600b0201600190810154600160b01b900416151590565b92915050565b8354600090600160601b90046001600160601b0316613a4d57506000612b4e565b60008315613aa1576001860154600a860154613a7c916001600160801b03600160601b90910481169116615a5c565b6001600160801b03169050613a9a816001600160601b038516613bae565b9050613adb565b6001860154600a860154613acf916001600160801b03600160601b909104811691600160801b900416615a5c565b6001600160801b031690505b8554613afc90610c4d908390600160601b90046001600160601b0316613bae565b9695505050505050565b8015613b3f5750600a01546001919091018054600160601b600160e01b0319166001600160801b03909216600160601b02919091179055565b50600a01546001919091018054600160601b600160e01b031916600160801b9092046001600160801b0316600160601b02919091179055565b60018201546000906139e090613b9990600160a81b900460ff166012615a7c565b613ba490600a615b83565b610c4d9084615807565b6000670de0b6b3a7640000613bc38385615807565b6139e0919061583e565b60006001600160601b03821115613c0c5760405162461bcd60e51b8152602060048201526003602482015262279c9b60e91b6044820152606401610695565b5090565b6000806001600160601b038416613c2c57506000905080613d9b565b6000836001600160601b031611613c555760405162461bcd60e51b815260040161069590615a29565b84613c735760018601546001600160601b0390811690841610613c88565b60018601546001600160601b03908116908416115b60018701549092506000906001600160601b039081169085161015613cc5576001870154613cc09085906001600160601b03166155ad565b613cdd565b6001870154613cdd906001600160601b0316856155ad565b9050828015613d1e575060028801548754613d0f9163ffffffff600160601b909104811691600160c01b900416615b92565b63ffffffff164263ffffffff16105b8015613d6c575060028801546001880154613d5791610c4d916001600160601b03169063ffffffff600160401b909104811690614c1816565b6001600160601b0316816001600160601b0316105b15613d7e575060009150819050613d9b565b613d97610c4d6001600160601b03838116908816613bae565b9150505b9550959350505050565b6000613db1848661460e565b9050613dbd81866155ad565b94506001600160601b03851615613f60576000613de9610c4d6001600160601b0388811690861661400e565b90506000613e0e828660050160109054906101000a90046001600160601b031661460e565b90506001600160601b03811615613ea557808560050160108282829054906101000a90046001600160601b0316613e4591906155ad565b92506101000a8154816001600160601b0302191690836001600160601b031602179055506000613e7e828761418e90919063ffffffff16565b606954607154919250613ea39188918c9185916001600160a01b0390811691166141c9565b505b6000613eb182846155ad565b90506001600160601b03811615613f5c57613ed6868a6001600160601b038416614c28565b600180870154600160b01b900416613efc576001860154600160a01b900460ff16613eff565b60005b60ff167fd0c3486ef4b9bf72f47c2ca4ac35dcb5e25dab2f82701138522da20515a39aa0613f3a88600190810154600160b01b900416151590565b6040805191151582526001600160601b03851660208301520160405180910390a25b5050505b6001600160601b038116156136fc576000613f8a610c4d6001600160601b0384811690861661400e565b6005850180549192508291600090613fac9084906001600160601b0316615582565b82546101009290920a6001600160601b03818102199093169183160217909155600186015460408051600160a01b90920460ff1682529184166020820152600080516020615d0483398151915292500160405180910390a15095945050505050565b600081613bc384670de0b6b3a7640000615807565b60008060666006018663ffffffff16815481106140425761404261548b565b90600052602060002090600b02019050600080614079838a898c600001600c9054906101000a90046001600160601b031689613c10565b6002850154919350915061409c908a908890889063ffffffff168686600061480f565b9998505050505050505050565b6071805463ffffffff600160a01b80830482166001018216810263ffffffff60a01b1990931692909217928390556040519190920490911681527f974288fc79dc8b8b1fe9f1f0b8f5738873e53e07d3122c5e3c02b834c018d6cb9060200160405180910390a1565b6071805463ffffffff600160e01b8083048216600101909116026001600160e01b03909116179055565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60018201546000906141ab90600160a81b900460ff166012615a7c565b6141b690600a615b83565b6139e0906001600160601b03841661583e565b60018501546001600160a01b03838116911614156142bc5760405163a9059cbb60e01b81526001600160a01b0382811660048301526024820185905283169063a9059cbb906044016020604051808303816000875af1158015614230573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142549190615bb1565b506040516339f4769360e01b81526001600160a01b038581166004830152602482018590528216906339f4769390604401600060405180830381600087803b15801561429f57600080fd5b505af11580156142b3573d6000803e3d6000fd5b505050506142d5565b60018501546142d5906001600160a01b03168585614c3d565b5050505050565b82156143dc576006840154600090614305908490600160601b90046001600160601b03166155ad565b90506001600160601b03811661432c576007850180546001600160601b03191690556143ab565b614388816001600160601b0316846001600160601b0316846001600160601b03166143579190615807565b6006880154600789015461437e916001600160601b03600160601b90910481169116615807565b61294c9190615bd3565b6007860180546001600160601b0319166001600160601b03929092169190911790555b6006850180546001600160601b03909216600160601b02600160601b600160c01b03199092169190911790556144bb565b60088401546000906143f89084906001600160601b03166155ad565b90506001600160601b03811661442257600885018054600160601b600160c01b0319169055614498565b61446f816001600160601b0316846001600160601b0316846001600160601b031661444d9190615807565b600888015461437e906001600160601b0380821691600160601b900416615807565b85600801600c6101000a8154816001600160601b0302191690836001600160601b031602179055505b6008850180546001600160601b0319166001600160601b03929092169190911790555b50505050565b60006144ce85848461479d565b6144da87878786613a2c565b613afc9190615582565b6001600160601b0382166144f7576142d5565b6000614512610c4d6001600160601b0385811690871661400e565b905081156145625785546001600160601b038083169116101561455d5760405162461bcd60e51b815260206004820152600360248201526204d3d360ec1b6044820152606401610695565b61457b565b85546145789082906001600160601b031661460e565b90505b8554819087906000906145989084906001600160601b03166155ad565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808560050160108282829054906101000a90046001600160601b03166145e29190615582565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550505050505050565b6000816001600160601b0316836001600160601b0316111561463057816139e0565b5090919050565b60008060666006018663ffffffff16815481106146565761465661548b565b90600052602060002090600b0201905060008061468d838a898c600001600c9054906101000a90046001600160601b031689613c10565b9150915061409c8987878660020160049054906101000a900463ffffffff168686600061480f565b82156147325760068401546000906146de908490600160601b90046001600160601b0316615582565b9050614388816001600160601b0316846001600160601b0316846001600160601b031661470b9190615807565b60068801546007890154612942916001600160601b03600160601b90910481169116615807565b600884015460009061474e9084906001600160601b0316615582565b905061446f816001600160601b0316846001600160601b0316846001600160601b031661477b9190615807565b6008880154612942906001600160601b0380821691600160601b900416615807565b600080670de0b6b3a7640000620186a0856001600160601b03168760020160109054906101000a900463ffffffff1663ffffffff16866001600160601b03166147e69190615807565b6147f09190615807565b6147fa919061583e565b614804919061583e565b90506136fc81613bcd565b86546000908190620186a090670de0b6b3a76400009063ffffffff89169061484a906001600160601b03808d1691600160601b900416615807565b6148549190615807565b61485e919061583e565b614868919061583e565b905061487d6001600160601b03841682615826565b895490915060009061489c906001600160601b03908116908b16613bae565b905085156148c357816148b86001600160601b03871683615826565b1015925050506148dc565b6148d66001600160601b03861683615826565b11159150505b979650505050505050565b600054610100900460ff1661490e5760405162461bcd60e51b8152600401610695906159de565b610433614c8f565b6003830154600090600160c81b900463ffffffff166149365750816139e0565b600384015460009061496790610c4d906001600160601b0387169063ffffffff600160c81b909104811690614c1816565b9050600183600181111561497d5761497d615a46565b14156149c657806001600160601b0316846001600160601b0316116149b45760405162461bcd60e51b815260040161069590615a29565b6149be81856155ad565b9150506139e0565b6149be8185615582565b600080826001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614a11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a359190615bea565b905060008113614a575760405162461bcd60e51b815260040161069590615a29565b614a666402540be40082615c03565b90506139e081613bcd565b6003830154600090600160a01b900463ffffffff16614a915750806139e0565b6003840154600090614abe906001600160601b0385169063ffffffff600160a01b909104811690614c1816565b90506000614ad8610c4d836001600160601b038716615bd3565b9050806001600160601b0316856001600160601b03161015614b68576001860154600387015460408051600160a01b9384900460ff1681526001600160601b03808a16602083015288169181019190915291900463ffffffff1660608201527ffe9ee1875afc0594e1dee9eaff7974f80f8690186178b0fe780077776deed4c89060800160405180910390a18094505b614b7e610c4d836001600160601b038716615826565b9050806001600160601b0316856001600160601b03161115614c0e576001860154600387015460408051600160a01b9384900460ff1681526001600160601b03808a16602083015288169181019190915291900463ffffffff1660608201527ffe9ee1875afc0594e1dee9eaff7974f80f8690186178b0fe780077776deed4c89060800160405180910390a18094505b5092949350505050565b6000620186a0613bc38385615807565b6009830154613761906001600160a01b031683835b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613761908490614cbf565b600054610100900460ff16614cb65760405162461bcd60e51b8152600401610695906159de565b6104333361413c565b6000614d14826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614d919092919063ffffffff16565b8051909150156137615780806020019051810190614d329190615bb1565b6137615760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610695565b6060612b4e8484600085856001600160a01b0385163b614df35760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610695565b600080866001600160a01b03168587604051614e0f9190615cb4565b60006040518083038185875af1925050503d8060008114614e4c576040519150601f19603f3d011682016040523d82523d6000602084013e614e51565b606091505b50915091506148dc82828660608315614e6b5750816139e0565b825115614e7b5782518084602001fd5b8160405162461bcd60e51b81526004016106959190615cd0565b6040518061010001604052806008906020820280368337509192915050565b60405180604001604052806002906020820280368337509192915050565b600060208284031215614ee457600080fd5b5035919050565b805182526020810151614f0960208401826001600160a01b03169052565b506040810151614f1e604084018260ff169052565b506060810151614f33606084018260ff169052565b506080810151614f4e608084018266ffffffffffffff169052565b5060a0810151614f6560a084018262ffffff169052565b5060c0810151614f7d60c084018263ffffffff169052565b5060e0810151614f9560e084018263ffffffff169052565b506101008181015163ffffffff908116918401919091526101208083015182169084015261014080830151821690840152610160808301516001600160a01b0390811691850191909152610180808401518316908501526101a08084015160ff16908501526101c0808401518316908501526101e0808401516001600160801b039081169186019190915261020080850151821690860152610220808501516001600160601b03908116918701919091526102408086015185169087015261026080860151821690870152610280808601518216908701526102a0808601518216908701526102c0808601518216908701526102e080860151821690870152610300808601518216908701526103208086015190911690860152610340808501519092169185019190915261036080840151831690850152610380808401518316908501526103a080840151909216918401919091526103c0808301518216908401526103e08083015191821681850152906144bb565b6020808252825182820181905260009190848201906040850190845b8181101561514f5761513b838551614eeb565b928401926104009290920191600101615128565b50909695505050505050565b803560ff8116811461516c57600080fd5b919050565b60006020828403121561518357600080fd5b6139e08261515b565b80356001600160a01b038116811461516c57600080fd5b600080600080600080600060e0888a0312156151be57600080fd5b6151c78861518c565b96506151d56020890161518c565b95506151e36040890161518c565b94506151f16060890161518c565b93506151ff6080890161518c565b925061520d60a0890161518c565b915061521b60c0890161518c565b905092959891949750929550565b80356001600160601b038116811461516c57600080fd5b60008060008060008060c0878903121561525957600080fd5b86359550602087013594506152706040880161515b565b935061527e60608801615229565b925061528c60808801615229565b915061529a60a08801615229565b90509295509295509295565b600080600080608085870312156152bc57600080fd5b84359350602085013592506152d360408601615229565b91506152e160608601615229565b905092959194509250565b60008060008060008060c0878903121561530557600080fd5b8635955061531560208801615229565b94506152706040880161515b565b60006020828403121561533557600080fd5b6139e08261518c565b6104008101613a268284614eeb565b6000806000806080858703121561536357600080fd5b8435935061537360208601615229565b92506152d360408601615229565b6000806040838503121561539457600080fd5b50508035926020909101359150565b6101408101818460005b60088110156153d257815163ffffffff168352602092830192909101906001016153ad565b50505061010082018360005b60028110156154065781516001600160601b03168352602092830192909101906001016153de565b5050509392505050565b600080600080600060a0868803121561542857600080fd5b853594506154386020870161515b565b935061544660408701615229565b925061545460608701615229565b915061546260808701615229565b90509295509295909350565b6020808252600390820152621314d560ea1b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b602080825260039082015262424f4b60e81b604082015260600190565b6020808252600390820152620413d360ec1b604082015260600190565b6020808252600390820152620543d360ec1b604082015260600190565b60208082526003908201526229aa2160e91b604082015260600190565b60208082526003908201526215149160ea1b604082015260600190565b602080825260039082015262454e4160e81b604082015260600190565b60208082526003908201526214d21560ea1b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b60006001600160601b038083168185168083038211156155a4576155a461556c565b01949350505050565b60006001600160601b03838116908316818110156155cd576155cd61556c565b039392505050565b60208082526003908201526221494d60e81b604082015260600190565b815181526020808301516101408301916156109084018260ff169052565b506040830151615625604084018260ff169052565b506060830151615639606084018215159052565b506080830151608083015260a083015161565e60a08401826001600160601b03169052565b5060c083015161567960c08401826001600160601b03169052565b5060e083015161569460e08401826001600160601b03169052565b50610100838101516001600160601b038116848301525050610120838101516001600160601b038116848301525b505092915050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b815181526020808301516101a083019161571d9084018260ff169052565b506040830151615732604084018260ff169052565b506060830151615746606084018215159052565b50608083015161576160808401826001600160601b03169052565b5060a083015161577c60a08401826001600160601b03169052565b5060c083015161579760c08401826001600160601b03169052565b5060e08301516157b260e08401826001600160601b03169052565b50610100838101516001600160601b03908116918401919091526101208085015115159084015261014080850151821690840152610160808501518216908401526101808085015191821681850152906156c2565b60008160001904831182151516156158215761582161556c565b500290565b600082198211156158395761583961556c565b500190565b60008261585b57634e487b7160e01b600052601260045260246000fd5b500490565b8151815260208083015161014083019161587e9084018260ff169052565b506040830151615892604084018215159052565b5060608301516158ad60608401826001600160601b03169052565b5060808301516158c860808401826001600160601b03169052565b5060a083015161565e60a08401826001600160601b03169052565b815181526020808301516101808301916159019084018260ff169052565b506040830151615916604084018260ff169052565b50606083015161592a606084018215159052565b50608083015161594560808401826001600160601b03169052565b5060a083015161596060a08401826001600160601b03169052565b5060c083015161597b60c08401826001600160601b03169052565b5060e083015161599660e08401826001600160601b03169052565b50610100838101516001600160601b039081169184019190915261012080850151151590840152610140808501518216908401526101608085015191821681850152906156c2565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6020808252600390820152620503d360ec1b604082015260600190565b634e487b7160e01b600052602160045260246000fd5b60006001600160801b03838116908316818110156155cd576155cd61556c565b600060ff821660ff841680821015615a9657615a9661556c565b90039392505050565b600181815b80851115615ada578160001904821115615ac057615ac061556c565b80851615615acd57918102915b93841c9390800290615aa4565b509250929050565b600082615af157506001613a26565b81615afe57506000613a26565b8160018114615b145760028114615b1e57615b3a565b6001915050613a26565b60ff841115615b2f57615b2f61556c565b50506001821b613a26565b5060208310610133831016604e8410600b8410161715615b5d575081810a613a26565b615b678383615a9f565b8060001904821115615b7b57615b7b61556c565b029392505050565b60006139e060ff841683615ae2565b600063ffffffff8083168185168083038211156155a4576155a461556c565b600060208284031215615bc357600080fd5b815180151581146139e057600080fd5b600082821015615be557615be561556c565b500390565b600060208284031215615bfc57600080fd5b5051919050565b60006001600160ff1b0381841382841380821686840486111615615c2957615c2961556c565b600160ff1b6000871282811687830589121615615c4857615c4861556c565b60008712925087820587128484161615615c6457615c6461556c565b87850587128184161615615c7a57615c7a61556c565b505050929093029392505050565b60005b83811015615ca3578181015183820152602001615c8b565b838111156144bb5750506000910152565b60008251615cc6818460208701615c88565b9190910192915050565b6020815260008251806020840152615cef816040850160208701615c88565b601f01601f1916919091016040019291505056fefe5353ab8f25b751655ce462adb7567c9e4dff8da72a5f26094ef0e9300d737ca26469706673582212203012e16c16e59533e488c7b0272ea8efb74578ebc61b1cfc87f52c31ebc8c23b64736f6c634300080a0033

Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.