ETH Price: $3,926.74 (-1.42%)

Contract

0x266B30DafF534B03e770518d9c5730d07C04F412
 

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Initialize59505682024-07-11 15:15:51150 days ago1720710951IN
0x266B30Da...07C04F412
0 ETH0.000333321.51025821

Parent Transaction Hash Block From To
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
OffchainExchange

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 800 runs

Other Settings:
default evmVersion
File 1 of 32 : OffchainExchange.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "./interfaces/engine/IProductEngine.sol";
import "./libraries/MathSD21x18.sol";
import "./common/Constants.sol";
import "./libraries/MathHelper.sol";
import "./libraries/Logger.sol";
import "./interfaces/IOffchainExchange.sol";
import "./EndpointGated.sol";
import "./common/Errors.sol";
import "./interfaces/engine/ISpotEngine.sol";
import "./interfaces/engine/IPerpEngine.sol";

contract OffchainExchange is
    IOffchainExchange,
    EndpointGated,
    EIP712Upgradeable
{
    using MathSD21x18 for int128;
    IClearinghouse internal clearinghouse;

    mapping(uint32 => MarketInfoStore) internal marketInfo;
    mapping(uint32 => address) internal virtualBookContract;

    mapping(uint32 => LpParams) internal lpParams;
    mapping(bytes32 => int128) public filledAmounts;

    ISpotEngine internal spotEngine;
    IPerpEngine internal perpEngine;

    mapping(address => mapping(uint32 => FeeRates)) internal feeRates;

    mapping(address => bool) internal addressTouched;
    address[] internal customFeeAddresses;

    mapping(uint32 => uint32) internal quoteIds;

    function getAllFeeRates(address[] memory users, uint32[] memory productIds)
        external
        view
        returns (FeeRates[] memory)
    {
        FeeRates[] memory rates = new FeeRates[](
            users.length * productIds.length
        );

        for (uint256 i = 0; i < users.length; i++) {
            for (uint256 j = 0; j < productIds.length; j++) {
                rates[i * productIds.length + j] = feeRates[users[i]][
                    productIds[j]
                ];
            }
        }
        return rates;
    }

    function getCustomFeeAddresses(uint32 startAt, uint32 limit)
        external
        view
        returns (address[] memory)
    {
        uint32 endAt = startAt + limit;
        uint32 total = uint32(customFeeAddresses.length);
        if (endAt > total) {
            endAt = total;
        }
        if (startAt > total) {
            startAt = total;
        }
        address[] memory addresses = new address[](endAt - startAt);
        for (uint32 i = startAt; i < endAt; i++) {
            addresses[i - startAt] = customFeeAddresses[i];
        }
        return addresses;
    }

    // copied from EIP712Upgradeable
    bytes32 private constant _TYPE_HASH =
        keccak256(
            "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
        );

    function getMarketInfo(uint32 productId)
        public
        view
        returns (MarketInfo memory m)
    {
        MarketInfoStore memory market = marketInfo[productId];
        m.quoteId = quoteIds[productId];
        m.collectedFees = market.collectedFees;
        m.minSize = int128(market.minSize) * 1e9;
        m.sizeIncrement = int128(market.sizeIncrement) * 1e9;
        return m;
    }

    struct CallState {
        IPerpEngine perp;
        ISpotEngine spot;
        bool isPerp;
        uint32 productId;
    }

    function _getCallState(uint32 productId)
        internal
        view
        returns (CallState memory)
    {
        address engineAddr = clearinghouse.getEngineByProduct(productId);
        IPerpEngine perp = perpEngine;

        // don't read the spot engine from storage if its a perp engine
        if (engineAddr == address(perp)) {
            return
                CallState({
                    perp: IPerpEngine(engineAddr),
                    spot: ISpotEngine(address(0)),
                    isPerp: true,
                    productId: productId
                });
        } else {
            return
                CallState({
                    perp: IPerpEngine(address(0)),
                    spot: spotEngine,
                    isPerp: false,
                    productId: productId
                });
        }
    }

    function _updateBalances(
        CallState memory callState,
        uint32 quoteId,
        bytes32 subaccount,
        int128 baseDelta,
        int128 quoteDelta
    ) internal {
        if (callState.isPerp) {
            callState.perp.updateBalance(
                callState.productId,
                subaccount,
                baseDelta,
                quoteDelta
            );
        } else {
            if (quoteId == QUOTE_PRODUCT_ID) {
                callState.spot.updateBalance(
                    callState.productId,
                    subaccount,
                    baseDelta,
                    quoteDelta
                );
            } else {
                callState.spot.updateBalance(
                    callState.productId,
                    subaccount,
                    baseDelta
                );
                callState.spot.updateBalance(quoteId, subaccount, quoteDelta);
            }
        }
    }

    function initialize(address _clearinghouse, address _endpoint)
        external
        initializer
    {
        __Ownable_init();
        setEndpoint(_endpoint);

        __EIP712_init("Vertex", "0.0.1");
        clearinghouse = IClearinghouse(_clearinghouse);
        spotEngine = ISpotEngine(
            clearinghouse.getEngineByType(IProductEngine.EngineType.SPOT)
        );
        perpEngine = IPerpEngine(
            clearinghouse.getEngineByType(IProductEngine.EngineType.PERP)
        );
    }

    function requireEngine() internal virtual {
        require(
            msg.sender == address(spotEngine) ||
                msg.sender == address(perpEngine),
            "only engine can modify config"
        );
    }

    function updateMarket(
        uint32 productId,
        uint32 quoteId,
        address virtualBook,
        int128 sizeIncrement,
        int128 minSize,
        int128 lpSpreadX18
    ) external {
        requireEngine();
        if (virtualBook != address(0)) {
            require(
                virtualBookContract[productId] == address(0),
                "virtual book already set"
            );
            virtualBookContract[productId] = virtualBook;
        }

        if (quoteId != type(uint32).max) {
            quoteIds[productId] = quoteId;
        }

        marketInfo[productId].minSize = int64(minSize / 1e9);
        marketInfo[productId].sizeIncrement = int64(sizeIncrement / 1e9);
        lpParams[productId] = LpParams(lpSpreadX18);
    }

    function getLpParams(uint32 productId)
        external
        view
        returns (LpParams memory)
    {
        return lpParams[productId];
    }

    function getSizeIncrement(uint32 productId) external view returns (int128) {
        return int128(marketInfo[productId].sizeIncrement) * 1e9;
    }

    function getMinSize(uint32 productId) external view returns (int128) {
        return int128(marketInfo[productId].minSize) * 1e9;
    }

    function getDigest(uint32 productId, IEndpoint.Order memory order)
        public
        view
        returns (bytes32)
    {
        string
            memory structType = "Order(bytes32 sender,int128 priceX18,int128 amount,uint64 expiration,uint64 nonce)";

        bytes32 structHash = keccak256(
            abi.encode(
                keccak256(bytes(structType)),
                order.sender,
                order.priceX18,
                order.amount,
                order.expiration,
                order.nonce
            )
        );

        bytes32 domainSeparator = keccak256(
            abi.encode(
                _TYPE_HASH,
                _EIP712NameHash(),
                _EIP712VersionHash(),
                block.chainid,
                address(virtualBookContract[productId])
            )
        );

        return ECDSAUpgradeable.toTypedDataHash(domainSeparator, structHash);
    }

    function _isPerp(IPerpEngine engine, uint32 productId)
        internal
        view
        returns (bool)
    {
        return clearinghouse.getEngineByProduct(productId) == address(engine);
    }

    function _checkSignature(
        bytes32 subaccount,
        bytes32 digest,
        address linkedSigner,
        bytes memory signature
    ) internal view virtual returns (bool) {
        address signer = ECDSA.recover(digest, signature);
        return
            (signer != address(0)) &&
            (subaccount == FEES_ACCOUNT ||
                signer == address(uint160(bytes20(subaccount))) ||
                signer == linkedSigner);
    }

    function _expired(uint64 expiration) internal view returns (bool) {
        return expiration & ((1 << 58) - 1) <= getOracleTime();
    }

    function _isReduceOnly(uint64 expiration) internal pure returns (bool) {
        return ((expiration >> 61) & 1) == 1;
    }

    function _validateOrder(
        CallState memory callState,
        MarketInfo memory,
        IEndpoint.SignedOrder memory signedOrder,
        bytes32 orderDigest,
        address /* linkedSigner */
    ) internal view returns (bool) {
        if (signedOrder.order.sender == X_ACCOUNT) {
            return true;
        }
        IEndpoint.Order memory order = signedOrder.order;
        int128 filledAmount = filledAmounts[orderDigest];
        order.amount -= filledAmount;

        if (_isReduceOnly(order.expiration)) {
            int128 amount = callState.isPerp
                ? callState
                    .perp
                    .getBalance(callState.productId, order.sender)
                    .amount
                : callState
                    .spot
                    .getBalance(callState.productId, order.sender)
                    .amount;
            if ((order.amount > 0) == (amount > 0)) {
                order.amount = 0;
            } else if (order.amount > 0) {
                order.amount = MathHelper.min(order.amount, -amount);
            } else if (order.amount < 0) {
                order.amount = MathHelper.max(order.amount, -amount);
            }
        }

        return
            (order.priceX18 > 0) &&
            //            _checkSignature(
            //                order.sender,
            //                orderDigest,
            //                linkedSigner,
            //                signedOrder.signature
            //            ) &&
            // valid amount
            (order.amount != 0) &&
            !_expired(order.expiration);
    }

    function _feeAmount(
        uint32 productId,
        bytes32 subaccount,
        MarketInfo memory market,
        int128 matchBase,
        int128 matchQuote,
        int128 alreadyMatched,
        int128 orderPriceX18,
        bool taker
    ) internal view returns (int128, int128) {
        // X account is passthrough for trading and incurs
        // no fees
        if (subaccount == X_ACCOUNT) {
            return (0, matchQuote);
        }
        int128 meteredQuote = 0;
        if (taker) {
            // flat minimum fee
            if (alreadyMatched == 0) {
                meteredQuote += market.minSize.mul(orderPriceX18);
                if (matchQuote < 0) {
                    meteredQuote = -meteredQuote;
                }
            }

            // exclude the portion on [0, self.min_size) for match_quote and
            // add to metered_quote
            int128 matchBaseAbs = matchBase.abs();
            // fee is only applied on [minSize, amount)
            int128 feeApplied = MathHelper.abs(alreadyMatched + matchBase) -
                market.minSize;
            feeApplied = MathHelper.min(feeApplied, matchBaseAbs);
            if (feeApplied > 0) {
                meteredQuote += matchQuote.mulDiv(feeApplied, matchBaseAbs);
            }
        } else {
            // for maker rebates things stay the same
            meteredQuote += matchQuote;
        }

        int128 keepRateX18 = ONE -
            getFeeFractionX18(subaccount, productId, taker);
        int128 newMeteredQuote = (meteredQuote > 0)
            ? meteredQuote.mul(keepRateX18)
            : meteredQuote.div(keepRateX18);
        int128 fee = meteredQuote - newMeteredQuote;
        market.collectedFees += fee;
        return (fee, matchQuote - fee);
    }

    function feeAmount(
        uint32 productId,
        bytes32 subaccount,
        MarketInfo memory market,
        int128 matchBase,
        int128 matchQuote,
        int128 alreadyMatched,
        int128 orderPriceX18,
        bool taker
    ) internal virtual returns (int128, int128) {
        return
            _feeAmount(
                productId,
                subaccount,
                market,
                matchBase,
                matchQuote,
                alreadyMatched,
                orderPriceX18,
                taker
            );
    }

    struct OrdersInfo {
        bytes32 takerDigest;
        bytes32 makerDigest;
        int128 makerAmount;
    }

    function _matchOrderAMM(
        CallState memory callState,
        int128 baseDelta, // change in the LP's base position
        int128 quoteDelta, // change in the LP's quote position
        IEndpoint.SignedOrder memory taker
    ) internal returns (int128, int128) {
        // 1. assert that the price is better than the limit price
        int128 impliedPriceX18 = quoteDelta.div(baseDelta).abs();
        if (taker.order.amount > 0) {
            // if buying, the implied price must be lower than the limit price
            require(
                impliedPriceX18 <= taker.order.priceX18,
                ERR_ORDERS_CANNOT_BE_MATCHED
            );

            // AMM must be selling
            // magnitude of what AMM is selling must be less than or equal to what the taker is buying
            require(
                baseDelta < 0 && taker.order.amount >= -baseDelta,
                ERR_ORDERS_CANNOT_BE_MATCHED
            );
        } else {
            // if selling, the implied price must be higher than the limit price
            require(
                impliedPriceX18 >= taker.order.priceX18,
                ERR_ORDERS_CANNOT_BE_MATCHED
            );
            // AMM must be buying
            // magnitude of what AMM is buying must be less than or equal to what the taker is selling
            require(
                baseDelta > 0 && taker.order.amount <= -baseDelta,
                ERR_ORDERS_CANNOT_BE_MATCHED
            );
        }

        IProductEngine engine = callState.isPerp
            ? IProductEngine(callState.perp)
            : IProductEngine(callState.spot);

        (int128 baseSwapped, int128 quoteSwapped) = engine.swapLp(
            callState.productId,
            baseDelta,
            quoteDelta
        );

        taker.order.amount += baseSwapped;
        return (-baseSwapped, -quoteSwapped);
    }

    function _matchOrderOrder(
        CallState memory callState,
        MarketInfo memory market,
        IEndpoint.Order memory taker,
        IEndpoint.Order memory maker,
        OrdersInfo memory ordersInfo
    ) internal returns (int128 takerAmountDelta, int128 takerQuoteDelta) {
        // execution happens at the maker's price
        if (taker.amount < 0) {
            takerAmountDelta = MathHelper.max(taker.amount, -maker.amount);
        } else if (taker.amount > 0) {
            takerAmountDelta = MathHelper.min(taker.amount, -maker.amount);
        } else {
            return (0, 0);
        }

        takerAmountDelta -= takerAmountDelta % market.sizeIncrement;

        int128 makerQuoteDelta = takerAmountDelta.mul(maker.priceX18);

        takerQuoteDelta = -makerQuoteDelta;

        // apply the maker fee
        int128 makerFee;

        (makerFee, makerQuoteDelta) = feeAmount(
            callState.productId,
            maker.sender,
            market,
            -takerAmountDelta,
            makerQuoteDelta,
            0, // alreadyMatched doesn't matter for a maker order
            0, // price doesn't matter for a maker order
            false
        );

        taker.amount -= takerAmountDelta;
        maker.amount += takerAmountDelta;

        _updateBalances(
            callState,
            market.quoteId,
            maker.sender,
            -takerAmountDelta,
            makerQuoteDelta
        );

        emit FillOrder(
            callState.productId,
            ordersInfo.makerDigest,
            maker.sender,
            maker.priceX18,
            ordersInfo.makerAmount,
            maker.expiration,
            maker.nonce,
            false,
            makerFee,
            -takerAmountDelta,
            makerQuoteDelta
        );
    }

    function matchOrderAMM(
        IEndpoint.MatchOrderAMM calldata txn,
        address takerLinkedSigner
    ) external onlyEndpoint {
        CallState memory callState = _getCallState(txn.productId);
        MarketInfo memory market = getMarketInfo(txn.productId);
        bytes32 takerDigest = getDigest(txn.productId, txn.taker.order);
        int128 takerAmount = txn.taker.order.amount;

        // need to convert the taker order from calldata into memory
        // otherwise modifications we make to the order's amounts
        // don't persist
        IEndpoint.SignedOrder memory taker = txn.taker;

        require(
            _validateOrder(
                callState,
                market,
                taker,
                takerDigest,
                takerLinkedSigner
            ),
            ERR_INVALID_TAKER
        );

        (int128 takerAmountDelta, int128 takerQuoteDelta) = _matchOrderAMM(
            callState,
            txn.baseDelta,
            txn.quoteDelta,
            taker
        );

        // apply the taker fee
        int128 takerFee;

        (takerFee, takerQuoteDelta) = feeAmount(
            txn.productId,
            taker.order.sender,
            market,
            takerAmountDelta,
            takerQuoteDelta,
            takerAmount - taker.order.amount - takerAmountDelta,
            -takerQuoteDelta.div(takerAmountDelta),
            true
        );

        _updateBalances(
            callState,
            market.quoteId,
            taker.order.sender,
            takerAmountDelta,
            takerQuoteDelta
        );

        require(isHealthy(taker.order.sender), ERR_INVALID_TAKER);

        emit FillOrder(
            txn.productId,
            takerDigest,
            taker.order.sender,
            taker.order.priceX18,
            takerAmount,
            taker.order.expiration,
            taker.order.nonce,
            true,
            takerFee,
            takerAmountDelta,
            takerQuoteDelta
        );
        marketInfo[txn.productId].collectedFees = market.collectedFees;
        filledAmounts[takerDigest] = takerAmount - taker.order.amount;
    }

    function isHealthy(
        bytes32 /* subaccount */
    ) internal view virtual returns (bool) {
        return true;
    }

    function matchOrders(IEndpoint.MatchOrdersWithSigner calldata txn)
        external
        onlyEndpoint
    {
        CallState memory callState = _getCallState(txn.matchOrders.productId);
        int128 takerAmount;
        int128 takerFee;
        int128 takerAmountDelta;
        int128 takerQuoteDelta;
        OrdersInfo memory ordersInfo;

        {
            MarketInfo memory market = getMarketInfo(callState.productId);
            IEndpoint.SignedOrder memory taker = txn.matchOrders.taker;
            IEndpoint.SignedOrder memory maker = txn.matchOrders.maker;
            ordersInfo = OrdersInfo({
                takerDigest: getDigest(callState.productId, taker.order),
                makerDigest: getDigest(callState.productId, maker.order),
                makerAmount: maker.order.amount
            });

            takerAmount = taker.order.amount;

            require(
                _validateOrder(
                    callState,
                    market,
                    taker,
                    ordersInfo.takerDigest,
                    txn.takerLinkedSigner
                ),
                ERR_INVALID_TAKER
            );
            require(
                _validateOrder(
                    callState,
                    market,
                    maker,
                    ordersInfo.makerDigest,
                    txn.makerLinkedSigner
                ),
                ERR_INVALID_MAKER
            );

            // ensure orders are crossing
            require(
                (maker.order.amount > 0) != (taker.order.amount > 0),
                ERR_ORDERS_CANNOT_BE_MATCHED
            );
            if (maker.order.amount > 0) {
                require(
                    maker.order.priceX18 >= taker.order.priceX18,
                    ERR_ORDERS_CANNOT_BE_MATCHED
                );
            } else {
                require(
                    maker.order.priceX18 <= taker.order.priceX18,
                    ERR_ORDERS_CANNOT_BE_MATCHED
                );
            }

            (takerAmountDelta, takerQuoteDelta) = _matchOrderOrder(
                callState,
                market,
                taker.order,
                maker.order,
                ordersInfo
            );

            // apply the taker fee
            (takerFee, takerQuoteDelta) = feeAmount(
                callState.productId,
                taker.order.sender,
                market,
                takerAmountDelta,
                takerQuoteDelta,
                takerAmount - taker.order.amount - takerAmountDelta,
                maker.order.priceX18,
                true
            );

            _updateBalances(
                callState,
                market.quoteId,
                taker.order.sender,
                takerAmountDelta,
                takerQuoteDelta
            );

            require(isHealthy(taker.order.sender), ERR_INVALID_TAKER);
            require(isHealthy(maker.order.sender), ERR_INVALID_MAKER);

            marketInfo[callState.productId].collectedFees = market
                .collectedFees;

            if (txn.matchOrders.taker.order.sender != X_ACCOUNT) {
                filledAmounts[ordersInfo.takerDigest] =
                    takerAmount -
                    taker.order.amount;
            }

            if (txn.matchOrders.maker.order.sender != X_ACCOUNT) {
                filledAmounts[ordersInfo.makerDigest] =
                    ordersInfo.makerAmount -
                    maker.order.amount;
            }
        }

        emit FillOrder(
            callState.productId,
            ordersInfo.takerDigest,
            txn.matchOrders.taker.order.sender,
            txn.matchOrders.taker.order.priceX18,
            takerAmount,
            txn.matchOrders.taker.order.expiration,
            txn.matchOrders.taker.order.nonce,
            true,
            takerFee,
            takerAmountDelta,
            takerQuoteDelta
        );
    }

    function swapAMM(IEndpoint.SwapAMM calldata txn) external onlyEndpoint {
        MarketInfo memory market = getMarketInfo(txn.productId);
        CallState memory callState = _getCallState(txn.productId);

        if (callState.isPerp) {
            require(
                txn.amount % market.sizeIncrement == 0,
                ERR_INVALID_SWAP_PARAMS
            );
        }

        IProductEngine engine = callState.isPerp
            ? IProductEngine(callState.perp)
            : IProductEngine(callState.spot);

        (int128 takerAmountDelta, int128 takerQuoteDelta) = engine.swapLp(
            txn.productId,
            txn.amount,
            -txn.amount.mul(txn.priceX18)
        );

        takerAmountDelta = -takerAmountDelta;
        takerQuoteDelta = -takerQuoteDelta;

        int128 takerFee;

        (takerFee, takerQuoteDelta) = feeAmount(
            txn.productId,
            txn.sender,
            market,
            takerAmountDelta,
            takerQuoteDelta,
            (takerAmountDelta > 0) ? market.minSize : -market.minSize, // just charge the protocol fee without any flat stuff
            0,
            true
        );

        _updateBalances(
            callState,
            market.quoteId,
            txn.sender,
            takerAmountDelta,
            takerQuoteDelta
        );
        require(
            clearinghouse.getHealth(
                txn.sender,
                IProductEngine.HealthType.INITIAL
            ) >= 0,
            ERR_INVALID_TAKER
        );
        marketInfo[txn.productId].collectedFees = market.collectedFees;
    }

    function dumpFees() external onlyEndpoint {
        // loop over all spot and perp product ids
        uint32[] memory productIds = spotEngine.getProductIds();

        for (uint32 i = 1; i < productIds.length; i++) {
            uint32 productId = productIds[i];
            MarketInfoStore memory market = marketInfo[productId];

            // its possible for fees to be <= 0 if there is a cross-chain trade
            // and the maker rebate is exclusively coming from one chain
            if (market.collectedFees <= 0) {
                continue;
            }

            spotEngine.updateBalance(
                quoteIds[productId],
                FEES_ACCOUNT,
                market.collectedFees
            );

            market.collectedFees = 0;
            marketInfo[productId] = market;
        }

        productIds = perpEngine.getProductIds();

        for (uint32 i = 0; i < productIds.length; i++) {
            uint32 productId = productIds[i];
            MarketInfoStore memory market = marketInfo[productId];
            if (market.collectedFees == 0) {
                continue;
            }

            perpEngine.updateBalance(
                productId,
                FEES_ACCOUNT,
                0,
                market.collectedFees
            );

            market.collectedFees = 0;
            marketInfo[productId] = market;
        }
    }

    function getFeeFractionX18(
        bytes32 subaccount,
        uint32 productId,
        bool taker
    ) public view returns (int128) {
        FeeRates memory userFeeRates = feeRates[
            address(uint160(bytes20(subaccount)))
        ][productId];
        if (userFeeRates.isNonDefault == 0) {
            // use the default fee rates.
            userFeeRates = FeeRates(0, 200_000_000_000_000, 1);
        }
        return taker ? userFeeRates.takerRateX18 : userFeeRates.makerRateX18;
    }

    function getFeeRatesX18(bytes32 subaccount, uint32 productId)
        public
        view
        returns (int128, int128)
    {
        FeeRates memory userFeeRates = feeRates[
            address(uint160(bytes20(subaccount)))
        ][productId];
        if (userFeeRates.isNonDefault == 0) {
            // use the default fee rates.
            userFeeRates = FeeRates(0, 200_000_000_000_000, 1);
        }
        return (userFeeRates.takerRateX18, userFeeRates.makerRateX18);
    }

    function updateFeeRates(
        address user,
        uint32 productId,
        int64 makerRateX18,
        int64 takerRateX18
    ) external onlyEndpoint {
        if (!addressTouched[user]) {
            addressTouched[user] = true;
            customFeeAddresses.push(user);
        }
        if (productId == QUOTE_PRODUCT_ID) {
            uint32[] memory spotProductIds = spotEngine.getProductIds();
            uint32[] memory perpProductIds = perpEngine.getProductIds();
            for (uint32 i = 0; i < spotProductIds.length; i++) {
                if (spotProductIds[i] == QUOTE_PRODUCT_ID) {
                    continue;
                }
                feeRates[user][spotProductIds[i]] = FeeRates(
                    makerRateX18,
                    takerRateX18,
                    1
                );
            }
            for (uint32 i = 0; i < perpProductIds.length; i++) {
                feeRates[user][perpProductIds[i]] = FeeRates(
                    makerRateX18,
                    takerRateX18,
                    1
                );
            }
        } else {
            feeRates[user][productId] = FeeRates(makerRateX18, takerRateX18, 1);
        }
    }

    function getVirtualBook(uint32 productId) external view returns (address) {
        return virtualBookContract[productId];
    }

    function getAllVirtualBooks() external view returns (address[] memory) {
        uint32[] memory spotProductIds = spotEngine.getProductIds();
        uint32[] memory perpProductIds = perpEngine.getProductIds();

        uint32 maxProductId = 0;
        for (uint32 i = 0; i < spotProductIds.length; i++) {
            if (spotProductIds[i] > maxProductId) {
                maxProductId = spotProductIds[i];
            }
        }
        for (uint32 i = 0; i < perpProductIds.length; i++) {
            if (perpProductIds[i] > maxProductId) {
                maxProductId = perpProductIds[i];
            }
        }

        address[] memory virtualBooks = new address[](maxProductId + 1);
        for (uint32 i = 0; i <= maxProductId; i++) {
            virtualBooks[i] = virtualBookContract[i];
        }
        return virtualBooks;
    }
}

File 2 of 32 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

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.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * 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 prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

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

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

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

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Internal function that returns the initialized version. Returns `_initialized`
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Internal function that returns the initialized version. Returns `_initializing`
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

File 3 of 32 : draft-EIP712Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/cryptography/draft-EIP712.sol)

pragma solidity ^0.8.0;

// EIP-712 is Final as of 2022-08-11. This file is deprecated.

import "./EIP712Upgradeable.sol";

File 4 of 32 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 5 of 32 : IProductEngine.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../clearinghouse/IClearinghouse.sol";
import "../../libraries/RiskHelper.sol";

interface IProductEngine {
    event AddProduct(uint32 productId);

    enum EngineType {
        SPOT,
        PERP
    }

    enum HealthType {
        INITIAL,
        MAINTENANCE,
        PNL
    }

    struct ProductDelta {
        uint32 productId;
        bytes32 subaccount;
        int128 amountDelta;
        int128 vQuoteDelta;
    }

    struct CoreRisk {
        int128 amount;
        int128 price;
        int128 longWeight;
    }

    /// @notice Initializes the engine
    function initialize(
        address _clearinghouse,
        address _offchainExchange,
        address _quote,
        address _endpoint,
        address _admin
    ) external;

    function getHealthContribution(
        bytes32 subaccount,
        IProductEngine.HealthType healthType
    ) external view returns (int128);

    function getCoreRisk(
        bytes32 subaccount,
        uint32 productId,
        IProductEngine.HealthType healthType
    ) external view returns (IProductEngine.CoreRisk memory);

    function updateProduct(bytes calldata txn) external;

    function swapLp(
        uint32 productId,
        int128 baseDelta,
        int128 quoteDelta
    ) external returns (int128, int128);

    function mintLp(
        uint32 productId,
        bytes32 subaccount,
        int128 amountBase,
        int128 quoteAmountLow,
        int128 quoteAmountHigh
    ) external;

    function burnLp(
        uint32 productId,
        bytes32 subaccount,
        // passing 0 here means to burn all
        int128 amountLp
    ) external returns (int128, int128);

    function decomposeLps(
        uint32 isoGroup,
        bytes32 liquidatee,
        bytes32 liquidator
    ) external returns (int128);

    /// @notice return clearinghouse addr
    function getClearinghouse() external view returns (address);

    /// @notice return productIds associated with engine
    function getProductIds() external view returns (uint32[] memory);

    /// @notice return productIds associated with an isoGroup
    // 0 -> cross group
    function getProductIds(uint32 isoGroup)
        external
        view
        returns (uint32[] memory);

    function getRisk(uint32 productId)
        external
        view
        returns (RiskHelper.Risk memory);

    /// @notice return the type of engine
    function getEngineType() external pure returns (IProductEngine.EngineType);

    function updatePrice(uint32 productId, int128 priceX18) external;
}

File 6 of 32 : MathSD21x18.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "prb-math/contracts/PRBMathSD59x18.sol";

library MathSD21x18 {
    using PRBMathSD59x18 for int256;

    int128 private constant ONE_X18 = 1000000000000000000;
    int128 private constant MIN_X18 = -0x80000000000000000000000000000000;
    int128 private constant MAX_X18 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
    string private constant ERR_OVERFLOW = "OF";
    string private constant ERR_DIV_BY_ZERO = "DBZ";

    function fromInt(int128 x) internal pure returns (int128) {
        unchecked {
            int256 result = int256(x) * ONE_X18;
            require(result >= MIN_X18 && result <= MAX_X18, ERR_OVERFLOW);
            return int128(result);
        }
    }

    function mulDiv(
        int128 x,
        int128 y,
        int128 z
    ) internal pure returns (int128) {
        unchecked {
            require(z != 0, ERR_DIV_BY_ZERO);
            int256 result = (int256(x) * y) / z;
            require(result >= MIN_X18 && result <= MAX_X18, ERR_OVERFLOW);
            return int128(result);
        }
    }

    function toInt(int128 x) internal pure returns (int128) {
        unchecked {
            return int128(x / ONE_X18);
        }
    }

    function add(int128 x, int128 y) internal pure returns (int128) {
        unchecked {
            int256 result = int256(x) + y;
            require(result >= MIN_X18 && result <= MAX_X18, ERR_OVERFLOW);
            return int128(result);
        }
    }

    function sub(int128 x, int128 y) internal pure returns (int128) {
        unchecked {
            int256 result = int256(x) - y;
            require(result >= MIN_X18 && result <= MAX_X18, ERR_OVERFLOW);
            return int128(result);
        }
    }

    function mul(int128 x, int128 y) internal pure returns (int128) {
        unchecked {
            int256 result = (int256(x) * y) / ONE_X18;
            require(result >= MIN_X18 && result <= MAX_X18, ERR_OVERFLOW);
            return int128(result);
        }
    }

    function div(int128 x, int128 y) internal pure returns (int128) {
        unchecked {
            require(y != 0, ERR_DIV_BY_ZERO);
            int256 result = (int256(x) * ONE_X18) / y;
            require(result >= MIN_X18 && result <= MAX_X18, ERR_OVERFLOW);
            return int128(result);
        }
    }

    function abs(int128 x) internal pure returns (int128) {
        unchecked {
            require(x != MIN_X18, ERR_OVERFLOW);
            return x < 0 ? -x : x;
        }
    }

    function sqrt(int128 x) internal pure returns (int128) {
        unchecked {
            int256 result = int256(x).sqrt();
            require(result >= MIN_X18 && result <= MAX_X18, ERR_OVERFLOW);
            return int128(result);
        }
    }

    // note that y is not X18
    function pow(int128 x, int128 y) internal pure returns (int128) {
        unchecked {
            require(y >= 0, ERR_OVERFLOW);
            int128 result = ONE_X18;
            for (int128 i = 1; i <= y; i *= 2) {
                if (i & y != 0) {
                    result = mul(result, x);
                }
                x = mul(x, x);
            }
            return result;
        }
    }
}

File 7 of 32 : Constants.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @dev Each clearinghouse has a unique quote product
uint32 constant QUOTE_PRODUCT_ID = 0;

/// @dev Fees account
bytes32 constant FEES_ACCOUNT = bytes32(0);
bytes32 constant X_ACCOUNT = 0x0000000000000000000000000000000000000000000000000000000000000001;

string constant DEFAULT_REFERRAL_CODE = "-1";

uint128 constant MINIMUM_LIQUIDITY = 10**3;

int128 constant ONE = 10**18;

uint8 constant MAX_DECIMALS = 18;

int128 constant TAKER_SEQUENCER_FEE = 0; // $0.00

int128 constant SLOW_MODE_FEE = 1000000; // $1

int128 constant LIQUIDATION_FEE = 1e18; // $1
int128 constant HEALTHCHECK_FEE = 1e18; // $1

uint128 constant INT128_MAX = uint128(type(int128).max);

uint64 constant SECONDS_PER_DAY = 3600 * 24;

uint32 constant VRTX_PRODUCT_ID = 41;

int128 constant LIQUIDATION_FEE_FRACTION = 500_000_000_000_000_000; // 50%

int128 constant INTEREST_FEE_FRACTION = 200_000_000_000_000_000; // 20%

int256 constant MIN_DEPOSIT_AMOUNT = 5 * ONE;

File 8 of 32 : MathHelper.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "./MathSD21x18.sol";

/// @title MathHelper
/// @dev Provides basic math functions
library MathHelper {
    using MathSD21x18 for int128;

    /// @notice Returns market id for two given product ids
    function max(int128 a, int128 b) internal pure returns (int128) {
        return a > b ? a : b;
    }

    function min(int128 a, int128 b) internal pure returns (int128) {
        return a < b ? a : b;
    }

    function abs(int128 val) internal pure returns (int128) {
        return val < 0 ? -val : val;
    }

    // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
    function sqrt(int128 y) internal pure returns (int128 z) {
        require(y >= 0, "ds-math-sqrt-non-positive");
        if (y > 3) {
            z = y;
            int128 x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }

    function sqrt256(int256 y) internal pure returns (int256 z) {
        require(y >= 0, "ds-math-sqrt-non-positive");
        if (y > 3) {
            z = y;
            int256 x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }

    function int2str(int128 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0";
        }

        bool negative = value < 0;
        uint128 absval = uint128(negative ? -value : value);
        string memory out = uint2str(absval);
        if (negative) {
            out = string.concat("-", out);
        }
        return out;
    }

    function uint2str(uint128 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0";
        }
        uint128 temp = value;
        uint128 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint128(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.1.0/contracts/math/SignedSafeMath.sol#L86
    function add(int128 x, int128 y) internal pure returns (int128) {
        int128 z = x + y;
        require((y >= 0 && z >= x) || (y < 0 && z < x), "ds-math-add-overflow");
        return z;
    }

    // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.1.0/contracts/math/SignedSafeMath.sol#L69
    function sub(int128 x, int128 y) internal pure returns (int128) {
        int128 z = x - y;
        require(
            (y >= 0 && z <= x) || (y < 0 && z > x),
            "ds-math-sub-underflow"
        );
        return z;
    }

    function mul(int128 x, int128 y) internal pure returns (int128 z) {
        require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
    }

    function floor(int128 x, int128 y) internal pure returns (int128 z) {
        require(y > 0, "ds-math-floor-neg-mod");
        int128 r = x % y;
        if (r == 0) {
            z = x;
        } else {
            z = (x >= 0 ? x - r : x - r - y);
        }
    }

    function ceil(int128 x, int128 y) internal pure returns (int128 z) {
        require(y > 0, "ds-math-ceil-neg-mod");
        int128 r = x % y;
        if (r == 0) {
            z = x;
        } else {
            z = (x >= 0 ? x + y - r : x - r);
        }
    }

    // we don't need to floor base with sizeIncrement in this function
    // because this function is only used by `view` functions, which means
    // the returned values will not be written into storage.
    function ammEquilibrium(
        int128 base,
        int128 quote,
        int128 priceX18
    ) internal pure returns (int128, int128) {
        if (base == 0 || quote == 0) {
            return (0, 0);
        }
        int256 k = int256(base) * quote;
        // base * price * base == k
        // base = sqrt(k / price);
        base = int128(MathHelper.sqrt256((k * 1e18) / priceX18));
        quote = (base == 0) ? int128(0) : int128(k / base);
        return (base, quote);
    }

    function isSwapValid(
        int128 baseDelta,
        int128 quoteDelta,
        int128 base,
        int128 quote
    ) internal pure returns (bool) {
        if (
            base == 0 ||
            quote == 0 ||
            base + baseDelta <= 0 ||
            quote + quoteDelta <= 0
        ) {
            return false;
        }
        int256 kPrev = int256(base) * quote;
        int256 kNew = int256(base + baseDelta) * (quote + quoteDelta);
        return kNew > kPrev;
    }

    function swap(
        int128 amountSwap,
        int128 base,
        int128 quote,
        int128 priceX18,
        int128 sizeIncrement,
        int128 lpSpreadX18
    ) internal pure returns (int128, int128) {
        // (amountSwap % sizeIncrement) is guaranteed to be 0
        if (base == 0 || quote == 0) {
            return (0, 0);
        }
        int128 currentPriceX18 = quote.div(base);

        int128 keepRateX18 = 1e18 - lpSpreadX18;

        // selling
        if (amountSwap > 0) {
            priceX18 = priceX18.div(keepRateX18);
            if (priceX18 >= currentPriceX18) {
                return (0, 0);
            }
        } else {
            priceX18 = priceX18.mul(keepRateX18);
            if (priceX18 <= currentPriceX18) {
                return (0, 0);
            }
        }

        int256 k = int256(base) * quote;
        int128 baseAtPrice = int128(
            (MathHelper.sqrt256(k) * 1e9) / MathHelper.sqrt(priceX18)
        );
        // base -> base + amountSwap

        int128 baseSwapped;

        if (
            (amountSwap > 0 && base + amountSwap > baseAtPrice) ||
            (amountSwap < 0 && base + amountSwap < baseAtPrice)
        ) {
            // we hit price limits before we exhaust amountSwap
            if (baseAtPrice >= base) {
                baseSwapped = MathHelper.floor(
                    baseAtPrice - base,
                    sizeIncrement
                );
            } else {
                baseSwapped = MathHelper.ceil(
                    baseAtPrice - base,
                    sizeIncrement
                );
            }
        } else {
            // just swap it all
            // amountSwap is already guaranteed to adhere to sizeIncrement
            baseSwapped = amountSwap;
        }

        int128 quoteSwapped = int128(k / (base + baseSwapped) - quote);
        if (amountSwap > 0) {
            quoteSwapped = quoteSwapped.mul(keepRateX18);
        } else {
            quoteSwapped = quoteSwapped.div(keepRateX18);
        }
        return (baseSwapped, quoteSwapped);
    }
}

File 9 of 32 : Logger.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/Strings.sol";
import "./MathHelper.sol";

library Logger {
    event VertexEVMLog(string message);

    function log(string memory message) internal {
        emit VertexEVMLog(message);
    }

    function log(int128 value) internal {
        log(MathHelper.int2str(value));
    }

    function log(string memory message, int128 value) internal {
        log(string.concat(message, " ", MathHelper.int2str(value)));
    }

    function log(string memory message, uint128 value) internal {
        log(string.concat(message, " ", MathHelper.uint2str(value)));
    }

    //    function log(string memory message, uint32 value) internal {
    //        log(message, uint128(value));
    //    }

    function log(string memory message, address value) internal {
        log(
            string.concat(message, " ", Strings.toHexString(uint160(value), 20))
        );
    }

    function log(string memory messages, bytes32 value) internal {
        log(string.concat(messages, " ", string(abi.encodePacked(value))));
    }
}

File 10 of 32 : IOffchainExchange.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "./clearinghouse/IClearinghouse.sol";

interface IOffchainExchange {
    event FillOrder(
        uint32 indexed productId,
        // original order information
        bytes32 indexed digest,
        bytes32 indexed subaccount,
        int128 priceX18,
        int128 amount,
        uint64 expiration,
        uint64 nonce,
        // whether this order is taking or making
        bool isTaker,
        // amount paid in fees (in quote)
        int128 feeAmount,
        // change in this subaccount's base balance from this fill
        int128 baseDelta,
        // change in this subaccount's quote balance from this fill
        int128 quoteDelta
    );

    struct FeeRates {
        int64 makerRateX18;
        int64 takerRateX18;
        uint8 isNonDefault; // 1: non-default, 0: default
    }

    struct LpParams {
        int128 lpSpreadX18;
    }

    struct MarketInfoStore {
        int64 minSize;
        int64 sizeIncrement;
        int128 collectedFees;
    }

    struct MarketInfo {
        uint32 quoteId;
        int128 minSize;
        int128 sizeIncrement;
        int128 collectedFees;
    }

    function initialize(address _clearinghouse, address _endpoint) external;

    function updateFeeRates(
        address user,
        uint32 productId,
        int64 makerRateX18,
        int64 takerRateX18
    ) external;

    function updateMarket(
        uint32 productId,
        uint32 quoteId,
        address virtualBook,
        int128 sizeIncrement,
        int128 minSize,
        int128 lpSpreadX18
    ) external;

    function getMinSize(uint32 productId) external view returns (int128);

    function getDigest(uint32 productId, IEndpoint.Order memory order)
        external
        view
        returns (bytes32);

    function getSizeIncrement(uint32 productId) external view returns (int128);

    function getMarketInfo(uint32 productId)
        external
        view
        returns (MarketInfo memory);

    function getLpParams(uint32 productId)
        external
        view
        returns (LpParams memory);

    function swapAMM(IEndpoint.SwapAMM calldata tx) external;

    function matchOrderAMM(
        IEndpoint.MatchOrderAMM calldata tx,
        address takerLinkedSigner
    ) external;

    function matchOrders(IEndpoint.MatchOrdersWithSigner calldata tx) external;

    function dumpFees() external;
}

File 11 of 32 : EndpointGated.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "./interfaces/IEndpoint.sol";
import "./interfaces/IEndpointGated.sol";
import "./libraries/MathSD21x18.sol";
import "./common/Constants.sol";
import "hardhat/console.sol";

abstract contract EndpointGated is OwnableUpgradeable, IEndpointGated {
    address private endpoint;

    function setEndpoint(address _endpoint) internal onlyOwner {
        endpoint = _endpoint;
    }

    function getEndpoint() public view returns (address) {
        return endpoint;
    }

    function getOracleTime() internal view returns (uint128) {
        return IEndpoint(endpoint).getTime();
    }

    modifier onlyEndpoint() {
        require(
            msg.sender == endpoint,
            "SequencerGated: caller is not the endpoint"
        );
        _;
    }
}

File 12 of 32 : Errors.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

// Trying to take an action on vertex when
string constant ERR_REQUIRES_DEPOSIT = "RS";

// ERC20 Transfer failed
string constant ERR_TRANSFER_FAILED = "TF";

// Unauthorized
string constant ERR_UNAUTHORIZED = "U";

// Invalid product
string constant ERR_INVALID_PRODUCT = "IP";

// Subaccount health too low
string constant ERR_SUBACCT_HEALTH = "SH";

// Not liquidatable
string constant ERR_NOT_LIQUIDATABLE = "NL";

// Liquidator health too low
string constant ERR_NOT_LIQUIDATABLE_INITIAL = "NLI";

// Liquidatee has positive initial health
string constant ERR_LIQUIDATED_TOO_MUCH = "LTM";

// Trying to liquidate quote, or
string constant ERR_INVALID_LIQUIDATION_PARAMS = "NILP";

// Trying to liquidate perp but the amount is not divisible by sizeIncrement
string constant ERR_INVALID_LIQUIDATION_AMOUNT = "NILA";

// Tried to liquidate too little, too much or signs are different
string constant ERR_NOT_LIQUIDATABLE_AMT = "NLA";

// Tried to liquidate liabilities before perps
string constant ERR_NOT_LIQUIDATABLE_LIABILITIES = "NLL";

// Tried to finalize subaccount that cannot be finalized
string constant ERR_NOT_FINALIZABLE_SUBACCOUNT = "NFS";

// Not enough quote to settle
string constant ERR_CANNOT_SETTLE = "CS";

// Not enough insurance to settle
string constant ERR_NO_INSURANCE = "NI";

// Above reserve ratio
string constant ERR_RESERVE_RATIO = "RR";

// Invalid socialize amount
string constant ERR_INVALID_SOCIALIZE_AMT = "ISA";

// Socializing product with no open interest
string constant ERR_NO_OPEN_INTEREST = "NOI";

// FOK not filled, this isn't rly an error so this is jank
string constant ERR_FOK_NOT_FILLED = "ENF";

// bad product config via weights
string constant ERR_BAD_PRODUCT_CONFIG = "BPC";

// subacct name too long
string constant ERR_LONG_NAME = "LN";

// already registered in health group
string constant ERR_ALREADY_REGISTERED = "AR";

// invalid health group provided
string constant ERR_INVALID_HEALTH_GROUP = "IHG";

string constant ERR_GETTING_ZERO_HEALTH_GROUP = "GZHG";

// trying to burn more LP than owned
string constant ERR_INSUFFICIENT_LP = "ILP";

// taker order subaccount fails risk or is invalid
string constant ERR_INVALID_TAKER = "IT";

// maker order subaccount fails risk or is invalid
string constant ERR_INVALID_MAKER = "IM";

string constant ERR_INVALID_SIGNATURE = "IS";

string constant ERR_ORDERS_CANNOT_BE_MATCHED = "OCBM";

string constant ERR_INVALID_LP_AMOUNT = "ILA";

string constant ERR_SLIPPAGE_TOO_HIGH = "STH";

string constant ERR_SUBACCOUNT_NOT_FOUND = "SNF";

string constant ERR_INVALID_PRICE = "IPR";

string constant ERR_INVALID_TIME = "ITI";

// states on node and engine are not same
string constant ERR_DSYNC = "DSYNC";

string constant ERR_INVALID_SWAP_PARAMS = "ISP";

string constant ERR_INVALID_REFERRAL_CODE = "IRC";

string constant ERR_CONVERSION_OVERFLOW = "CO";

string constant ERR_ONLY_CLEARINGHOUSE_CAN_SET_BOOK = "OCCSB";

// we match on containing these strings in sequencer
string constant ERR_INVALID_SUBMISSION_INDEX = "IX";
string constant ERR_NO_SLOW_MODE_TXS_REMAINING = "no slow mode transactions remaining";
string constant ERR_INVALID_COUNT = "IC";
string constant ERR_SLOW_TX_TOO_RECENT = "STTR";
string constant ERR_WALLET_NOT_TRANSFERABLE = "WNT";

string constant ERR_WALLET_SANCTIONED = "WS";

string constant ERR_SLOW_MODE_WRONG_SENDER = "SMWS";
string constant ERR_WRONG_NONCE = "WN";

// initially wanted to call this
// ERR_FULL_UTILIZATION but the shortened
// error string may make people mad on the frontend
string constant ERR_MAX_UTILIZATION = "MU";

string constant ERR_INVALID_RISK_GROUP = "IRG";

string constant ERR_VERIFY_SCHNORR = "VSR";

string constant ERR_DEPOSIT_TOO_SMALL = "DTS";

string constant ERR_CODE_NOT_MATCH = "CNM";

File 13 of 32 : ISpotEngine.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "./IProductEngine.sol";
import "../../libraries/RiskHelper.sol";

interface ISpotEngine is IProductEngine {
    struct Config {
        address token;
        int128 interestInflectionUtilX18;
        int128 interestFloorX18;
        int128 interestSmallCapX18;
        int128 interestLargeCapX18;
    }

    struct State {
        int128 cumulativeDepositsMultiplierX18;
        int128 cumulativeBorrowsMultiplierX18;
        int128 totalDepositsNormalized;
        int128 totalBorrowsNormalized;
    }

    struct Balance {
        int128 amount;
        int128 lastCumulativeMultiplierX18;
    }

    struct BalanceNormalized {
        int128 amountNormalized;
    }

    struct LpState {
        int128 supply;
        Balance quote;
        Balance base;
    }

    struct LpBalance {
        int128 amount;
    }

    struct Balances {
        BalanceNormalized balance;
        LpBalance lpBalance;
    }

    struct UpdateProductTx {
        uint32 productId;
        int128 sizeIncrement;
        int128 minSize;
        int128 lpSpreadX18;
        Config config;
        RiskHelper.RiskStore riskStore;
    }

    function getStateAndBalance(uint32 productId, bytes32 subaccount)
        external
        view
        returns (State memory, Balance memory);

    function getBalance(uint32 productId, bytes32 subaccount)
        external
        view
        returns (Balance memory);

    function getStatesAndBalances(uint32 productId, bytes32 subaccount)
        external
        view
        returns (
            LpState memory,
            LpBalance memory,
            State memory,
            Balance memory
        );

    function getConfig(uint32 productId) external view returns (Config memory);

    function getWithdrawFee(uint32 productId) external view returns (int128);

    function getToken(uint32 productId) external view returns (address);

    function updateBalance(
        uint32 productId,
        bytes32 subaccount,
        int128 amountDelta
    ) external;

    function updateBalance(
        uint32 productId,
        bytes32 subaccount,
        int128 amountDelta,
        int128 quoteDelta
    ) external;

    function updateQuoteFromInsurance(bytes32 subaccount, int128 insurance)
        external
        returns (int128);

    function updateStates(uint128 dt) external;

    function updateMinDepositRate(uint32 productId, int128 minDepositRateX18)
        external;

    function manualAssert(
        int128[] calldata totalDeposits,
        int128[] calldata totalBorrows
    ) external view;

    function socializeSubaccount(bytes32 subaccount) external;

    function assertUtilization(uint32 productId) external view;
}

File 14 of 32 : IPerpEngine.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "./IProductEngine.sol";
import "../../libraries/RiskHelper.sol";

interface IPerpEngine is IProductEngine {
    struct State {
        int128 cumulativeFundingLongX18;
        int128 cumulativeFundingShortX18;
        int128 availableSettle;
        int128 openInterest;
    }

    struct Balance {
        int128 amount;
        int128 vQuoteBalance;
        int128 lastCumulativeFundingX18;
    }

    struct LpState {
        int128 supply;
        // TODO: this should be removed; we can just get it from State.cumulativeFundingLongX18
        int128 lastCumulativeFundingX18;
        int128 cumulativeFundingPerLpX18;
        int128 base;
        int128 quote;
    }

    struct LpBalance {
        int128 amount;
        // NOTE: funding payments should be rolled
        // into Balance.vQuoteBalance;
        int128 lastCumulativeFundingX18;
    }

    struct UpdateProductTx {
        uint32 productId;
        int128 sizeIncrement;
        int128 minSize;
        int128 lpSpreadX18;
        RiskHelper.RiskStore riskStore;
    }

    function getStateAndBalance(uint32 productId, bytes32 subaccount)
        external
        view
        returns (State memory, Balance memory);

    function getBalance(uint32 productId, bytes32 subaccount)
        external
        view
        returns (Balance memory);

    function getStatesAndBalances(uint32 productId, bytes32 subaccount)
        external
        view
        returns (
            LpState memory,
            LpBalance memory,
            State memory,
            Balance memory
        );

    /// @dev Returns amount settled and emits SettlePnl events for each product
    function settlePnl(bytes32 subaccount, uint256 productIds)
        external
        returns (int128);

    function getSettlementState(uint32 productId, bytes32 subaccount)
        external
        view
        returns (
            int128 availableSettle,
            LpState memory lpState,
            LpBalance memory lpBalance,
            State memory state,
            Balance memory balance
        );

    function updateBalance(
        uint32 productId,
        bytes32 subaccount,
        int128 amountDelta,
        int128 vQuoteDelta
    ) external;

    function updateStates(uint128 dt, int128[] calldata avgPriceDiffs) external;

    function manualAssert(int128[] calldata openInterests) external view;

    function getPositionPnl(uint32 productId, bytes32 subaccount)
        external
        view
        returns (int128);

    function socializeSubaccount(bytes32 subaccount, int128 insurance)
        external
        returns (int128);
}

File 15 of 32 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (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 functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

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

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

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

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

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

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

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

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

File 16 of 32 : EIP712Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/cryptography/EIP712.sol)

pragma solidity ^0.8.0;

import "./ECDSAUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * _Available since v3.4._
 *
 * @custom:storage-size 52
 */
abstract contract EIP712Upgradeable is Initializable {
    /* solhint-disable var-name-mixedcase */
    bytes32 private _HASHED_NAME;
    bytes32 private _HASHED_VERSION;
    bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    /* solhint-enable var-name-mixedcase */

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
        __EIP712_init_unchained(name, version);
    }

    function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
        bytes32 hashedName = keccak256(bytes(name));
        bytes32 hashedVersion = keccak256(bytes(version));
        _HASHED_NAME = hashedName;
        _HASHED_VERSION = hashedVersion;
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
    }

    function _buildDomainSeparator(
        bytes32 typeHash,
        bytes32 nameHash,
        bytes32 versionHash
    ) private view returns (bytes32) {
        return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev The hash of the name parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712NameHash() internal virtual view returns (bytes32) {
        return _HASHED_NAME;
    }

    /**
     * @dev The hash of the version parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712VersionHash() internal virtual view returns (bytes32) {
        return _HASHED_VERSION;
    }

    /**
     * @dev 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 17 of 32 : ECDSAUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../StringsUpgradeable.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSAUpgradeable {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", StringsUpgradeable.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 18 of 32 : StringsUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/MathUpgradeable.sol";

/**
 * @dev String operations.
 */
library StringsUpgradeable {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = MathUpgradeable.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, MathUpgradeable.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

File 19 of 32 : MathUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library MathUpgradeable {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

File 20 of 32 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

File 21 of 32 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

File 22 of 32 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 23 of 32 : IClearinghouse.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "./IClearinghouseEventEmitter.sol";
import "../engine/IProductEngine.sol";
import "../IEndpoint.sol";
import "../IEndpointGated.sol";
import "../../libraries/RiskHelper.sol";

interface IClearinghouse is IClearinghouseEventEmitter, IEndpointGated {
    function addEngine(
        address engine,
        address offchainExchange,
        IProductEngine.EngineType engineType
    ) external;

    function registerProduct(uint32 productId) external;

    function transferQuote(IEndpoint.TransferQuote calldata tx) external;

    function depositCollateral(IEndpoint.DepositCollateral calldata tx)
        external;

    function withdrawCollateral(
        bytes32 sender,
        uint32 productId,
        uint128 amount,
        address sendTo
    ) external;

    function mintLp(IEndpoint.MintLp calldata tx) external;

    function burnLp(IEndpoint.BurnLp calldata tx) external;

    function liquidateSubaccount(IEndpoint.LiquidateSubaccount calldata tx)
        external;

    function depositInsurance(bytes calldata transaction) external;

    function settlePnl(bytes calldata transaction) external;

    function claimSequencerFees(
        IEndpoint.ClaimSequencerFees calldata tx,
        int128[] calldata fees
    ) external;

    /// @notice Retrieve quote ERC20 address
    function getQuote() external view returns (address);

    /// @notice Returns the registered engine address by type
    function getEngineByType(IProductEngine.EngineType engineType)
        external
        view
        returns (address);

    /// @notice Returns the engine associated with a product ID
    function getEngineByProduct(uint32 productId)
        external
        view
        returns (address);

    /// @notice Returns health for the subaccount across all engines
    function getHealth(bytes32 subaccount, IProductEngine.HealthType healthType)
        external
        view
        returns (int128);

    /// @notice Returns the amount of insurance remaining in this clearinghouse
    function getInsurance() external view returns (int128);

    function getSpreads() external view returns (uint256);

    function upgradeClearinghouseLiq(address _clearinghouseLiq) external;

    function getClearinghouseLiq() external view returns (address);

    function burnLpAndTransfer(IEndpoint.BurnLpAndTransfer calldata txn)
        external;

    function requireMinDeposit(uint32 productId, uint128 amount) external;

    function assertCode(bytes calldata tx) external;

    function manualAssert(bytes calldata tx) external;

    function getWithdrawPool() external view returns (address);

    function setWithdrawPool(address _withdrawPool) external;
}

File 24 of 32 : RiskHelper.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "./MathSD21x18.sol";
import "../interfaces/engine/IProductEngine.sol";
import "../common/Constants.sol";
import "../common/Errors.sol";
import "./MathHelper.sol";

/// @title RiskHelper
/// @dev Provides basic math functions
library RiskHelper {
    using MathSD21x18 for int128;

    struct RiskStore {
        // these weights are all
        // between 0 and 2
        // these integers are the real
        // weights times 1e9
        int32 longWeightInitial;
        int32 shortWeightInitial;
        int32 longWeightMaintenance;
        int32 shortWeightMaintenance;
        int128 priceX18;
    }

    struct Risk {
        int128 longWeightInitialX18;
        int128 shortWeightInitialX18;
        int128 longWeightMaintenanceX18;
        int128 shortWeightMaintenanceX18;
        int128 priceX18;
    }

    function _getSpreadHealthRebateAmount(
        Risk memory perpRisk,
        int128 basisAmount,
        int128 priceSumX18,
        IProductEngine.HealthType healthType
    ) internal pure returns (int128) {
        // 5x more leverage than the standard perp
        // by refunding 4/5 of the health penalty
        int128 rebateRateX18 = ((ONE - _getWeightX18(perpRisk, 1, healthType)) *
            4) / 5;
        return rebateRateX18.mul(priceSumX18).mul(basisAmount);
    }

    function _getLpRawValue(
        int128 baseAmount,
        int128 quoteAmount,
        int128 priceX18
    ) internal pure returns (int128) {
        // naive way: value an LP token by value of the raw components 2 * arithmetic mean of base value and quote value
        // price manipulation proof way: use the geometric mean
        return
            2 *
            int128(
                MathHelper.sqrt256(
                    int256(baseAmount.mul(priceX18)) * quoteAmount
                )
            );
    }

    function _getWeightX18(
        Risk memory risk,
        int128 amount,
        IProductEngine.HealthType healthType
    ) internal pure returns (int128) {
        // (1 + imf * sqrt(amount))
        if (healthType == IProductEngine.HealthType.PNL) {
            return ONE;
        }

        int128 weight;
        if (amount >= 0) {
            weight = healthType == IProductEngine.HealthType.INITIAL
                ? risk.longWeightInitialX18
                : risk.longWeightMaintenanceX18;
        } else {
            weight = healthType == IProductEngine.HealthType.INITIAL
                ? risk.shortWeightInitialX18
                : risk.shortWeightMaintenanceX18;
        }

        return weight;
    }

    function isoGroup(uint32 productId) internal pure returns (uint32) {
        require(productId < 256, "unimplemented");
        // return productId >= 256 ? productId : 0;
        return 0;
    }

    function canTrade(bytes32 subaccount, uint32 productId)
        internal
        pure
        returns (bool)
    {
        require(
            isoGroup(subaccount) == 0 && isoGroup(productId) == 0,
            "unimplemented"
        );
        // if (productId == QUOTE_PRODUCT_ID) {
        //   return true;
        // }
        // return isoGroup(subaccount) == isoGroup(productId);
        return true;
    }

    // subaccount names encoding:
    // if endswith "iso" -> isolated margin account
    // productId a u32 specified by the 4 bytes before "iso"
    // i.e. |5 bytes| 4 byte product id| 'iso' |

    // frontend accounts are identified by starting with "defau"
    // if you are a frontend account, we use the linked signer
    // of the default frontend cross margin account:
    // |default| 0 0 0 0 0|

    function isoGroup(
        bytes32 /* subaccount */
    ) internal pure returns (uint32) {
        // uint256 s = uint256(subaccount);
        // int.from_bytes(b'iso', byteorder='big')
        // if (uint24(s) == 6910831) {
        //     return uint32(s >> 24);
        // }
        return 0;
    }

    // if its a default frontend subaccount
    function isFrontendAccount(bytes32 subaccount)
        internal
        pure
        returns (bool)
    {
        /// int.from_bytes(b'defau', byteorder='big') << 56
        return
            (uint256(subaccount) & 31071085969061750775199301632) ==
            31071085969061750775199301632;
    }

    function defaultFrontendAccount(bytes32 sender)
        internal
        pure
        returns (bytes32)
    {
        uint256 s = uint256(sender);
        // (((1 << 21) - 1) << 96) | (int.from_bytes(b'default', byteorder='big') << 40)
        s &= 166153451316037938940915905023967232;
        // int.from_bytes(b'default', byteorder='big') << 40
        s |= 31071085969092277616032874496;
        return bytes32(s);
    }
}

File 25 of 32 : IClearinghouseEventEmitter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

interface IClearinghouseEventEmitter {
    /// @notice Emitted during initialization
    event ClearinghouseInitialized(address endpoint, address quote);

    /// @notice Emitted when collateral is modified for a subaccount
    event ModifyCollateral(
        int128 amount,
        bytes32 indexed subaccount,
        uint32 productId
    );

    event Liquidation(
        bytes32 indexed liquidatorSubaccount,
        bytes32 indexed liquidateeSubaccount,
        uint32 productId,
        bool isEncodedSpread,
        int128 amount,
        int128 amountQuote
    );
}

File 26 of 32 : IEndpoint.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "./clearinghouse/IClearinghouse.sol";

interface IEndpoint {
    event SubmitTransactions();

    // events that we parse transactions into
    enum TransactionType {
        LiquidateSubaccount,
        DepositCollateral,
        WithdrawCollateral,
        SpotTick,
        UpdatePrice,
        SettlePnl,
        MatchOrders,
        DepositInsurance,
        ExecuteSlowMode,
        MintLp,
        BurnLp,
        SwapAMM,
        MatchOrderAMM,
        DumpFees,
        ClaimSequencerFees,
        PerpTick,
        ManualAssert,
        Rebate, // deprecated
        UpdateProduct,
        LinkSigner,
        UpdateFeeRates,
        BurnLpAndTransfer,
        MatchOrdersRFQ,
        TransferQuote,
        RebalanceXWithdraw,
        UpdateMinDepositRate,
        AssertCode
    }

    struct UpdateProduct {
        address engine;
        bytes tx;
    }

    /// requires signature from sender
    enum LiquidationMode {
        SPREAD,
        SPOT,
        PERP
    }

    struct LegacyLiquidateSubaccount {
        bytes32 sender;
        bytes32 liquidatee;
        uint8 mode;
        uint32 healthGroup;
        int128 amount;
        uint64 nonce;
    }

    struct LiquidateSubaccount {
        bytes32 sender;
        bytes32 liquidatee;
        uint32 productId;
        bool isEncodedSpread;
        int128 amount;
        uint64 nonce;
    }

    struct LegacySignedLiquidateSubaccount {
        LegacyLiquidateSubaccount tx;
        bytes signature;
    }

    struct SignedLiquidateSubaccount {
        LiquidateSubaccount tx;
        bytes signature;
    }

    struct DepositCollateral {
        bytes32 sender;
        uint32 productId;
        uint128 amount;
    }

    struct SignedDepositCollateral {
        DepositCollateral tx;
        bytes signature;
    }

    struct WithdrawCollateral {
        bytes32 sender;
        uint32 productId;
        uint128 amount;
        uint64 nonce;
    }

    struct SignedWithdrawCollateral {
        WithdrawCollateral tx;
        bytes signature;
    }

    struct MintLp {
        bytes32 sender;
        uint32 productId;
        uint128 amountBase;
        uint128 quoteAmountLow;
        uint128 quoteAmountHigh;
        uint64 nonce;
    }

    struct SignedMintLp {
        MintLp tx;
        bytes signature;
    }

    struct BurnLp {
        bytes32 sender;
        uint32 productId;
        uint128 amount;
        uint64 nonce;
    }

    struct SignedBurnLp {
        BurnLp tx;
        bytes signature;
    }

    struct LinkSigner {
        bytes32 sender;
        bytes32 signer;
        uint64 nonce;
    }

    struct SignedLinkSigner {
        LinkSigner tx;
        bytes signature;
    }

    /// callable by endpoint; no signature verifications needed
    struct PerpTick {
        uint128 time;
        int128[] avgPriceDiffs;
    }

    struct LegacySpotTick {
        uint128 time;
    }

    struct SpotTick {
        uint128 time;
        // utilization ratio across all chains
        int128[] utilizationRatiosX18;
    }

    struct ManualAssert {
        int128[] openInterests;
        int128[] totalDeposits;
        int128[] totalBorrows;
    }

    struct AssertCode {
        string[] contractNames;
        bytes32[] codeHashes;
    }

    struct Rebate {
        bytes32[] subaccounts;
        int128[] amounts;
    }

    struct UpdateFeeRates {
        address user;
        uint32 productId;
        // the absolute value of fee rates can't be larger than 100%,
        // so their X18 values are in the range [-1e18, 1e18], which
        // can be stored by using int64.
        int64 makerRateX18;
        int64 takerRateX18;
    }

    struct ClaimSequencerFees {
        bytes32 subaccount;
    }

    struct RebalanceXWithdraw {
        uint32 productId;
        uint128 amount;
        address sendTo;
    }

    struct UpdateMinDepositRate {
        uint32 productId;
        int128 minDepositRateX18;
    }

    struct UpdatePrice {
        uint32 productId;
        int128 priceX18;
    }

    struct SettlePnl {
        bytes32[] subaccounts;
        uint256[] productIds;
    }

    /// matching
    struct Order {
        bytes32 sender;
        int128 priceX18;
        int128 amount;
        uint64 expiration;
        uint64 nonce;
    }

    struct SignedOrder {
        Order order;
        bytes signature;
    }

    struct LegacyMatchOrders {
        uint32 productId;
        bool amm;
        SignedOrder taker;
        SignedOrder maker;
    }

    struct MatchOrders {
        uint32 productId;
        SignedOrder taker;
        SignedOrder maker;
    }

    struct MatchOrdersWithSigner {
        MatchOrders matchOrders;
        address takerLinkedSigner;
        address makerLinkedSigner;
    }

    // just swap against AMM -- theres no maker order
    struct MatchOrderAMM {
        uint32 productId;
        int128 baseDelta;
        int128 quoteDelta;
        SignedOrder taker;
    }

    struct SwapAMM {
        bytes32 sender;
        uint32 productId;
        int128 amount;
        int128 priceX18;
    }

    struct DepositInsurance {
        uint128 amount;
    }

    struct SlowModeTx {
        uint64 executableAt;
        address sender;
        bytes tx;
    }

    struct SlowModeConfig {
        uint64 timeout;
        uint64 txCount;
        uint64 txUpTo;
    }

    // legacy :(
    struct Prices {
        int128 spotPriceX18;
        int128 perpPriceX18;
    }

    struct BurnLpAndTransfer {
        bytes32 sender;
        uint32 productId;
        uint128 amount;
        bytes32 recipient;
    }

    struct TransferQuote {
        bytes32 sender;
        bytes32 recipient;
        uint128 amount;
        uint64 nonce;
    }

    struct SignedTransferQuote {
        TransferQuote tx;
        bytes signature;
    }

    function depositCollateral(
        bytes12 subaccountName,
        uint32 productId,
        uint128 amount
    ) external;

    function depositCollateralWithReferral(
        bytes12 subaccountName,
        uint32 productId,
        uint128 amount,
        string calldata referralCode
    ) external;

    function depositCollateralWithReferral(
        bytes32 subaccount,
        uint32 productId,
        uint128 amount,
        string calldata referralCode
    ) external;

    function submitSlowModeTransaction(bytes calldata transaction) external;

    function getTime() external view returns (uint128);

    function getSequencer() external view returns (address);

    function getNonce(address sender) external view returns (uint64);

    function getOffchainExchange() external view returns (address);

    function getPriceX18(uint32 productId) external view returns (int128);
}

File 27 of 32 : IEndpointGated.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.13;

import "./IEndpoint.sol";

interface IEndpointGated {
    function getEndpoint() external view returns (address endpoint);
}

File 28 of 32 : PRBMathSD59x18.sol
// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;

import "./PRBMath.sol";

/// @title PRBMathSD59x18
/// @author Paul Razvan Berg
/// @notice Smart contract library for advanced fixed-point math that works with int256 numbers considered to have 18
/// trailing decimals. We call this number representation signed 59.18-decimal fixed-point, since the numbers can have
/// a sign and there can be up to 59 digits in the integer part and up to 18 decimals in the fractional part. The numbers
/// are bound by the minimum and the maximum values permitted by the Solidity type int256.
library PRBMathSD59x18 {
    /// @dev log2(e) as a signed 59.18-decimal fixed-point number.
    int256 internal constant LOG2_E = 1_442695040888963407;

    /// @dev Half the SCALE number.
    int256 internal constant HALF_SCALE = 5e17;

    /// @dev The maximum value a signed 59.18-decimal fixed-point number can have.
    int256 internal constant MAX_SD59x18 =
        57896044618658097711785492504343953926634992332820282019728_792003956564819967;

    /// @dev The maximum whole value a signed 59.18-decimal fixed-point number can have.
    int256 internal constant MAX_WHOLE_SD59x18 =
        57896044618658097711785492504343953926634992332820282019728_000000000000000000;

    /// @dev The minimum value a signed 59.18-decimal fixed-point number can have.
    int256 internal constant MIN_SD59x18 =
        -57896044618658097711785492504343953926634992332820282019728_792003956564819968;

    /// @dev The minimum whole value a signed 59.18-decimal fixed-point number can have.
    int256 internal constant MIN_WHOLE_SD59x18 =
        -57896044618658097711785492504343953926634992332820282019728_000000000000000000;

    /// @dev How many trailing decimals can be represented.
    int256 internal constant SCALE = 1e18;

    /// INTERNAL FUNCTIONS ///

    /// @notice Calculate the absolute value of x.
    ///
    /// @dev Requirements:
    /// - x must be greater than MIN_SD59x18.
    ///
    /// @param x The number to calculate the absolute value for.
    /// @param result The absolute value of x.
    function abs(int256 x) internal pure returns (int256 result) {
        unchecked {
            if (x == MIN_SD59x18) {
                revert PRBMathSD59x18__AbsInputTooSmall();
            }
            result = x < 0 ? -x : x;
        }
    }

    /// @notice Calculates the arithmetic average of x and y, rounding down.
    /// @param x The first operand as a signed 59.18-decimal fixed-point number.
    /// @param y The second operand as a signed 59.18-decimal fixed-point number.
    /// @return result The arithmetic average as a signed 59.18-decimal fixed-point number.
    function avg(int256 x, int256 y) internal pure returns (int256 result) {
        // The operations can never overflow.
        unchecked {
            int256 sum = (x >> 1) + (y >> 1);
            if (sum < 0) {
                // If at least one of x and y is odd, we add 1 to the result. This is because shifting negative numbers to the
                // right rounds down to infinity.
                assembly {
                    result := add(sum, and(or(x, y), 1))
                }
            } else {
                // If both x and y are odd, we add 1 to the result. This is because if both numbers are odd, the 0.5
                // remainder gets truncated twice.
                result = sum + (x & y & 1);
            }
        }
    }

    /// @notice Yields the least greatest signed 59.18 decimal fixed-point number greater than or equal to x.
    ///
    /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
    /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
    ///
    /// Requirements:
    /// - x must be less than or equal to MAX_WHOLE_SD59x18.
    ///
    /// @param x The signed 59.18-decimal fixed-point number to ceil.
    /// @param result The least integer greater than or equal to x, as a signed 58.18-decimal fixed-point number.
    function ceil(int256 x) internal pure returns (int256 result) {
        if (x > MAX_WHOLE_SD59x18) {
            revert PRBMathSD59x18__CeilOverflow(x);
        }
        unchecked {
            int256 remainder = x % SCALE;
            if (remainder == 0) {
                result = x;
            } else {
                // Solidity uses C fmod style, which returns a modulus with the same sign as x.
                result = x - remainder;
                if (x > 0) {
                    result += SCALE;
                }
            }
        }
    }

    /// @notice Divides two signed 59.18-decimal fixed-point numbers, returning a new signed 59.18-decimal fixed-point number.
    ///
    /// @dev Variant of "mulDiv" that works with signed numbers. Works by computing the signs and the absolute values separately.
    ///
    /// Requirements:
    /// - All from "PRBMath.mulDiv".
    /// - None of the inputs can be MIN_SD59x18.
    /// - The denominator cannot be zero.
    /// - The result must fit within int256.
    ///
    /// Caveats:
    /// - All from "PRBMath.mulDiv".
    ///
    /// @param x The numerator as a signed 59.18-decimal fixed-point number.
    /// @param y The denominator as a signed 59.18-decimal fixed-point number.
    /// @param result The quotient as a signed 59.18-decimal fixed-point number.
    function div(int256 x, int256 y) internal pure returns (int256 result) {
        if (x == MIN_SD59x18 || y == MIN_SD59x18) {
            revert PRBMathSD59x18__DivInputTooSmall();
        }

        // Get hold of the absolute values of x and y.
        uint256 ax;
        uint256 ay;
        unchecked {
            ax = x < 0 ? uint256(-x) : uint256(x);
            ay = y < 0 ? uint256(-y) : uint256(y);
        }

        // Compute the absolute value of (x*SCALE)÷y. The result must fit within int256.
        uint256 rAbs = PRBMath.mulDiv(ax, uint256(SCALE), ay);
        if (rAbs > uint256(MAX_SD59x18)) {
            revert PRBMathSD59x18__DivOverflow(rAbs);
        }

        // Get the signs of x and y.
        uint256 sx;
        uint256 sy;
        assembly {
            sx := sgt(x, sub(0, 1))
            sy := sgt(y, sub(0, 1))
        }

        // XOR over sx and sy. This is basically checking whether the inputs have the same sign. If yes, the result
        // should be positive. Otherwise, it should be negative.
        result = sx ^ sy == 1 ? -int256(rAbs) : int256(rAbs);
    }

    /// @notice Returns Euler's number as a signed 59.18-decimal fixed-point number.
    /// @dev See https://en.wikipedia.org/wiki/E_(mathematical_constant).
    function e() internal pure returns (int256 result) {
        result = 2_718281828459045235;
    }

    /// @notice Calculates the natural exponent of x.
    ///
    /// @dev Based on the insight that e^x = 2^(x * log2(e)).
    ///
    /// Requirements:
    /// - All from "log2".
    /// - x must be less than 133.084258667509499441.
    ///
    /// Caveats:
    /// - All from "exp2".
    /// - For any x less than -41.446531673892822322, the result is zero.
    ///
    /// @param x The exponent as a signed 59.18-decimal fixed-point number.
    /// @return result The result as a signed 59.18-decimal fixed-point number.
    function exp(int256 x) internal pure returns (int256 result) {
        // Without this check, the value passed to "exp2" would be less than -59.794705707972522261.
        if (x < -41_446531673892822322) {
            return 0;
        }

        // Without this check, the value passed to "exp2" would be greater than 192.
        if (x >= 133_084258667509499441) {
            revert PRBMathSD59x18__ExpInputTooBig(x);
        }

        // Do the fixed-point multiplication inline to save gas.
        unchecked {
            int256 doubleScaleProduct = x * LOG2_E;
            result = exp2((doubleScaleProduct + HALF_SCALE) / SCALE);
        }
    }

    /// @notice Calculates the binary exponent of x using the binary fraction method.
    ///
    /// @dev See https://ethereum.stackexchange.com/q/79903/24693.
    ///
    /// Requirements:
    /// - x must be 192 or less.
    /// - The result must fit within MAX_SD59x18.
    ///
    /// Caveats:
    /// - For any x less than -59.794705707972522261, the result is zero.
    ///
    /// @param x The exponent as a signed 59.18-decimal fixed-point number.
    /// @return result The result as a signed 59.18-decimal fixed-point number.
    function exp2(int256 x) internal pure returns (int256 result) {
        // This works because 2^(-x) = 1/2^x.
        if (x < 0) {
            // 2^59.794705707972522262 is the maximum number whose inverse does not truncate down to zero.
            if (x < -59_794705707972522261) {
                return 0;
            }

            // Do the fixed-point inversion inline to save gas. The numerator is SCALE * SCALE.
            unchecked {
                result = 1e36 / exp2(-x);
            }
        } else {
            // 2^192 doesn't fit within the 192.64-bit format used internally in this function.
            if (x >= 192e18) {
                revert PRBMathSD59x18__Exp2InputTooBig(x);
            }

            unchecked {
                // Convert x to the 192.64-bit fixed-point format.
                uint256 x192x64 = (uint256(x) << 64) / uint256(SCALE);

                // Safe to convert the result to int256 directly because the maximum input allowed is 192.
                result = int256(PRBMath.exp2(x192x64));
            }
        }
    }

    /// @notice Yields the greatest signed 59.18 decimal fixed-point number less than or equal to x.
    ///
    /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
    /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
    ///
    /// Requirements:
    /// - x must be greater than or equal to MIN_WHOLE_SD59x18.
    ///
    /// @param x The signed 59.18-decimal fixed-point number to floor.
    /// @param result The greatest integer less than or equal to x, as a signed 58.18-decimal fixed-point number.
    function floor(int256 x) internal pure returns (int256 result) {
        if (x < MIN_WHOLE_SD59x18) {
            revert PRBMathSD59x18__FloorUnderflow(x);
        }
        unchecked {
            int256 remainder = x % SCALE;
            if (remainder == 0) {
                result = x;
            } else {
                // Solidity uses C fmod style, which returns a modulus with the same sign as x.
                result = x - remainder;
                if (x < 0) {
                    result -= SCALE;
                }
            }
        }
    }

    /// @notice Yields the excess beyond the floor of x for positive numbers and the part of the number to the right
    /// of the radix point for negative numbers.
    /// @dev Based on the odd function definition. https://en.wikipedia.org/wiki/Fractional_part
    /// @param x The signed 59.18-decimal fixed-point number to get the fractional part of.
    /// @param result The fractional part of x as a signed 59.18-decimal fixed-point number.
    function frac(int256 x) internal pure returns (int256 result) {
        unchecked {
            result = x % SCALE;
        }
    }

    /// @notice Converts a number from basic integer form to signed 59.18-decimal fixed-point representation.
    ///
    /// @dev Requirements:
    /// - x must be greater than or equal to MIN_SD59x18 divided by SCALE.
    /// - x must be less than or equal to MAX_SD59x18 divided by SCALE.
    ///
    /// @param x The basic integer to convert.
    /// @param result The same number in signed 59.18-decimal fixed-point representation.
    function fromInt(int256 x) internal pure returns (int256 result) {
        unchecked {
            if (x < MIN_SD59x18 / SCALE) {
                revert PRBMathSD59x18__FromIntUnderflow(x);
            }
            if (x > MAX_SD59x18 / SCALE) {
                revert PRBMathSD59x18__FromIntOverflow(x);
            }
            result = x * SCALE;
        }
    }

    /// @notice Calculates geometric mean of x and y, i.e. sqrt(x * y), rounding down.
    ///
    /// @dev Requirements:
    /// - x * y must fit within MAX_SD59x18, lest it overflows.
    /// - x * y cannot be negative.
    ///
    /// @param x The first operand as a signed 59.18-decimal fixed-point number.
    /// @param y The second operand as a signed 59.18-decimal fixed-point number.
    /// @return result The result as a signed 59.18-decimal fixed-point number.
    function gm(int256 x, int256 y) internal pure returns (int256 result) {
        if (x == 0) {
            return 0;
        }

        unchecked {
            // Checking for overflow this way is faster than letting Solidity do it.
            int256 xy = x * y;
            if (xy / x != y) {
                revert PRBMathSD59x18__GmOverflow(x, y);
            }

            // The product cannot be negative.
            if (xy < 0) {
                revert PRBMathSD59x18__GmNegativeProduct(x, y);
            }

            // We don't need to multiply by the SCALE here because the x*y product had already picked up a factor of SCALE
            // during multiplication. See the comments within the "sqrt" function.
            result = int256(PRBMath.sqrt(uint256(xy)));
        }
    }

    /// @notice Calculates 1 / x, rounding toward zero.
    ///
    /// @dev Requirements:
    /// - x cannot be zero.
    ///
    /// @param x The signed 59.18-decimal fixed-point number for which to calculate the inverse.
    /// @return result The inverse as a signed 59.18-decimal fixed-point number.
    function inv(int256 x) internal pure returns (int256 result) {
        unchecked {
            // 1e36 is SCALE * SCALE.
            result = 1e36 / x;
        }
    }

    /// @notice Calculates the natural logarithm of x.
    ///
    /// @dev Based on the insight that ln(x) = log2(x) / log2(e).
    ///
    /// Requirements:
    /// - All from "log2".
    ///
    /// Caveats:
    /// - All from "log2".
    /// - This doesn't return exactly 1 for 2718281828459045235, for that we would need more fine-grained precision.
    ///
    /// @param x The signed 59.18-decimal fixed-point number for which to calculate the natural logarithm.
    /// @return result The natural logarithm as a signed 59.18-decimal fixed-point number.
    function ln(int256 x) internal pure returns (int256 result) {
        // Do the fixed-point multiplication inline to save gas. This is overflow-safe because the maximum value that log2(x)
        // can return is 195205294292027477728.
        unchecked {
            result = (log2(x) * SCALE) / LOG2_E;
        }
    }

    /// @notice Calculates the common logarithm of x.
    ///
    /// @dev First checks if x is an exact power of ten and it stops if yes. If it's not, calculates the common
    /// logarithm based on the insight that log10(x) = log2(x) / log2(10).
    ///
    /// Requirements:
    /// - All from "log2".
    ///
    /// Caveats:
    /// - All from "log2".
    ///
    /// @param x The signed 59.18-decimal fixed-point number for which to calculate the common logarithm.
    /// @return result The common logarithm as a signed 59.18-decimal fixed-point number.
    function log10(int256 x) internal pure returns (int256 result) {
        if (x <= 0) {
            revert PRBMathSD59x18__LogInputTooSmall(x);
        }

        // Note that the "mul" in this block is the assembly mul operation, not the "mul" function defined in this contract.
        // prettier-ignore
        assembly {
            switch x
            case 1 { result := mul(SCALE, sub(0, 18)) }
            case 10 { result := mul(SCALE, sub(1, 18)) }
            case 100 { result := mul(SCALE, sub(2, 18)) }
            case 1000 { result := mul(SCALE, sub(3, 18)) }
            case 10000 { result := mul(SCALE, sub(4, 18)) }
            case 100000 { result := mul(SCALE, sub(5, 18)) }
            case 1000000 { result := mul(SCALE, sub(6, 18)) }
            case 10000000 { result := mul(SCALE, sub(7, 18)) }
            case 100000000 { result := mul(SCALE, sub(8, 18)) }
            case 1000000000 { result := mul(SCALE, sub(9, 18)) }
            case 10000000000 { result := mul(SCALE, sub(10, 18)) }
            case 100000000000 { result := mul(SCALE, sub(11, 18)) }
            case 1000000000000 { result := mul(SCALE, sub(12, 18)) }
            case 10000000000000 { result := mul(SCALE, sub(13, 18)) }
            case 100000000000000 { result := mul(SCALE, sub(14, 18)) }
            case 1000000000000000 { result := mul(SCALE, sub(15, 18)) }
            case 10000000000000000 { result := mul(SCALE, sub(16, 18)) }
            case 100000000000000000 { result := mul(SCALE, sub(17, 18)) }
            case 1000000000000000000 { result := 0 }
            case 10000000000000000000 { result := SCALE }
            case 100000000000000000000 { result := mul(SCALE, 2) }
            case 1000000000000000000000 { result := mul(SCALE, 3) }
            case 10000000000000000000000 { result := mul(SCALE, 4) }
            case 100000000000000000000000 { result := mul(SCALE, 5) }
            case 1000000000000000000000000 { result := mul(SCALE, 6) }
            case 10000000000000000000000000 { result := mul(SCALE, 7) }
            case 100000000000000000000000000 { result := mul(SCALE, 8) }
            case 1000000000000000000000000000 { result := mul(SCALE, 9) }
            case 10000000000000000000000000000 { result := mul(SCALE, 10) }
            case 100000000000000000000000000000 { result := mul(SCALE, 11) }
            case 1000000000000000000000000000000 { result := mul(SCALE, 12) }
            case 10000000000000000000000000000000 { result := mul(SCALE, 13) }
            case 100000000000000000000000000000000 { result := mul(SCALE, 14) }
            case 1000000000000000000000000000000000 { result := mul(SCALE, 15) }
            case 10000000000000000000000000000000000 { result := mul(SCALE, 16) }
            case 100000000000000000000000000000000000 { result := mul(SCALE, 17) }
            case 1000000000000000000000000000000000000 { result := mul(SCALE, 18) }
            case 10000000000000000000000000000000000000 { result := mul(SCALE, 19) }
            case 100000000000000000000000000000000000000 { result := mul(SCALE, 20) }
            case 1000000000000000000000000000000000000000 { result := mul(SCALE, 21) }
            case 10000000000000000000000000000000000000000 { result := mul(SCALE, 22) }
            case 100000000000000000000000000000000000000000 { result := mul(SCALE, 23) }
            case 1000000000000000000000000000000000000000000 { result := mul(SCALE, 24) }
            case 10000000000000000000000000000000000000000000 { result := mul(SCALE, 25) }
            case 100000000000000000000000000000000000000000000 { result := mul(SCALE, 26) }
            case 1000000000000000000000000000000000000000000000 { result := mul(SCALE, 27) }
            case 10000000000000000000000000000000000000000000000 { result := mul(SCALE, 28) }
            case 100000000000000000000000000000000000000000000000 { result := mul(SCALE, 29) }
            case 1000000000000000000000000000000000000000000000000 { result := mul(SCALE, 30) }
            case 10000000000000000000000000000000000000000000000000 { result := mul(SCALE, 31) }
            case 100000000000000000000000000000000000000000000000000 { result := mul(SCALE, 32) }
            case 1000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 33) }
            case 10000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 34) }
            case 100000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 35) }
            case 1000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 36) }
            case 10000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 37) }
            case 100000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 38) }
            case 1000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 39) }
            case 10000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 40) }
            case 100000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 41) }
            case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 42) }
            case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 43) }
            case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 44) }
            case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 45) }
            case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 46) }
            case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 47) }
            case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 48) }
            case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 49) }
            case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 50) }
            case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 51) }
            case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 52) }
            case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 53) }
            case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 54) }
            case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 55) }
            case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 56) }
            case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 57) }
            case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 58) }
            default {
                result := MAX_SD59x18
            }
        }

        if (result == MAX_SD59x18) {
            // Do the fixed-point division inline to save gas. The denominator is log2(10).
            unchecked {
                result = (log2(x) * SCALE) / 3_321928094887362347;
            }
        }
    }

    /// @notice Calculates the binary logarithm of x.
    ///
    /// @dev Based on the iterative approximation algorithm.
    /// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
    ///
    /// Requirements:
    /// - x must be greater than zero.
    ///
    /// Caveats:
    /// - The results are not perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation.
    ///
    /// @param x The signed 59.18-decimal fixed-point number for which to calculate the binary logarithm.
    /// @return result The binary logarithm as a signed 59.18-decimal fixed-point number.
    function log2(int256 x) internal pure returns (int256 result) {
        if (x <= 0) {
            revert PRBMathSD59x18__LogInputTooSmall(x);
        }
        unchecked {
            // This works because log2(x) = -log2(1/x).
            int256 sign;
            if (x >= SCALE) {
                sign = 1;
            } else {
                sign = -1;
                // Do the fixed-point inversion inline to save gas. The numerator is SCALE * SCALE.
                assembly {
                    x := div(1000000000000000000000000000000000000, x)
                }
            }

            // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n).
            uint256 n = PRBMath.mostSignificantBit(uint256(x / SCALE));

            // The integer part of the logarithm as a signed 59.18-decimal fixed-point number. The operation can't overflow
            // because n is maximum 255, SCALE is 1e18 and sign is either 1 or -1.
            result = int256(n) * SCALE;

            // This is y = x * 2^(-n).
            int256 y = x >> n;

            // If y = 1, the fractional part is zero.
            if (y == SCALE) {
                return result * sign;
            }

            // Calculate the fractional part via the iterative approximation.
            // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster.
            for (int256 delta = int256(HALF_SCALE); delta > 0; delta >>= 1) {
                y = (y * y) / SCALE;

                // Is y^2 > 2 and so in the range [2,4)?
                if (y >= 2 * SCALE) {
                    // Add the 2^(-m) factor to the logarithm.
                    result += delta;

                    // Corresponds to z/2 on Wikipedia.
                    y >>= 1;
                }
            }
            result *= sign;
        }
    }

    /// @notice Multiplies two signed 59.18-decimal fixed-point numbers together, returning a new signed 59.18-decimal
    /// fixed-point number.
    ///
    /// @dev Variant of "mulDiv" that works with signed numbers and employs constant folding, i.e. the denominator is
    /// always 1e18.
    ///
    /// Requirements:
    /// - All from "PRBMath.mulDivFixedPoint".
    /// - None of the inputs can be MIN_SD59x18
    /// - The result must fit within MAX_SD59x18.
    ///
    /// Caveats:
    /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMath.mulDiv" to understand how this works.
    ///
    /// @param x The multiplicand as a signed 59.18-decimal fixed-point number.
    /// @param y The multiplier as a signed 59.18-decimal fixed-point number.
    /// @return result The product as a signed 59.18-decimal fixed-point number.
    function mul(int256 x, int256 y) internal pure returns (int256 result) {
        if (x == MIN_SD59x18 || y == MIN_SD59x18) {
            revert PRBMathSD59x18__MulInputTooSmall();
        }

        unchecked {
            uint256 ax;
            uint256 ay;
            ax = x < 0 ? uint256(-x) : uint256(x);
            ay = y < 0 ? uint256(-y) : uint256(y);

            uint256 rAbs = PRBMath.mulDivFixedPoint(ax, ay);
            if (rAbs > uint256(MAX_SD59x18)) {
                revert PRBMathSD59x18__MulOverflow(rAbs);
            }

            uint256 sx;
            uint256 sy;
            assembly {
                sx := sgt(x, sub(0, 1))
                sy := sgt(y, sub(0, 1))
            }
            result = sx ^ sy == 1 ? -int256(rAbs) : int256(rAbs);
        }
    }

    /// @notice Returns PI as a signed 59.18-decimal fixed-point number.
    function pi() internal pure returns (int256 result) {
        result = 3_141592653589793238;
    }

    /// @notice Raises x to the power of y.
    ///
    /// @dev Based on the insight that x^y = 2^(log2(x) * y).
    ///
    /// Requirements:
    /// - All from "exp2", "log2" and "mul".
    /// - z cannot be zero.
    ///
    /// Caveats:
    /// - All from "exp2", "log2" and "mul".
    /// - Assumes 0^0 is 1.
    ///
    /// @param x Number to raise to given power y, as a signed 59.18-decimal fixed-point number.
    /// @param y Exponent to raise x to, as a signed 59.18-decimal fixed-point number.
    /// @return result x raised to power y, as a signed 59.18-decimal fixed-point number.
    function pow(int256 x, int256 y) internal pure returns (int256 result) {
        if (x == 0) {
            result = y == 0 ? SCALE : int256(0);
        } else {
            result = exp2(mul(log2(x), y));
        }
    }

    /// @notice Raises x (signed 59.18-decimal fixed-point number) to the power of y (basic unsigned integer) using the
    /// famous algorithm "exponentiation by squaring".
    ///
    /// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring
    ///
    /// Requirements:
    /// - All from "abs" and "PRBMath.mulDivFixedPoint".
    /// - The result must fit within MAX_SD59x18.
    ///
    /// Caveats:
    /// - All from "PRBMath.mulDivFixedPoint".
    /// - Assumes 0^0 is 1.
    ///
    /// @param x The base as a signed 59.18-decimal fixed-point number.
    /// @param y The exponent as an uint256.
    /// @return result The result as a signed 59.18-decimal fixed-point number.
    function powu(int256 x, uint256 y) internal pure returns (int256 result) {
        uint256 xAbs = uint256(abs(x));

        // Calculate the first iteration of the loop in advance.
        uint256 rAbs = y & 1 > 0 ? xAbs : uint256(SCALE);

        // Equivalent to "for(y /= 2; y > 0; y /= 2)" but faster.
        uint256 yAux = y;
        for (yAux >>= 1; yAux > 0; yAux >>= 1) {
            xAbs = PRBMath.mulDivFixedPoint(xAbs, xAbs);

            // Equivalent to "y % 2 == 1" but faster.
            if (yAux & 1 > 0) {
                rAbs = PRBMath.mulDivFixedPoint(rAbs, xAbs);
            }
        }

        // The result must fit within the 59.18-decimal fixed-point representation.
        if (rAbs > uint256(MAX_SD59x18)) {
            revert PRBMathSD59x18__PowuOverflow(rAbs);
        }

        // Is the base negative and the exponent an odd number?
        bool isNegative = x < 0 && y & 1 == 1;
        result = isNegative ? -int256(rAbs) : int256(rAbs);
    }

    /// @notice Returns 1 as a signed 59.18-decimal fixed-point number.
    function scale() internal pure returns (int256 result) {
        result = SCALE;
    }

    /// @notice Calculates the square root of x, rounding down.
    /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
    ///
    /// Requirements:
    /// - x cannot be negative.
    /// - x must be less than MAX_SD59x18 / SCALE.
    ///
    /// @param x The signed 59.18-decimal fixed-point number for which to calculate the square root.
    /// @return result The result as a signed 59.18-decimal fixed-point .
    function sqrt(int256 x) internal pure returns (int256 result) {
        unchecked {
            if (x < 0) {
                revert PRBMathSD59x18__SqrtNegativeInput(x);
            }
            if (x > MAX_SD59x18 / SCALE) {
                revert PRBMathSD59x18__SqrtOverflow(x);
            }
            // Multiply x by the SCALE to account for the factor of SCALE that is picked up when multiplying two signed
            // 59.18-decimal fixed-point numbers together (in this case, those two numbers are both the square root).
            result = int256(PRBMath.sqrt(uint256(x * SCALE)));
        }
    }

    /// @notice Converts a signed 59.18-decimal fixed-point number to basic integer form, rounding down in the process.
    /// @param x The signed 59.18-decimal fixed-point number to convert.
    /// @return result The same number in basic integer form.
    function toInt(int256 x) internal pure returns (int256 result) {
        unchecked {
            result = x / SCALE;
        }
    }
}

File 29 of 32 : PRBMath.sol
// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;

/// @notice Emitted when the result overflows uint256.
error PRBMath__MulDivFixedPointOverflow(uint256 prod1);

/// @notice Emitted when the result overflows uint256.
error PRBMath__MulDivOverflow(uint256 prod1, uint256 denominator);

/// @notice Emitted when one of the inputs is type(int256).min.
error PRBMath__MulDivSignedInputTooSmall();

/// @notice Emitted when the intermediary absolute result overflows int256.
error PRBMath__MulDivSignedOverflow(uint256 rAbs);

/// @notice Emitted when the input is MIN_SD59x18.
error PRBMathSD59x18__AbsInputTooSmall();

/// @notice Emitted when ceiling a number overflows SD59x18.
error PRBMathSD59x18__CeilOverflow(int256 x);

/// @notice Emitted when one of the inputs is MIN_SD59x18.
error PRBMathSD59x18__DivInputTooSmall();

/// @notice Emitted when one of the intermediary unsigned results overflows SD59x18.
error PRBMathSD59x18__DivOverflow(uint256 rAbs);

/// @notice Emitted when the input is greater than 133.084258667509499441.
error PRBMathSD59x18__ExpInputTooBig(int256 x);

/// @notice Emitted when the input is greater than 192.
error PRBMathSD59x18__Exp2InputTooBig(int256 x);

/// @notice Emitted when flooring a number underflows SD59x18.
error PRBMathSD59x18__FloorUnderflow(int256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format overflows SD59x18.
error PRBMathSD59x18__FromIntOverflow(int256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format underflows SD59x18.
error PRBMathSD59x18__FromIntUnderflow(int256 x);

/// @notice Emitted when the product of the inputs is negative.
error PRBMathSD59x18__GmNegativeProduct(int256 x, int256 y);

/// @notice Emitted when multiplying the inputs overflows SD59x18.
error PRBMathSD59x18__GmOverflow(int256 x, int256 y);

/// @notice Emitted when the input is less than or equal to zero.
error PRBMathSD59x18__LogInputTooSmall(int256 x);

/// @notice Emitted when one of the inputs is MIN_SD59x18.
error PRBMathSD59x18__MulInputTooSmall();

/// @notice Emitted when the intermediary absolute result overflows SD59x18.
error PRBMathSD59x18__MulOverflow(uint256 rAbs);

/// @notice Emitted when the intermediary absolute result overflows SD59x18.
error PRBMathSD59x18__PowuOverflow(uint256 rAbs);

/// @notice Emitted when the input is negative.
error PRBMathSD59x18__SqrtNegativeInput(int256 x);

/// @notice Emitted when the calculating the square root overflows SD59x18.
error PRBMathSD59x18__SqrtOverflow(int256 x);

/// @notice Emitted when addition overflows UD60x18.
error PRBMathUD60x18__AddOverflow(uint256 x, uint256 y);

/// @notice Emitted when ceiling a number overflows UD60x18.
error PRBMathUD60x18__CeilOverflow(uint256 x);

/// @notice Emitted when the input is greater than 133.084258667509499441.
error PRBMathUD60x18__ExpInputTooBig(uint256 x);

/// @notice Emitted when the input is greater than 192.
error PRBMathUD60x18__Exp2InputTooBig(uint256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format format overflows UD60x18.
error PRBMathUD60x18__FromUintOverflow(uint256 x);

/// @notice Emitted when multiplying the inputs overflows UD60x18.
error PRBMathUD60x18__GmOverflow(uint256 x, uint256 y);

/// @notice Emitted when the input is less than 1.
error PRBMathUD60x18__LogInputTooSmall(uint256 x);

/// @notice Emitted when the calculating the square root overflows UD60x18.
error PRBMathUD60x18__SqrtOverflow(uint256 x);

/// @notice Emitted when subtraction underflows UD60x18.
error PRBMathUD60x18__SubUnderflow(uint256 x, uint256 y);

/// @dev Common mathematical functions used in both PRBMathSD59x18 and PRBMathUD60x18. Note that this shared library
/// does not always assume the signed 59.18-decimal fixed-point or the unsigned 60.18-decimal fixed-point
/// representation. When it does not, it is explicitly mentioned in the NatSpec documentation.
library PRBMath {
    /// STRUCTS ///

    struct SD59x18 {
        int256 value;
    }

    struct UD60x18 {
        uint256 value;
    }

    /// STORAGE ///

    /// @dev How many trailing decimals can be represented.
    uint256 internal constant SCALE = 1e18;

    /// @dev Largest power of two divisor of SCALE.
    uint256 internal constant SCALE_LPOTD = 262144;

    /// @dev SCALE inverted mod 2^256.
    uint256 internal constant SCALE_INVERSE =
        78156646155174841979727994598816262306175212592076161876661_508869554232690281;

    /// FUNCTIONS ///

    /// @notice Calculates the binary exponent of x using the binary fraction method.
    /// @dev Has to use 192.64-bit fixed-point numbers.
    /// See https://ethereum.stackexchange.com/a/96594/24693.
    /// @param x The exponent as an unsigned 192.64-bit fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function exp2(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            // Start from 0.5 in the 192.64-bit fixed-point format.
            result = 0x800000000000000000000000000000000000000000000000;

            // Multiply the result by root(2, 2^-i) when the bit at position i is 1. None of the intermediary results overflows
            // because the initial result is 2^191 and all magic factors are less than 2^65.
            if (x & 0x8000000000000000 > 0) {
                result = (result * 0x16A09E667F3BCC909) >> 64;
            }
            if (x & 0x4000000000000000 > 0) {
                result = (result * 0x1306FE0A31B7152DF) >> 64;
            }
            if (x & 0x2000000000000000 > 0) {
                result = (result * 0x1172B83C7D517ADCE) >> 64;
            }
            if (x & 0x1000000000000000 > 0) {
                result = (result * 0x10B5586CF9890F62A) >> 64;
            }
            if (x & 0x800000000000000 > 0) {
                result = (result * 0x1059B0D31585743AE) >> 64;
            }
            if (x & 0x400000000000000 > 0) {
                result = (result * 0x102C9A3E778060EE7) >> 64;
            }
            if (x & 0x200000000000000 > 0) {
                result = (result * 0x10163DA9FB33356D8) >> 64;
            }
            if (x & 0x100000000000000 > 0) {
                result = (result * 0x100B1AFA5ABCBED61) >> 64;
            }
            if (x & 0x80000000000000 > 0) {
                result = (result * 0x10058C86DA1C09EA2) >> 64;
            }
            if (x & 0x40000000000000 > 0) {
                result = (result * 0x1002C605E2E8CEC50) >> 64;
            }
            if (x & 0x20000000000000 > 0) {
                result = (result * 0x100162F3904051FA1) >> 64;
            }
            if (x & 0x10000000000000 > 0) {
                result = (result * 0x1000B175EFFDC76BA) >> 64;
            }
            if (x & 0x8000000000000 > 0) {
                result = (result * 0x100058BA01FB9F96D) >> 64;
            }
            if (x & 0x4000000000000 > 0) {
                result = (result * 0x10002C5CC37DA9492) >> 64;
            }
            if (x & 0x2000000000000 > 0) {
                result = (result * 0x1000162E525EE0547) >> 64;
            }
            if (x & 0x1000000000000 > 0) {
                result = (result * 0x10000B17255775C04) >> 64;
            }
            if (x & 0x800000000000 > 0) {
                result = (result * 0x1000058B91B5BC9AE) >> 64;
            }
            if (x & 0x400000000000 > 0) {
                result = (result * 0x100002C5C89D5EC6D) >> 64;
            }
            if (x & 0x200000000000 > 0) {
                result = (result * 0x10000162E43F4F831) >> 64;
            }
            if (x & 0x100000000000 > 0) {
                result = (result * 0x100000B1721BCFC9A) >> 64;
            }
            if (x & 0x80000000000 > 0) {
                result = (result * 0x10000058B90CF1E6E) >> 64;
            }
            if (x & 0x40000000000 > 0) {
                result = (result * 0x1000002C5C863B73F) >> 64;
            }
            if (x & 0x20000000000 > 0) {
                result = (result * 0x100000162E430E5A2) >> 64;
            }
            if (x & 0x10000000000 > 0) {
                result = (result * 0x1000000B172183551) >> 64;
            }
            if (x & 0x8000000000 > 0) {
                result = (result * 0x100000058B90C0B49) >> 64;
            }
            if (x & 0x4000000000 > 0) {
                result = (result * 0x10000002C5C8601CC) >> 64;
            }
            if (x & 0x2000000000 > 0) {
                result = (result * 0x1000000162E42FFF0) >> 64;
            }
            if (x & 0x1000000000 > 0) {
                result = (result * 0x10000000B17217FBB) >> 64;
            }
            if (x & 0x800000000 > 0) {
                result = (result * 0x1000000058B90BFCE) >> 64;
            }
            if (x & 0x400000000 > 0) {
                result = (result * 0x100000002C5C85FE3) >> 64;
            }
            if (x & 0x200000000 > 0) {
                result = (result * 0x10000000162E42FF1) >> 64;
            }
            if (x & 0x100000000 > 0) {
                result = (result * 0x100000000B17217F8) >> 64;
            }
            if (x & 0x80000000 > 0) {
                result = (result * 0x10000000058B90BFC) >> 64;
            }
            if (x & 0x40000000 > 0) {
                result = (result * 0x1000000002C5C85FE) >> 64;
            }
            if (x & 0x20000000 > 0) {
                result = (result * 0x100000000162E42FF) >> 64;
            }
            if (x & 0x10000000 > 0) {
                result = (result * 0x1000000000B17217F) >> 64;
            }
            if (x & 0x8000000 > 0) {
                result = (result * 0x100000000058B90C0) >> 64;
            }
            if (x & 0x4000000 > 0) {
                result = (result * 0x10000000002C5C860) >> 64;
            }
            if (x & 0x2000000 > 0) {
                result = (result * 0x1000000000162E430) >> 64;
            }
            if (x & 0x1000000 > 0) {
                result = (result * 0x10000000000B17218) >> 64;
            }
            if (x & 0x800000 > 0) {
                result = (result * 0x1000000000058B90C) >> 64;
            }
            if (x & 0x400000 > 0) {
                result = (result * 0x100000000002C5C86) >> 64;
            }
            if (x & 0x200000 > 0) {
                result = (result * 0x10000000000162E43) >> 64;
            }
            if (x & 0x100000 > 0) {
                result = (result * 0x100000000000B1721) >> 64;
            }
            if (x & 0x80000 > 0) {
                result = (result * 0x10000000000058B91) >> 64;
            }
            if (x & 0x40000 > 0) {
                result = (result * 0x1000000000002C5C8) >> 64;
            }
            if (x & 0x20000 > 0) {
                result = (result * 0x100000000000162E4) >> 64;
            }
            if (x & 0x10000 > 0) {
                result = (result * 0x1000000000000B172) >> 64;
            }
            if (x & 0x8000 > 0) {
                result = (result * 0x100000000000058B9) >> 64;
            }
            if (x & 0x4000 > 0) {
                result = (result * 0x10000000000002C5D) >> 64;
            }
            if (x & 0x2000 > 0) {
                result = (result * 0x1000000000000162E) >> 64;
            }
            if (x & 0x1000 > 0) {
                result = (result * 0x10000000000000B17) >> 64;
            }
            if (x & 0x800 > 0) {
                result = (result * 0x1000000000000058C) >> 64;
            }
            if (x & 0x400 > 0) {
                result = (result * 0x100000000000002C6) >> 64;
            }
            if (x & 0x200 > 0) {
                result = (result * 0x10000000000000163) >> 64;
            }
            if (x & 0x100 > 0) {
                result = (result * 0x100000000000000B1) >> 64;
            }
            if (x & 0x80 > 0) {
                result = (result * 0x10000000000000059) >> 64;
            }
            if (x & 0x40 > 0) {
                result = (result * 0x1000000000000002C) >> 64;
            }
            if (x & 0x20 > 0) {
                result = (result * 0x10000000000000016) >> 64;
            }
            if (x & 0x10 > 0) {
                result = (result * 0x1000000000000000B) >> 64;
            }
            if (x & 0x8 > 0) {
                result = (result * 0x10000000000000006) >> 64;
            }
            if (x & 0x4 > 0) {
                result = (result * 0x10000000000000003) >> 64;
            }
            if (x & 0x2 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
            if (x & 0x1 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }

            // We're doing two things at the same time:
            //
            //   1. Multiply the result by 2^n + 1, where "2^n" is the integer part and the one is added to account for
            //      the fact that we initially set the result to 0.5. This is accomplished by subtracting from 191
            //      rather than 192.
            //   2. Convert the result to the unsigned 60.18-decimal fixed-point format.
            //
            // This works because 2^(191-ip) = 2^ip / 2^191, where "ip" is the integer part "2^n".
            result *= SCALE;
            result >>= (191 - (x >> 64));
        }
    }

    /// @notice Finds the zero-based index of the first one in the binary representation of x.
    /// @dev See the note on msb in the "Find First Set" Wikipedia article https://en.wikipedia.org/wiki/Find_first_set
    /// @param x The uint256 number for which to find the index of the most significant bit.
    /// @return msb The index of the most significant bit as an uint256.
    function mostSignificantBit(uint256 x) internal pure returns (uint256 msb) {
        if (x >= 2**128) {
            x >>= 128;
            msb += 128;
        }
        if (x >= 2**64) {
            x >>= 64;
            msb += 64;
        }
        if (x >= 2**32) {
            x >>= 32;
            msb += 32;
        }
        if (x >= 2**16) {
            x >>= 16;
            msb += 16;
        }
        if (x >= 2**8) {
            x >>= 8;
            msb += 8;
        }
        if (x >= 2**4) {
            x >>= 4;
            msb += 4;
        }
        if (x >= 2**2) {
            x >>= 2;
            msb += 2;
        }
        if (x >= 2**1) {
            // No need to shift x any more.
            msb += 1;
        }
    }

    /// @notice Calculates floor(x*y÷denominator) with full precision.
    ///
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
    ///
    /// Requirements:
    /// - The denominator cannot be zero.
    /// - The result must fit within uint256.
    ///
    /// Caveats:
    /// - This function does not work with fixed-point numbers.
    ///
    /// @param x The multiplicand as an uint256.
    /// @param y The multiplier as an uint256.
    /// @param denominator The divisor as an uint256.
    /// @return result The result as an uint256.
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
        // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
        // variables such that product = prod1 * 2^256 + prod0.
        uint256 prod0; // Least significant 256 bits of the product
        uint256 prod1; // Most significant 256 bits of the product
        assembly {
            let mm := mulmod(x, y, not(0))
            prod0 := mul(x, y)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        // Handle non-overflow cases, 256 by 256 division.
        if (prod1 == 0) {
            unchecked {
                result = prod0 / denominator;
            }
            return result;
        }

        // Make sure the result is less than 2^256. Also prevents denominator == 0.
        if (prod1 >= denominator) {
            revert PRBMath__MulDivOverflow(prod1, denominator);
        }

        ///////////////////////////////////////////////
        // 512 by 256 division.
        ///////////////////////////////////////////////

        // Make division exact by subtracting the remainder from [prod1 prod0].
        uint256 remainder;
        assembly {
            // Compute remainder using mulmod.
            remainder := mulmod(x, y, denominator)

            // Subtract 256 bit number from 512 bit number.
            prod1 := sub(prod1, gt(remainder, prod0))
            prod0 := sub(prod0, remainder)
        }

        // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
        // See https://cs.stackexchange.com/q/138556/92363.
        unchecked {
            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 lpotdod = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by lpotdod.
                denominator := div(denominator, lpotdod)

                // Divide [prod1 prod0] by lpotdod.
                prod0 := div(prod0, lpotdod)

                // Flip lpotdod such that it is 2^256 / lpotdod. If lpotdod is zero, then it becomes one.
                lpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * lpotdod;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /// @notice Calculates floor(x*y÷1e18) with full precision.
    ///
    /// @dev Variant of "mulDiv" with constant folding, i.e. in which the denominator is always 1e18. Before returning the
    /// final result, we add 1 if (x * y) % SCALE >= HALF_SCALE. Without this, 6.6e-19 would be truncated to 0 instead of
    /// being rounded to 1e-18.  See "Listing 6" and text above it at https://accu.org/index.php/journals/1717.
    ///
    /// Requirements:
    /// - The result must fit within uint256.
    ///
    /// Caveats:
    /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMath.mulDiv" to understand how this works.
    /// - It is assumed that the result can never be type(uint256).max when x and y solve the following two equations:
    ///     1. x * y = type(uint256).max * SCALE
    ///     2. (x * y) % SCALE >= SCALE / 2
    ///
    /// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function mulDivFixedPoint(uint256 x, uint256 y) internal pure returns (uint256 result) {
        uint256 prod0;
        uint256 prod1;
        assembly {
            let mm := mulmod(x, y, not(0))
            prod0 := mul(x, y)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        if (prod1 >= SCALE) {
            revert PRBMath__MulDivFixedPointOverflow(prod1);
        }

        uint256 remainder;
        uint256 roundUpUnit;
        assembly {
            remainder := mulmod(x, y, SCALE)
            roundUpUnit := gt(remainder, 499999999999999999)
        }

        if (prod1 == 0) {
            unchecked {
                result = (prod0 / SCALE) + roundUpUnit;
                return result;
            }
        }

        assembly {
            result := add(
                mul(
                    or(
                        div(sub(prod0, remainder), SCALE_LPOTD),
                        mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, SCALE_LPOTD), SCALE_LPOTD), 1))
                    ),
                    SCALE_INVERSE
                ),
                roundUpUnit
            )
        }
    }

    /// @notice Calculates floor(x*y÷denominator) with full precision.
    ///
    /// @dev An extension of "mulDiv" for signed numbers. Works by computing the signs and the absolute values separately.
    ///
    /// Requirements:
    /// - None of the inputs can be type(int256).min.
    /// - The result must fit within int256.
    ///
    /// @param x The multiplicand as an int256.
    /// @param y The multiplier as an int256.
    /// @param denominator The divisor as an int256.
    /// @return result The result as an int256.
    function mulDivSigned(
        int256 x,
        int256 y,
        int256 denominator
    ) internal pure returns (int256 result) {
        if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
            revert PRBMath__MulDivSignedInputTooSmall();
        }

        // Get hold of the absolute values of x, y and the denominator.
        uint256 ax;
        uint256 ay;
        uint256 ad;
        unchecked {
            ax = x < 0 ? uint256(-x) : uint256(x);
            ay = y < 0 ? uint256(-y) : uint256(y);
            ad = denominator < 0 ? uint256(-denominator) : uint256(denominator);
        }

        // Compute the absolute value of (x*y)÷denominator. The result must fit within int256.
        uint256 rAbs = mulDiv(ax, ay, ad);
        if (rAbs > uint256(type(int256).max)) {
            revert PRBMath__MulDivSignedOverflow(rAbs);
        }

        // Get the signs of x, y and the denominator.
        uint256 sx;
        uint256 sy;
        uint256 sd;
        assembly {
            sx := sgt(x, sub(0, 1))
            sy := sgt(y, sub(0, 1))
            sd := sgt(denominator, sub(0, 1))
        }

        // XOR over sx, sy and sd. This is checking whether there are one or three negative signs in the inputs.
        // If yes, the result should be negative.
        result = sx ^ sy ^ sd == 0 ? -int256(rAbs) : int256(rAbs);
    }

    /// @notice Calculates the square root of x, rounding down.
    /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
    ///
    /// Caveats:
    /// - This function does not work with fixed-point numbers.
    ///
    /// @param x The uint256 number for which to calculate the square root.
    /// @return result The result as an uint256.
    function sqrt(uint256 x) internal pure returns (uint256 result) {
        if (x == 0) {
            return 0;
        }

        // Set the initial guess to the least power of two that is greater than or equal to sqrt(x).
        uint256 xAux = uint256(x);
        result = 1;
        if (xAux >= 0x100000000000000000000000000000000) {
            xAux >>= 128;
            result <<= 64;
        }
        if (xAux >= 0x10000000000000000) {
            xAux >>= 64;
            result <<= 32;
        }
        if (xAux >= 0x100000000) {
            xAux >>= 32;
            result <<= 16;
        }
        if (xAux >= 0x10000) {
            xAux >>= 16;
            result <<= 8;
        }
        if (xAux >= 0x100) {
            xAux >>= 8;
            result <<= 4;
        }
        if (xAux >= 0x10) {
            xAux >>= 4;
            result <<= 2;
        }
        if (xAux >= 0x8) {
            result <<= 1;
        }

        // The operations can never overflow because the result is max 2^127 when it enters this block.
        unchecked {
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1; // Seven iterations should be enough
            uint256 roundedDownResult = x / result;
            return result >= roundedDownResult ? roundedDownResult : result;
        }
    }
}

File 30 of 32 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (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 Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

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

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions 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);
    }

    /**
     * @dev 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 31 of 32 : console.sol
// SPDX-License-Identifier: MIT
pragma solidity >= 0.4.22 <0.9.0;

library console {
	address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);

	function _sendLogPayload(bytes memory payload) private view {
		uint256 payloadLength = payload.length;
		address consoleAddress = CONSOLE_ADDRESS;
		assembly {
			let payloadStart := add(payload, 32)
			let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
		}
	}

	function log() internal view {
		_sendLogPayload(abi.encodeWithSignature("log()"));
	}

	function logInt(int p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(int)", p0));
	}

	function logUint(uint p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
	}

	function logString(string memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
	}

	function logBool(bool p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
	}

	function logAddress(address p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
	}

	function logBytes(bytes memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
	}

	function logBytes1(bytes1 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
	}

	function logBytes2(bytes2 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
	}

	function logBytes3(bytes3 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
	}

	function logBytes4(bytes4 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
	}

	function logBytes5(bytes5 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
	}

	function logBytes6(bytes6 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
	}

	function logBytes7(bytes7 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
	}

	function logBytes8(bytes8 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
	}

	function logBytes9(bytes9 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
	}

	function logBytes10(bytes10 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
	}

	function logBytes11(bytes11 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
	}

	function logBytes12(bytes12 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
	}

	function logBytes13(bytes13 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
	}

	function logBytes14(bytes14 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
	}

	function logBytes15(bytes15 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
	}

	function logBytes16(bytes16 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
	}

	function logBytes17(bytes17 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
	}

	function logBytes18(bytes18 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
	}

	function logBytes19(bytes19 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
	}

	function logBytes20(bytes20 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
	}

	function logBytes21(bytes21 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
	}

	function logBytes22(bytes22 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
	}

	function logBytes23(bytes23 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
	}

	function logBytes24(bytes24 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
	}

	function logBytes25(bytes25 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
	}

	function logBytes26(bytes26 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
	}

	function logBytes27(bytes27 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
	}

	function logBytes28(bytes28 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
	}

	function logBytes29(bytes29 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
	}

	function logBytes30(bytes30 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
	}

	function logBytes31(bytes31 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
	}

	function logBytes32(bytes32 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
	}

	function log(uint p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
	}

	function log(string memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
	}

	function log(bool p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
	}

	function log(address p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
	}

	function log(uint p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1));
	}

	function log(uint p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1));
	}

	function log(uint p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1));
	}

	function log(uint p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1));
	}

	function log(string memory p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1));
	}

	function log(string memory p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
	}

	function log(string memory p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
	}

	function log(string memory p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
	}

	function log(bool p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1));
	}

	function log(bool p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
	}

	function log(bool p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
	}

	function log(bool p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
	}

	function log(address p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1));
	}

	function log(address p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
	}

	function log(address p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
	}

	function log(address p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
	}

	function log(uint p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2));
	}

	function log(uint p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2));
	}

	function log(uint p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2));
	}

	function log(uint p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2));
	}

	function log(uint p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2));
	}

	function log(uint p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2));
	}

	function log(uint p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2));
	}

	function log(uint p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2));
	}

	function log(uint p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2));
	}

	function log(uint p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2));
	}

	function log(uint p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2));
	}

	function log(uint p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
	}

	function log(string memory p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2));
	}

	function log(string memory p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
	}

	function log(string memory p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
	}

	function log(string memory p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
	}

	function log(bool p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2));
	}

	function log(bool p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2));
	}

	function log(bool p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2));
	}

	function log(bool p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
	}

	function log(bool p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2));
	}

	function log(bool p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
	}

	function log(bool p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
	}

	function log(bool p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
	}

	function log(bool p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2));
	}

	function log(bool p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
	}

	function log(bool p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
	}

	function log(bool p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
	}

	function log(address p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2));
	}

	function log(address p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2));
	}

	function log(address p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2));
	}

	function log(address p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2));
	}

	function log(address p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2));
	}

	function log(address p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
	}

	function log(address p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
	}

	function log(address p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
	}

	function log(address p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2));
	}

	function log(address p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
	}

	function log(address p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
	}

	function log(address p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
	}

	function log(address p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2));
	}

	function log(address p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
	}

	function log(address p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
	}

	function log(address p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
	}

	function log(uint p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
	}

}

File 32 of 32 : 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;
    }

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

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

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"productId","type":"uint32"},{"indexed":true,"internalType":"bytes32","name":"digest","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"subaccount","type":"bytes32"},{"indexed":false,"internalType":"int128","name":"priceX18","type":"int128"},{"indexed":false,"internalType":"int128","name":"amount","type":"int128"},{"indexed":false,"internalType":"uint64","name":"expiration","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"nonce","type":"uint64"},{"indexed":false,"internalType":"bool","name":"isTaker","type":"bool"},{"indexed":false,"internalType":"int128","name":"feeAmount","type":"int128"},{"indexed":false,"internalType":"int128","name":"baseDelta","type":"int128"},{"indexed":false,"internalType":"int128","name":"quoteDelta","type":"int128"}],"name":"FillOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","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"},{"inputs":[],"name":"dumpFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"filledAmounts","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"users","type":"address[]"},{"internalType":"uint32[]","name":"productIds","type":"uint32[]"}],"name":"getAllFeeRates","outputs":[{"components":[{"internalType":"int64","name":"makerRateX18","type":"int64"},{"internalType":"int64","name":"takerRateX18","type":"int64"},{"internalType":"uint8","name":"isNonDefault","type":"uint8"}],"internalType":"struct IOffchainExchange.FeeRates[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllVirtualBooks","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"startAt","type":"uint32"},{"internalType":"uint32","name":"limit","type":"uint32"}],"name":"getCustomFeeAddresses","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"productId","type":"uint32"},{"components":[{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"int128","name":"priceX18","type":"int128"},{"internalType":"int128","name":"amount","type":"int128"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"}],"internalType":"struct IEndpoint.Order","name":"order","type":"tuple"}],"name":"getDigest","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEndpoint","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"subaccount","type":"bytes32"},{"internalType":"uint32","name":"productId","type":"uint32"},{"internalType":"bool","name":"taker","type":"bool"}],"name":"getFeeFractionX18","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"subaccount","type":"bytes32"},{"internalType":"uint32","name":"productId","type":"uint32"}],"name":"getFeeRatesX18","outputs":[{"internalType":"int128","name":"","type":"int128"},{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"productId","type":"uint32"}],"name":"getLpParams","outputs":[{"components":[{"internalType":"int128","name":"lpSpreadX18","type":"int128"}],"internalType":"struct IOffchainExchange.LpParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"productId","type":"uint32"}],"name":"getMarketInfo","outputs":[{"components":[{"internalType":"uint32","name":"quoteId","type":"uint32"},{"internalType":"int128","name":"minSize","type":"int128"},{"internalType":"int128","name":"sizeIncrement","type":"int128"},{"internalType":"int128","name":"collectedFees","type":"int128"}],"internalType":"struct IOffchainExchange.MarketInfo","name":"m","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"productId","type":"uint32"}],"name":"getMinSize","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"productId","type":"uint32"}],"name":"getSizeIncrement","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"productId","type":"uint32"}],"name":"getVirtualBook","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_clearinghouse","type":"address"},{"internalType":"address","name":"_endpoint","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"productId","type":"uint32"},{"internalType":"int128","name":"baseDelta","type":"int128"},{"internalType":"int128","name":"quoteDelta","type":"int128"},{"components":[{"components":[{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"int128","name":"priceX18","type":"int128"},{"internalType":"int128","name":"amount","type":"int128"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"}],"internalType":"struct IEndpoint.Order","name":"order","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct IEndpoint.SignedOrder","name":"taker","type":"tuple"}],"internalType":"struct IEndpoint.MatchOrderAMM","name":"txn","type":"tuple"},{"internalType":"address","name":"takerLinkedSigner","type":"address"}],"name":"matchOrderAMM","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"uint32","name":"productId","type":"uint32"},{"components":[{"components":[{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"int128","name":"priceX18","type":"int128"},{"internalType":"int128","name":"amount","type":"int128"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"}],"internalType":"struct IEndpoint.Order","name":"order","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct IEndpoint.SignedOrder","name":"taker","type":"tuple"},{"components":[{"components":[{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"int128","name":"priceX18","type":"int128"},{"internalType":"int128","name":"amount","type":"int128"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"uint64","name":"nonce","type":"uint64"}],"internalType":"struct IEndpoint.Order","name":"order","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct IEndpoint.SignedOrder","name":"maker","type":"tuple"}],"internalType":"struct IEndpoint.MatchOrders","name":"matchOrders","type":"tuple"},{"internalType":"address","name":"takerLinkedSigner","type":"address"},{"internalType":"address","name":"makerLinkedSigner","type":"address"}],"internalType":"struct IEndpoint.MatchOrdersWithSigner","name":"txn","type":"tuple"}],"name":"matchOrders","outputs":[],"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":[{"components":[{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"uint32","name":"productId","type":"uint32"},{"internalType":"int128","name":"amount","type":"int128"},{"internalType":"int128","name":"priceX18","type":"int128"}],"internalType":"struct IEndpoint.SwapAMM","name":"txn","type":"tuple"}],"name":"swapAMM","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"productId","type":"uint32"},{"internalType":"int64","name":"makerRateX18","type":"int64"},{"internalType":"int64","name":"takerRateX18","type":"int64"}],"name":"updateFeeRates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"productId","type":"uint32"},{"internalType":"uint32","name":"quoteId","type":"uint32"},{"internalType":"address","name":"virtualBook","type":"address"},{"internalType":"int128","name":"sizeIncrement","type":"int128"},{"internalType":"int128","name":"minSize","type":"int128"},{"internalType":"int128","name":"lpSpreadX18","type":"int128"}],"name":"updateMarket","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b506147fa806100206000396000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806378f0d3ce116100e3578063b60aaa7c1161008c578063d895202a11610066578063d895202a14610404578063f2b2633114610424578063f2fde38b1461043757600080fd5b8063b60aaa7c146103d6578063b76d78e3146103e9578063ce933e59146103fc57600080fd5b806395ee6071116100bd57806395ee607114610391578063aed8e967146103b2578063b5cbd70e146103c357600080fd5b806378f0d3ce1461035a57806388bc79681461036d5780638da5cb5b1461038057600080fd5b806340f1a34d1161014557806366f87bd11161011f57806366f87bd114610303578063707c8b581461034a578063715018a61461035257600080fd5b806340f1a34d146102665780634821c8b51461029c578063485cc955146102f057600080fd5b80631d029b4d116101765780631d029b4d146101d95780632da1c59b146102335780633fceea281461024657600080fd5b80630f2c878e146101925780630f4b509d146101c4575b600080fd5b6101a56101a0366004613a80565b61044a565b60408051600f93840b81529190920b6020820152015b60405180910390f35b6101d76101d2366004613ac8565b6104e4565b005b6101ec6101e7366004613ae4565b610885565b6040516101bb9190600060808201905063ffffffff83511682526020830151600f0b60208301526040830151600f0b60408301526060830151600f0b606083015292915050565b6101d7610241366004613b25565b610946565b610259610254366004613ba7565b610b08565b6040516101bb9190613bd5565b610289610274366004613c22565b609e60205260009081526040902054600f0b81565b604051600f9190910b81526020016101bb565b6102de6102aa366004613ae4565b604080516020808201835260009182905263ffffffff939093168152609d8352819020815192830190915254600f0b815290565b6040519051600f0b81526020016101bb565b6101d76102fe366004613c3b565b610c38565b610332610311366004613ae4565b63ffffffff166000908152609c60205260409020546001600160a01b031690565b6040516001600160a01b0390911681526020016101bb565b6101d7610efd565b6101d7611387565b6101d7610368366004613c69565b61139b565b6101d761037b366004613cb0565b6116d7565b6033546001600160a01b0316610332565b6103a461039f366004613e06565b611cda565b6040519081526020016101bb565b6065546001600160a01b0316610332565b6102896103d1366004613e3c565b611e5a565b6102896103e4366004613ae4565b611efa565b6101d76103f7366004613e95565b611f27565b6102596123dd565b610417610412366004613f85565b6126a8565b6040516101bb9190614047565b610289610432366004613ae4565b612840565b6101d76104453660046140aa565b61286e565b606082811c600090815260a16020908152604080832063ffffffff8616845282528083208151948501825254600781810b8652600160401b8204900b92850192909252600160801b90910460ff169083018190529091829182036104cd5750604080516060810182526000815265b5e620f4800060208201526001918101919091525b60208101519051600791820b96910b945092505050565b6065546001600160a01b031633146105565760405162461bcd60e51b815260206004820152602a60248201527f53657175656e63657247617465643a2063616c6c6572206973206e6f742074686044820152691948195b991c1bda5b9d60b21b60648201526084015b60405180910390fd5b600061056b6101e76040840160208501613ae4565b905060006105876105826040850160208601613ae4565b6128fe565b90508060400151156105f35781604001518360400160208101906105ab91906140c7565b6105b591906140fa565b60408051808201909152600381526204953560ec1b602082015290600f0b156105f15760405162461bcd60e51b815260040161054d919061411c565b505b6000816040015161060857816020015161060b565b81515b90506000806001600160a01b03831663c7167cf561062f6040890160208a01613ae4565b61063f60608a0160408b016140c7565b61066b61065260808c0160608d016140c7565b61066260608d0160408e016140c7565b600f0b90612a20565b61067490614187565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815263ffffffff939093166004840152600f91820b6024840152900b604482015260640160408051808303816000875af11580156106e0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061070491906141ad565b915091508161071290614187565b915061071d81614187565b905060006107676107346040890160208a01613ae4565b88358886866000600f83900b13610758578b6020015161075390614187565b61075e565b8b602001515b60006001612a9b565b875190935090915061077e90869089358686612ac0565b609a546040516388b6496f60e01b81526000916001600160a01b0316906388b6496f906107b2908b359085906004016141f2565b602060405180830381865afa1580156107cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107f39190614216565b600f0b121560405180604001604052806002815260200161125560f21b815250906108315760405162461bcd60e51b815260040161054d919061411c565b506060860151609b600061084b60408b0160208c01613ae4565b63ffffffff168152602081019190915260400160002080546001600160801b03928316600160801b02921691909117905550505050505050565b6040805160808101825260008082526020808301829052828401829052606080840183815263ffffffff878116808652609b85528786208851948501895254600781810b8652600160401b8204810b86880152600160801b909104600f90810b868b0190815292885260a49096529790952054168552925190910b909152805191929091610918910b633b9aca00614233565b600f0b6020808401919091528101516109389060070b633b9aca00614233565b600f0b604083015250919050565b61094e612cad565b6001600160a01b038416156109f85763ffffffff86166000908152609c60205260409020546001600160a01b0316156109c95760405162461bcd60e51b815260206004820152601860248201527f7669727475616c20626f6f6b20616c7265616479207365740000000000000000604482015260640161054d565b63ffffffff86166000908152609c6020526040902080546001600160a01b0319166001600160a01b0386161790555b63ffffffff85811614610a2e5763ffffffff868116600090815260a460205260409020805463ffffffff19169187169190911790555b610a3c633b9aca00836142d1565b63ffffffff87166000908152609b60205260409020805467ffffffffffffffff191667ffffffffffffffff92909216919091179055610a7f633b9aca00846142d1565b63ffffffff969096166000818152609b6020908152604080832080546fffffffffffffffff00000000000000001916600160401b67ffffffffffffffff909c169b909b029a909a1790995588518082018a52600f9490940b8452918152609d90915295909520945185546001600160801b0319166001600160801b039091161790945550505050565b60606000610b168385614318565b60a35490915063ffffffff8082169083161115610b31578091505b8063ffffffff168563ffffffff161115610b49578094505b6000610b558684614340565b63ffffffff1667ffffffffffffffff811115610b7357610b73613ceb565b604051908082528060200260200182016040528015610b9c578160200160208202803683370190505b509050855b8363ffffffff168163ffffffff161015610c2e5760a38163ffffffff1681548110610bce57610bce614365565b6000918252602090912001546001600160a01b031682610bee8984614340565b63ffffffff1681518110610c0457610c04614365565b6001600160a01b039092166020928302919091019091015280610c268161437b565b915050610ba1565b5095945050505050565b600054610100900460ff1615808015610c585750600054600160ff909116105b80610c725750303b158015610c72575060005460ff166001145b610ce45760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840161054d565b6000805460ff191660011790558015610d07576000805461ff0019166101001790555b610d0f612d1c565b610d1882612d8f565b610d8c6040518060400160405280600681526020017f56657274657800000000000000000000000000000000000000000000000000008152506040518060400160405280600581526020017f302e302e31000000000000000000000000000000000000000000000000000000815250612db9565b609a80546001600160a01b0319166001600160a01b038516908117909155604051635d2e9ad160e01b8152635d2e9ad190610dcc9060009060040161439e565b602060405180830381865afa158015610de9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0d91906143b8565b609f80546001600160a01b0319166001600160a01b03928316179055609a54604051635d2e9ad160e01b8152911690635d2e9ad190610e519060019060040161439e565b602060405180830381865afa158015610e6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9291906143b8565b60a080546001600160a01b0319166001600160a01b03929092169190911790558015610ef8576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050565b6065546001600160a01b03163314610f6a5760405162461bcd60e51b815260206004820152602a60248201527f53657175656e63657247617465643a2063616c6c6572206973206e6f742074686044820152691948195b991c1bda5b9d60b21b606482015260840161054d565b609f54604080516347428e7b60e01b815290516000926001600160a01b0316916347428e7b91600480830192869291908290030181865afa158015610fb3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610fdb91908101906143d5565b905060015b81518163ffffffff161015611175576000828263ffffffff168151811061100957611009614365565b60209081029190910181015163ffffffff81166000908152609b8352604080822081516060810183529054600781810b8352600160401b8204900b95820195909552600160801b909404600f0b9084018190529193501261106b575050611163565b609f5463ffffffff838116600090815260a460205260408082205485820151915163e0b0621f60e01b8152931660048401526024830191909152600f0b60448201526001600160a01b039091169063e0b0621f90606401600060405180830381600087803b1580156110dc57600080fd5b505af11580156110f0573d6000803e3d6000fd5b50506000604080850182815263ffffffff9096168252609b6020908152912084518154929095015195516001600160801b03908116600160801b0267ffffffffffffffff978816600160401b026001600160801b0319909416979096169690961791909117949094169290921790925550505b8061116d8161437b565b915050610fe0565b5060a060009054906101000a90046001600160a01b03166001600160a01b03166347428e7b6040518163ffffffff1660e01b8152600401600060405180830381865afa1580156111c9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526111f191908101906143d5565b905060005b81518163ffffffff161015611383576000828263ffffffff168151811061121f5761121f614365565b60209081029190910181015163ffffffff81166000908152609b8352604080822081516060810183529054600781810b8352600160401b8204900b95820195909552600160801b909404600f0b90840181905291935003611281575050611371565b60a054604082810151905163f8a42e5160e01b815263ffffffff851660048201526000602482018190526044820152600f9190910b60648201526001600160a01b039091169063f8a42e5190608401600060405180830381600087803b1580156112ea57600080fd5b505af11580156112fe573d6000803e3d6000fd5b50506000604080850182815263ffffffff9096168252609b6020908152912084518154929095015195516001600160801b03908116600160801b0267ffffffffffffffff978816600160401b026001600160801b0319909416979096169690961791909117949094169290921790925550505b8061137b8161437b565b9150506111f6565b5050565b61138f612e2e565b6113996000612e88565b565b6065546001600160a01b031633146114085760405162461bcd60e51b815260206004820152602a60248201527f53657175656e63657247617465643a2063616c6c6572206973206e6f742074686044820152691948195b991c1bda5b9d60b21b606482015260840161054d565b600061141a6105826020850185613ae4565b9050600061142e6101e76020860186613ae4565b9050600061145f6114426020870187613ae4565b61144f606088018861446f565b61039f903681900381019061448f565b90506000611470606087018761446f565b6114819060608101906040016140c7565b90506000611492606088018861446f565b61149b906144ab565b90506114aa858583868a612eda565b60405180604001604052806002815260200161125560f21b815250906114e35760405162461bcd60e51b815260040161054d919061411c565b50600080611511876114fb60408c0160208d016140c7565b61150b60608d0160408e016140c7565b86613116565b9092509050600061156c61152860208c018c613ae4565b855180516040909101518a90879087908290611544908d614558565b61154e9190614558565b61155c600f8a900b8b61339f565b61156590614187565b6001612a9b565b8851865151919450919250611584918a918686612ac0565b604080518082019091526002815261125560f21b602082015250835151866115af60208d018d613ae4565b865160208082015160608084015160809485015160408051600f95860b81528f860b9681019690965267ffffffffffffffff92831690860152169083015260019282019290925285820b60a082015287820b60c08201529086900b60e082015263ffffffff91909116907f7c57459d6f4f0fb2fc5b1e298c8c0eb238422944964aa1e249eaa78747f0cca9906101000160405180910390a46060870151609b600061165d60208e018e613ae4565b63ffffffff1681526020810191909152604090810160002080546001600160801b03938416600160801b029316929092179091558451015161169f9086614558565b6000968752609e602052604090962080546001600160801b0319166001600160801b0390971696909617909555505050505050505050565b6065546001600160a01b031633146117445760405162461bcd60e51b815260206004820152602a60248201527f53657175656e63657247617465643a2063616c6c6572206973206e6f742074686044820152691948195b991c1bda5b9d60b21b606482015260840161054d565b600061176161175383806145a8565b610582906020810190613ae4565b604080516060810182526000808252602082018190529181018290529192509081908190819060006117968760600151610885565b905060006117a489806145a8565b6117b290602081019061446f565b6117bb906144ab565b905060006117c98a806145a8565b6117d790604081019061446f565b6117e0906144ab565b905060405180606001604052806117ff8b606001518560000151611cda565b81526020016118168b606001518460000151611cda565b8152602001826000015160400151600f0b8152509350816000015160400151975061185a89848487600001518e602001602081019061185591906140aa565b612eda565b60405180604001604052806002815260200161125560f21b815250906118935760405162461bcd60e51b815260040161054d919061411c565b506118b289848387602001518e604001602081019061185591906140aa565b60405180604001604052806002815260200161494d60f01b815250906118eb5760405162461bcd60e51b815260040161054d919061411c565b5081516040908101518251820151825180840190935260048352634f43424d60e01b60208401526000600f91820b81129290910b130361193e5760405162461bcd60e51b815260040161054d919061411c565b506000816000015160400151600f0b13156119ae57816000015160200151600f0b816000015160200151600f0b1215604051806040016040528060048152602001634f43424d60e01b815250906119a85760405162461bcd60e51b815260040161054d919061411c565b50611a05565b816000015160200151600f0b816000015160200151600f0b1315604051806040016040528060048152602001634f43424d60e01b81525090611a035760405162461bcd60e51b815260040161054d919061411c565b505b611a1a89848460000151846000015188613408565b8096508197505050611a6289606001518360000151600001518589898b8860000151604001518f611a4b9190614558565b611a559190614558565b8751602001516001612a9b565b8451845151929950909650611a7b918b91908989612ac0565b604080518082019091526002815261125560f21b602082015250604080518082019091526002815261494d60f01b602082015250606080840151908a015163ffffffff166000908152609b6020526040902080546001600160801b03928316600160801b0292169190911790556001611af48b806145a8565b611b0290602081019061446f565b3514611b4757815160400151611b189089614558565b84516000908152609e6020526040902080546001600160801b0319166001600160801b03929092169190911790555b6001611b538b806145a8565b611b6190604081019061446f565b3514611bb2578060000151604001518460400151611b7f9190614558565b6020858101516000908152609e9091526040902080546001600160801b0319166001600160801b03929092169190911790555b50611bc19150889050806145a8565b611bcf90602081019061446f565b8151606088015191359163ffffffff167f7c57459d6f4f0fb2fc5b1e298c8c0eb238422944964aa1e249eaa78747f0cca9611c0a8b806145a8565b611c1890602081019061446f565b611c299060408101906020016140c7565b89611c348d806145a8565b611c4290602081019061446f565b611c539060808101906060016145be565b611c5d8e806145a8565b611c6b90602081019061446f565b611c7c9060a08101906080016145be565b60408051600f95860b815293850b602085015267ffffffffffffffff928316908401521660608201526001608082015289820b60a082015288820b60c08201529087900b60e08201526101000160405180910390a450505050505050565b6000806040518060800160405280605281526020016147736052913990506000818051906020012084600001518560200151866040015187606001518860800151604051602001611d64969594939291909586526020860194909452600f92830b6040860152910b606084015267ffffffffffffffff90811660808401521660a082015260c00190565b60405160208183030381529060405280519060200120905060007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f611da860665490565b60675463ffffffff89166000908152609c6020908152604091829020548251918201959095529081019290925260608201524660808201526001600160a01b0390911660a082015260c001604051602081830303815290604052805190602001209050611e50818360405161190160f01b6020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b9695505050505050565b606083811c600090815260a16020908152604080832063ffffffff8716845282528083208151948501825254600781810b8652600160401b8204900b92850192909252600160801b90910460ff169083018190529091908203611edc5750604080516060810182526000815265b5e620f4800060208201526001918101919091525b82611ee8578051611eee565b80602001515b60070b95945050505050565b63ffffffff81166000908152609b6020526040812054611f219060070b633b9aca00614233565b92915050565b6065546001600160a01b03163314611f945760405162461bcd60e51b815260206004820152602a60248201527f53657175656e63657247617465643a2063616c6c6572206973206e6f742074686044820152691948195b991c1bda5b9d60b21b606482015260840161054d565b6001600160a01b038416600090815260a2602052604090205460ff1661201a576001600160a01b038416600081815260a260205260408120805460ff1916600190811790915560a3805491820181559091527f60859188cffe297f44dde29f2d2865634621f26215049caeb304ccba566a8b170180546001600160a01b03191690911790555b63ffffffff831661234557609f54604080516347428e7b60e01b815290516000926001600160a01b0316916347428e7b91600480830192869291908290030181865afa15801561206e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261209691908101906143d5565b9050600060a060009054906101000a90046001600160a01b03166001600160a01b03166347428e7b6040518163ffffffff1660e01b8152600401600060405180830381865afa1580156120ed573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261211591908101906143d5565b905060005b82518163ffffffff16101561224457600063ffffffff16838263ffffffff168151811061214957612149614365565b602002602001015163ffffffff1603156122325760405180606001604052808660070b81526020018560070b8152602001600160ff1681525060a16000896001600160a01b03166001600160a01b031681526020019081526020016000206000858463ffffffff16815181106121c1576121c1614365565b60209081029190910181015163ffffffff1682528181019290925260409081016000208351815493850151949092015160ff16600160801b0260ff60801b1967ffffffffffffffff958616600160401b026001600160801b0319909516959093169490941792909217169190911790555b8061223c8161437b565b91505061211a565b5060005b81518163ffffffff16101561233d5760405180606001604052808660070b81526020018560070b8152602001600160ff1681525060a16000896001600160a01b03166001600160a01b031681526020019081526020016000206000848463ffffffff16815181106122bb576122bb614365565b60209081029190910181015163ffffffff1682528181019290925260409081016000208351815493850151949092015160ff16600160801b0260ff60801b1967ffffffffffffffff958616600160401b026001600160801b031990951695909316949094179290921716919091179055806123358161437b565b915050612248565b5050506123d7565b60408051606081018252600784810b825283900b602080830191825260018385019081526001600160a01b038916600090815260a1835285812063ffffffff8a168252909252939020915182549151935160ff16600160801b0260ff60801b1967ffffffffffffffff958616600160401b026001600160801b0319909416959092169490941791909117169190911790555b50505050565b60606000609f60009054906101000a90046001600160a01b03166001600160a01b03166347428e7b6040518163ffffffff1660e01b8152600401600060405180830381865afa158015612434573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261245c91908101906143d5565b9050600060a060009054906101000a90046001600160a01b03166001600160a01b03166347428e7b6040518163ffffffff1660e01b8152600401600060405180830381865afa1580156124b3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526124db91908101906143d5565b90506000805b83518163ffffffff161015612558578163ffffffff16848263ffffffff168151811061250f5761250f614365565b602002602001015163ffffffff16111561254657838163ffffffff168151811061253b5761253b614365565b602002602001015191505b806125508161437b565b9150506124e1565b5060005b82518163ffffffff1610156125d3578163ffffffff16838263ffffffff168151811061258a5761258a614365565b602002602001015163ffffffff1611156125c157828163ffffffff16815181106125b6576125b6614365565b602002602001015191505b806125cb8161437b565b91505061255c565b5060006125e1826001614318565b63ffffffff1667ffffffffffffffff8111156125ff576125ff613ceb565b604051908082528060200260200182016040528015612628578160200160208202803683370190505b50905060005b8263ffffffff168163ffffffff161161269f5763ffffffff81166000818152609c602052604090205483516001600160a01b03909116918491811061267557612675614365565b6001600160a01b0390921660209283029190910190910152806126978161437b565b91505061262e565b50949350505050565b60606000825184516126ba91906145d9565b67ffffffffffffffff8111156126d2576126d2613ceb565b60405190808252806020026020018201604052801561271d57816020015b60408051606081018252600080825260208083018290529282015282526000199092019101816126f05790505b50905060005b84518110156128385760005b84518110156128255760a1600087848151811061274e5761274e614365565b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020600086838151811061278a5761278a614365565b60209081029190910181015163ffffffff16825281810192909252604090810160002081516060810183529054600781810b8352600160401b8204900b93820193909352600160801b90920460ff16908201528551849083906127ed90866145d9565b6127f791906145f8565b8151811061280757612807614365565b6020026020010181905250808061281d90614610565b91505061272f565b508061283081614610565b915050612723565b509392505050565b63ffffffff81166000908152609b6020526040812054611f2190600160401b900460070b633b9aca00614233565b612876612e2e565b6001600160a01b0381166128f25760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161054d565b6128fb81612e88565b50565b604080516080810182526000808252602082018190529181018290526060810191909152609a5460405163deb14ec360e01b815263ffffffff841660048201526000916001600160a01b03169063deb14ec390602401602060405180830381865afa158015612971573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061299591906143b8565b60a0549091506001600160a01b039081169082168190036129e65750604080516080810182526001600160a01b0390921682526000602083015260019082015263ffffffff90921660608301525090565b5050604080516080810182526000808252609f546001600160a01b031660208301529181019190915263ffffffff90921660608301525090565b600080670de0b6b3a7640000600f85810b9085900b025b0590506f7fffffffffffffffffffffffffffffff198112801590612a62575060016001607f1b038113155b6040518060400160405280600281526020016127a360f11b815250906128385760405162461bcd60e51b815260040161054d919061411c565b600080612aae8a8a8a8a8a8a8a8a6135df565b915091505b9850989650505050505050565b846040015115612b51578451606086015160405163f8a42e5160e01b815263ffffffff909116600482015260248101859052600f84810b604483015283900b60648201526001600160a01b039091169063f8a42e51906084015b600060405180830381600087803b158015612b3457600080fd5b505af1158015612b48573d6000803e3d6000fd5b50505050612ca6565b63ffffffff8416612bb3576020850151606086015160405163f8a42e5160e01b815263ffffffff909116600482015260248101859052600f84810b604483015283900b60648201526001600160a01b039091169063f8a42e5190608401612b1a565b6020850151606086015160405163e0b0621f60e01b815263ffffffff909116600482015260248101859052600f84900b60448201526001600160a01b039091169063e0b0621f90606401600060405180830381600087803b158015612c1757600080fd5b505af1158015612c2b573d6000803e3d6000fd5b505050602086015160405163e0b0621f60e01b815263ffffffff8716600482015260248101869052600f84900b60448201526001600160a01b03909116915063e0b0621f90606401600060405180830381600087803b158015612c8d57600080fd5b505af1158015612ca1573d6000803e3d6000fd5b505050505b5050505050565b609f546001600160a01b0316331480612cd0575060a0546001600160a01b031633145b6113995760405162461bcd60e51b815260206004820152601d60248201527f6f6e6c7920656e67696e652063616e206d6f6469667920636f6e666967000000604482015260640161054d565b600054610100900460ff16612d875760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161054d565b611399613754565b612d97612e2e565b606580546001600160a01b0319166001600160a01b0392909216919091179055565b600054610100900460ff16612e245760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161054d565b61138382826137c8565b6033546001600160a01b031633146113995760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161054d565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b82515160009060001901612ef05750600161310d565b83516000848152609e602052604090819020549082018051600f9290920b91829190612f1d908390614558565b600f0b9052506060820151603d1c6001908116036130d55760008860400151612fc657602089015160608a01518451604051637c1e148760e01b815263ffffffff909216600483015260248201526001600160a01b0390911690637c1e1487906044016040805180830381865afa158015612f9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fc09190614629565b51613046565b885160608a01518451604051637c1e148760e01b815263ffffffff909216600483015260248201526001600160a01b0390911690637c1e148790604401606060405180830381865afa158015613020573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130449190614688565b515b9050600081600f0b13151560008460400151600f0b1315150361306f57600060408401526130d3565b60008360400151600f0b13156130a35761309683604001518261309190614187565b61384d565b600f0b60408401526130d3565b60008360400151600f0b12156130d3576130ca8360400151826130c590614187565b61386b565b600f0b60408401525b505b60008260200151600f0b1380156130f257506040820151600f0b15155b801561310857506131068260600151613880565b155b925050505b95945050505050565b6000808061313361312b600f87900b8861339f565b600f0b6138b1565b90506000846000015160400151600f0b131561320157836000015160200151600f0b81600f0b1315604051806040016040528060048152602001634f43424d60e01b815250906131965760405162461bcd60e51b815260040161054d919061411c565b50600086600f0b1280156131c057506131ae86614187565b600f0b846000015160400151600f0b12155b604051806040016040528060048152602001634f43424d60e01b815250906131fb5760405162461bcd60e51b815260040161054d919061411c565b506132b5565b836000015160200151600f0b81600f0b1215604051806040016040528060048152602001634f43424d60e01b8152509061324e5760405162461bcd60e51b815260040161054d919061411c565b50600086600f0b138015613278575061326686614187565b600f0b846000015160400151600f0b13155b604051806040016040528060048152602001634f43424d60e01b815250906132b35760405162461bcd60e51b815260040161054d919061411c565b505b600087604001516132ca5787602001516132cd565b87515b606089015160405163c7167cf560e01b815263ffffffff9091166004820152600f89810b602483015288900b604482015290915060009081906001600160a01b0384169063c7167cf59060640160408051808303816000875af1158015613338573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061335c91906141ad565b9150915081876000015160400181815161337691906146fa565b600f0b90525061338582614187565b61338e82614187565b955095505050505094509492505050565b600081600f0b600014156040518060400160405280600381526020016222212d60e91b815250906133e35760405162461bcd60e51b815260040161054d919061411c565b50600082600f0b670de0b6b3a7640000600f0b85600f0b0281612a3757612a376140e4565b60008060008560400151600f0b121561343857613431856040015185604001516130c590614187565b9150613469565b60008560400151600f0b131561345e576134318560400151856040015161309190614187565b5060009050806135d5565b604086015161347890836140fa565b6134829083614558565b915060006134a0856020015184600f0b612a2090919063ffffffff16565b90506134ab81614187565b915060006134d2896060015187600001518a876134c790614187565b866000806000612a9b565b809350819250505083876040018181516134ec9190614558565b600f0b9052506040860180518591906135069083906146fa565b600f0b90525087518651613525918b9161351f88614187565b86612ac0565b856000015185602001518a6060015163ffffffff167f7c57459d6f4f0fb2fc5b1e298c8c0eb238422944964aa1e249eaa78747f0cca9896020015189604001518b606001518c608001516000898d61357c90614187565b60408051600f98890b815296880b602088015267ffffffffffffffff9586169087015293909216606085015215156080840152830b60a0830152820b60c08201529087900b60e08201526101000160405180910390a450505b9550959350505050565b60008060001989016135f657506000905084612ab3565b600083156136b15785600f0b60000361363f57602089015161361b90600f0b86612a20565b61362590826146fa565b9050600087600f0b121561363f5761363c81614187565b90505b600061364d89600f0b6138b1565b905060008a6020015161366a8b8a61366591906146fa565b61391b565b6136749190614558565b9050613680818361384d565b9050600081600f0b13156136aa5761369d600f8a900b8284613936565b6136a790846146fa565b92505b50506136be565b6136bb87826146fa565b90505b60006136cb8b8d87611e5a565b6136dd90670de0b6b3a7640000614558565b905060008083600f0b136136fe576136f9600f84900b8361339f565b61370c565b61370c600f84900b83612a20565b9050600061371a8285614558565b9050808c60600181815161372e91906146fa565b600f0b9052508061373f818c614558565b95509550505050509850989650505050505050565b600054610100900460ff166137bf5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161054d565b61139933612e88565b600054610100900460ff166138335760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161054d565b815160209283012081519190920120606691909155606755565b600081600f0b83600f0b126138625781613864565b825b9392505050565b600081600f0b83600f0b136138625781613864565b600061388a6139fb565b6001600160801b0316826703ffffffffffffff1667ffffffffffffffff1611159050919050565b60408051808201909152600281526127a360f11b6020820152600090600f83900b6f7fffffffffffffffffffffffffffffff19036139025760405162461bcd60e51b815260040161054d919061411c565b50600082600f0b126139145781611f21565b5060000390565b60008082600f0b1261392d5781611f21565b611f2182614187565b600081600f0b600014156040518060400160405280600381526020016222212d60e91b8152509061397a5760405162461bcd60e51b815260040161054d919061411c565b50600082600f0b84600f0b86600f0b0281613997576139976140e4565b0590506f7fffffffffffffffffffffffffffffff1981128015906139c2575060016001607f1b038113155b6040518060400160405280600281526020016127a360f11b8152509061269f5760405162461bcd60e51b815260040161054d919061411c565b60655460408051632abf68dd60e11b815290516000926001600160a01b03169163557ed1ba9160048083019260209291908290030181865afa158015613a45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a699190614749565b905090565b63ffffffff811681146128fb57600080fd5b60008060408385031215613a9357600080fd5b823591506020830135613aa581613a6e565b809150509250929050565b600060808284031215613ac257600080fd5b50919050565b600060808284031215613ada57600080fd5b6138648383613ab0565b600060208284031215613af657600080fd5b813561386481613a6e565b6001600160a01b03811681146128fb57600080fd5b80600f0b81146128fb57600080fd5b60008060008060008060c08789031215613b3e57600080fd5b8635613b4981613a6e565b95506020870135613b5981613a6e565b94506040870135613b6981613b01565b93506060870135613b7981613b16565b92506080870135613b8981613b16565b915060a0870135613b9981613b16565b809150509295509295509295565b60008060408385031215613bba57600080fd5b8235613bc581613a6e565b91506020830135613aa581613a6e565b6020808252825182820181905260009190848201906040850190845b81811015613c165783516001600160a01b031683529284019291840191600101613bf1565b50909695505050505050565b600060208284031215613c3457600080fd5b5035919050565b60008060408385031215613c4e57600080fd5b8235613c5981613b01565b91506020830135613aa581613b01565b60008060408385031215613c7c57600080fd5b823567ffffffffffffffff811115613c9357600080fd5b613c9f85828601613ab0565b9250506020830135613aa581613b01565b600060208284031215613cc257600080fd5b813567ffffffffffffffff811115613cd957600080fd5b82016060818503121561386457600080fd5b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715613d2457613d24613ceb565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715613d5357613d53613ceb565b604052919050565b803567ffffffffffffffff81168114613d7357600080fd5b919050565b600060a08284031215613d8a57600080fd5b60405160a0810181811067ffffffffffffffff82111715613dad57613dad613ceb565b604052823581529050806020830135613dc581613b16565b60208201526040830135613dd881613b16565b6040820152613de960608401613d5b565b6060820152613dfa60808401613d5b565b60808201525092915050565b60008060c08385031215613e1957600080fd5b8235613e2481613a6e565b9150613e338460208501613d78565b90509250929050565b600080600060608486031215613e5157600080fd5b833592506020840135613e6381613a6e565b915060408401358015158114613e7857600080fd5b809150509250925092565b8035600781900b8114613d7357600080fd5b60008060008060808587031215613eab57600080fd5b8435613eb681613b01565b93506020850135613ec681613a6e565b9250613ed460408601613e83565b9150613ee260608601613e83565b905092959194509250565b600067ffffffffffffffff821115613f0757613f07613ceb565b5060051b60200190565b600082601f830112613f2257600080fd5b81356020613f37613f3283613eed565b613d2a565b82815260059290921b84018101918181019086841115613f5657600080fd5b8286015b84811015613f7a578035613f6d81613a6e565b8352918301918301613f5a565b509695505050505050565b60008060408385031215613f9857600080fd5b823567ffffffffffffffff80821115613fb057600080fd5b818501915085601f830112613fc457600080fd5b81356020613fd4613f3283613eed565b82815260059290921b84018101918181019089841115613ff357600080fd5b948201945b8386101561401a57853561400b81613b01565b82529482019490820190613ff8565b9650508601359250508082111561403057600080fd5b5061403d85828601613f11565b9150509250929050565b602080825282518282018190526000919060409081850190868401855b8281101561409d5781518051600790810b865287820151900b8786015285015160ff168585015260609093019290850190600101614064565b5091979650505050505050565b6000602082840312156140bc57600080fd5b813561386481613b01565b6000602082840312156140d957600080fd5b813561386481613b16565b634e487b7160e01b600052601260045260246000fd5b600082600f0b8061410d5761410d6140e4565b8083600f0b0791505092915050565b600060208083528351808285015260005b818110156141495785810183015185820160400152820161412d565b8181111561415b576000604083870101525b50601f01601f1916929092016040019392505050565b634e487b7160e01b600052601160045260246000fd5b600081600f0b60016001607f1b031981036141a4576141a4614171565b60000392915050565b600080604083850312156141c057600080fd5b82516141cb81613b16565b6020840151909250613aa581613b16565b634e487b7160e01b600052602160045260246000fd5b8281526040810160038310614209576142096141dc565b8260208301529392505050565b60006020828403121561422857600080fd5b815161386481613b16565b600081600f0b83600f0b60016001607f1b0360008213600084138383048511828216161561426357614263614171565b6f7fffffffffffffffffffffffffffffff19600085128281168783058712161561428f5761428f614171565b600087129250858205871284841616156142ab576142ab614171565b858505871281841616156142c1576142c1614171565b5050509290910295945050505050565b600081600f0b83600f0b806142e8576142e86140e4565b6f7fffffffffffffffffffffffffffffff1982146000198214161561430f5761430f614171565b90059392505050565b600063ffffffff80831681851680830382111561433757614337614171565b01949350505050565b600063ffffffff8381169083168181101561435d5761435d614171565b039392505050565b634e487b7160e01b600052603260045260246000fd5b600063ffffffff80831681810361439457614394614171565b6001019392505050565b60208101600283106143b2576143b26141dc565b91905290565b6000602082840312156143ca57600080fd5b815161386481613b01565b600060208083850312156143e857600080fd5b825167ffffffffffffffff8111156143ff57600080fd5b8301601f8101851361441057600080fd5b805161441e613f3282613eed565b81815260059190911b8201830190838101908783111561443d57600080fd5b928401925b8284101561446457835161445581613a6e565b82529284019290840190614442565b979650505050505050565b6000823560be1983360301811261448557600080fd5b9190910192915050565b600060a082840312156144a157600080fd5b6138648383613d78565b600060c082360312156144bd57600080fd5b6144c5613d01565b6144cf3684613d78565b815260a083013567ffffffffffffffff808211156144ec57600080fd5b9084019036601f8301126144ff57600080fd5b813560208282111561451357614513613ceb565b614525601f8301601f19168201613d2a565b9250818352368183860101111561453b57600080fd5b818185018285013760009183018101919091528301525092915050565b600081600f0b83600f0b600081128160016001607f1b03190183128115161561458357614583614171565b8160016001607f1b0301831381161561459e5761459e614171565b5090039392505050565b60008235605e1983360301811261448557600080fd5b6000602082840312156145d057600080fd5b61386482613d5b565b60008160001904831182151516156145f3576145f3614171565b500290565b6000821982111561460b5761460b614171565b500190565b60006001820161462257614622614171565b5060010190565b60006040828403121561463b57600080fd5b6040516040810181811067ffffffffffffffff8211171561465e5761465e613ceb565b604052825161466c81613b16565b8152602083015161467c81613b16565b60208201529392505050565b60006060828403121561469a57600080fd5b6040516060810181811067ffffffffffffffff821117156146bd576146bd613ceb565b60405282516146cb81613b16565b815260208301516146db81613b16565b602082015260408301516146ee81613b16565b60408201529392505050565b600081600f0b83600f0b600082128260016001607f1b030382138115161561472457614724614171565b8260016001607f1b031903821281161561474057614740614171565b50019392505050565b60006020828403121561475b57600080fd5b81516001600160801b038116811461386457600080fdfe4f7264657228627974657333322073656e6465722c696e743132382070726963655831382c696e7431323820616d6f756e742c75696e7436342065787069726174696f6e2c75696e743634206e6f6e636529a264697066735822122021c143a1fd67561bb17abafc2a8a724f3aa356c5e82ae9d5a21ef14480331cbd64736f6c634300080d0033

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061018d5760003560e01c806378f0d3ce116100e3578063b60aaa7c1161008c578063d895202a11610066578063d895202a14610404578063f2b2633114610424578063f2fde38b1461043757600080fd5b8063b60aaa7c146103d6578063b76d78e3146103e9578063ce933e59146103fc57600080fd5b806395ee6071116100bd57806395ee607114610391578063aed8e967146103b2578063b5cbd70e146103c357600080fd5b806378f0d3ce1461035a57806388bc79681461036d5780638da5cb5b1461038057600080fd5b806340f1a34d1161014557806366f87bd11161011f57806366f87bd114610303578063707c8b581461034a578063715018a61461035257600080fd5b806340f1a34d146102665780634821c8b51461029c578063485cc955146102f057600080fd5b80631d029b4d116101765780631d029b4d146101d95780632da1c59b146102335780633fceea281461024657600080fd5b80630f2c878e146101925780630f4b509d146101c4575b600080fd5b6101a56101a0366004613a80565b61044a565b60408051600f93840b81529190920b6020820152015b60405180910390f35b6101d76101d2366004613ac8565b6104e4565b005b6101ec6101e7366004613ae4565b610885565b6040516101bb9190600060808201905063ffffffff83511682526020830151600f0b60208301526040830151600f0b60408301526060830151600f0b606083015292915050565b6101d7610241366004613b25565b610946565b610259610254366004613ba7565b610b08565b6040516101bb9190613bd5565b610289610274366004613c22565b609e60205260009081526040902054600f0b81565b604051600f9190910b81526020016101bb565b6102de6102aa366004613ae4565b604080516020808201835260009182905263ffffffff939093168152609d8352819020815192830190915254600f0b815290565b6040519051600f0b81526020016101bb565b6101d76102fe366004613c3b565b610c38565b610332610311366004613ae4565b63ffffffff166000908152609c60205260409020546001600160a01b031690565b6040516001600160a01b0390911681526020016101bb565b6101d7610efd565b6101d7611387565b6101d7610368366004613c69565b61139b565b6101d761037b366004613cb0565b6116d7565b6033546001600160a01b0316610332565b6103a461039f366004613e06565b611cda565b6040519081526020016101bb565b6065546001600160a01b0316610332565b6102896103d1366004613e3c565b611e5a565b6102896103e4366004613ae4565b611efa565b6101d76103f7366004613e95565b611f27565b6102596123dd565b610417610412366004613f85565b6126a8565b6040516101bb9190614047565b610289610432366004613ae4565b612840565b6101d76104453660046140aa565b61286e565b606082811c600090815260a16020908152604080832063ffffffff8616845282528083208151948501825254600781810b8652600160401b8204900b92850192909252600160801b90910460ff169083018190529091829182036104cd5750604080516060810182526000815265b5e620f4800060208201526001918101919091525b60208101519051600791820b96910b945092505050565b6065546001600160a01b031633146105565760405162461bcd60e51b815260206004820152602a60248201527f53657175656e63657247617465643a2063616c6c6572206973206e6f742074686044820152691948195b991c1bda5b9d60b21b60648201526084015b60405180910390fd5b600061056b6101e76040840160208501613ae4565b905060006105876105826040850160208601613ae4565b6128fe565b90508060400151156105f35781604001518360400160208101906105ab91906140c7565b6105b591906140fa565b60408051808201909152600381526204953560ec1b602082015290600f0b156105f15760405162461bcd60e51b815260040161054d919061411c565b505b6000816040015161060857816020015161060b565b81515b90506000806001600160a01b03831663c7167cf561062f6040890160208a01613ae4565b61063f60608a0160408b016140c7565b61066b61065260808c0160608d016140c7565b61066260608d0160408e016140c7565b600f0b90612a20565b61067490614187565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815263ffffffff939093166004840152600f91820b6024840152900b604482015260640160408051808303816000875af11580156106e0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061070491906141ad565b915091508161071290614187565b915061071d81614187565b905060006107676107346040890160208a01613ae4565b88358886866000600f83900b13610758578b6020015161075390614187565b61075e565b8b602001515b60006001612a9b565b875190935090915061077e90869089358686612ac0565b609a546040516388b6496f60e01b81526000916001600160a01b0316906388b6496f906107b2908b359085906004016141f2565b602060405180830381865afa1580156107cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107f39190614216565b600f0b121560405180604001604052806002815260200161125560f21b815250906108315760405162461bcd60e51b815260040161054d919061411c565b506060860151609b600061084b60408b0160208c01613ae4565b63ffffffff168152602081019190915260400160002080546001600160801b03928316600160801b02921691909117905550505050505050565b6040805160808101825260008082526020808301829052828401829052606080840183815263ffffffff878116808652609b85528786208851948501895254600781810b8652600160401b8204810b86880152600160801b909104600f90810b868b0190815292885260a49096529790952054168552925190910b909152805191929091610918910b633b9aca00614233565b600f0b6020808401919091528101516109389060070b633b9aca00614233565b600f0b604083015250919050565b61094e612cad565b6001600160a01b038416156109f85763ffffffff86166000908152609c60205260409020546001600160a01b0316156109c95760405162461bcd60e51b815260206004820152601860248201527f7669727475616c20626f6f6b20616c7265616479207365740000000000000000604482015260640161054d565b63ffffffff86166000908152609c6020526040902080546001600160a01b0319166001600160a01b0386161790555b63ffffffff85811614610a2e5763ffffffff868116600090815260a460205260409020805463ffffffff19169187169190911790555b610a3c633b9aca00836142d1565b63ffffffff87166000908152609b60205260409020805467ffffffffffffffff191667ffffffffffffffff92909216919091179055610a7f633b9aca00846142d1565b63ffffffff969096166000818152609b6020908152604080832080546fffffffffffffffff00000000000000001916600160401b67ffffffffffffffff909c169b909b029a909a1790995588518082018a52600f9490940b8452918152609d90915295909520945185546001600160801b0319166001600160801b039091161790945550505050565b60606000610b168385614318565b60a35490915063ffffffff8082169083161115610b31578091505b8063ffffffff168563ffffffff161115610b49578094505b6000610b558684614340565b63ffffffff1667ffffffffffffffff811115610b7357610b73613ceb565b604051908082528060200260200182016040528015610b9c578160200160208202803683370190505b509050855b8363ffffffff168163ffffffff161015610c2e5760a38163ffffffff1681548110610bce57610bce614365565b6000918252602090912001546001600160a01b031682610bee8984614340565b63ffffffff1681518110610c0457610c04614365565b6001600160a01b039092166020928302919091019091015280610c268161437b565b915050610ba1565b5095945050505050565b600054610100900460ff1615808015610c585750600054600160ff909116105b80610c725750303b158015610c72575060005460ff166001145b610ce45760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840161054d565b6000805460ff191660011790558015610d07576000805461ff0019166101001790555b610d0f612d1c565b610d1882612d8f565b610d8c6040518060400160405280600681526020017f56657274657800000000000000000000000000000000000000000000000000008152506040518060400160405280600581526020017f302e302e31000000000000000000000000000000000000000000000000000000815250612db9565b609a80546001600160a01b0319166001600160a01b038516908117909155604051635d2e9ad160e01b8152635d2e9ad190610dcc9060009060040161439e565b602060405180830381865afa158015610de9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0d91906143b8565b609f80546001600160a01b0319166001600160a01b03928316179055609a54604051635d2e9ad160e01b8152911690635d2e9ad190610e519060019060040161439e565b602060405180830381865afa158015610e6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9291906143b8565b60a080546001600160a01b0319166001600160a01b03929092169190911790558015610ef8576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050565b6065546001600160a01b03163314610f6a5760405162461bcd60e51b815260206004820152602a60248201527f53657175656e63657247617465643a2063616c6c6572206973206e6f742074686044820152691948195b991c1bda5b9d60b21b606482015260840161054d565b609f54604080516347428e7b60e01b815290516000926001600160a01b0316916347428e7b91600480830192869291908290030181865afa158015610fb3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610fdb91908101906143d5565b905060015b81518163ffffffff161015611175576000828263ffffffff168151811061100957611009614365565b60209081029190910181015163ffffffff81166000908152609b8352604080822081516060810183529054600781810b8352600160401b8204900b95820195909552600160801b909404600f0b9084018190529193501261106b575050611163565b609f5463ffffffff838116600090815260a460205260408082205485820151915163e0b0621f60e01b8152931660048401526024830191909152600f0b60448201526001600160a01b039091169063e0b0621f90606401600060405180830381600087803b1580156110dc57600080fd5b505af11580156110f0573d6000803e3d6000fd5b50506000604080850182815263ffffffff9096168252609b6020908152912084518154929095015195516001600160801b03908116600160801b0267ffffffffffffffff978816600160401b026001600160801b0319909416979096169690961791909117949094169290921790925550505b8061116d8161437b565b915050610fe0565b5060a060009054906101000a90046001600160a01b03166001600160a01b03166347428e7b6040518163ffffffff1660e01b8152600401600060405180830381865afa1580156111c9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526111f191908101906143d5565b905060005b81518163ffffffff161015611383576000828263ffffffff168151811061121f5761121f614365565b60209081029190910181015163ffffffff81166000908152609b8352604080822081516060810183529054600781810b8352600160401b8204900b95820195909552600160801b909404600f0b90840181905291935003611281575050611371565b60a054604082810151905163f8a42e5160e01b815263ffffffff851660048201526000602482018190526044820152600f9190910b60648201526001600160a01b039091169063f8a42e5190608401600060405180830381600087803b1580156112ea57600080fd5b505af11580156112fe573d6000803e3d6000fd5b50506000604080850182815263ffffffff9096168252609b6020908152912084518154929095015195516001600160801b03908116600160801b0267ffffffffffffffff978816600160401b026001600160801b0319909416979096169690961791909117949094169290921790925550505b8061137b8161437b565b9150506111f6565b5050565b61138f612e2e565b6113996000612e88565b565b6065546001600160a01b031633146114085760405162461bcd60e51b815260206004820152602a60248201527f53657175656e63657247617465643a2063616c6c6572206973206e6f742074686044820152691948195b991c1bda5b9d60b21b606482015260840161054d565b600061141a6105826020850185613ae4565b9050600061142e6101e76020860186613ae4565b9050600061145f6114426020870187613ae4565b61144f606088018861446f565b61039f903681900381019061448f565b90506000611470606087018761446f565b6114819060608101906040016140c7565b90506000611492606088018861446f565b61149b906144ab565b90506114aa858583868a612eda565b60405180604001604052806002815260200161125560f21b815250906114e35760405162461bcd60e51b815260040161054d919061411c565b50600080611511876114fb60408c0160208d016140c7565b61150b60608d0160408e016140c7565b86613116565b9092509050600061156c61152860208c018c613ae4565b855180516040909101518a90879087908290611544908d614558565b61154e9190614558565b61155c600f8a900b8b61339f565b61156590614187565b6001612a9b565b8851865151919450919250611584918a918686612ac0565b604080518082019091526002815261125560f21b602082015250835151866115af60208d018d613ae4565b865160208082015160608084015160809485015160408051600f95860b81528f860b9681019690965267ffffffffffffffff92831690860152169083015260019282019290925285820b60a082015287820b60c08201529086900b60e082015263ffffffff91909116907f7c57459d6f4f0fb2fc5b1e298c8c0eb238422944964aa1e249eaa78747f0cca9906101000160405180910390a46060870151609b600061165d60208e018e613ae4565b63ffffffff1681526020810191909152604090810160002080546001600160801b03938416600160801b029316929092179091558451015161169f9086614558565b6000968752609e602052604090962080546001600160801b0319166001600160801b0390971696909617909555505050505050505050565b6065546001600160a01b031633146117445760405162461bcd60e51b815260206004820152602a60248201527f53657175656e63657247617465643a2063616c6c6572206973206e6f742074686044820152691948195b991c1bda5b9d60b21b606482015260840161054d565b600061176161175383806145a8565b610582906020810190613ae4565b604080516060810182526000808252602082018190529181018290529192509081908190819060006117968760600151610885565b905060006117a489806145a8565b6117b290602081019061446f565b6117bb906144ab565b905060006117c98a806145a8565b6117d790604081019061446f565b6117e0906144ab565b905060405180606001604052806117ff8b606001518560000151611cda565b81526020016118168b606001518460000151611cda565b8152602001826000015160400151600f0b8152509350816000015160400151975061185a89848487600001518e602001602081019061185591906140aa565b612eda565b60405180604001604052806002815260200161125560f21b815250906118935760405162461bcd60e51b815260040161054d919061411c565b506118b289848387602001518e604001602081019061185591906140aa565b60405180604001604052806002815260200161494d60f01b815250906118eb5760405162461bcd60e51b815260040161054d919061411c565b5081516040908101518251820151825180840190935260048352634f43424d60e01b60208401526000600f91820b81129290910b130361193e5760405162461bcd60e51b815260040161054d919061411c565b506000816000015160400151600f0b13156119ae57816000015160200151600f0b816000015160200151600f0b1215604051806040016040528060048152602001634f43424d60e01b815250906119a85760405162461bcd60e51b815260040161054d919061411c565b50611a05565b816000015160200151600f0b816000015160200151600f0b1315604051806040016040528060048152602001634f43424d60e01b81525090611a035760405162461bcd60e51b815260040161054d919061411c565b505b611a1a89848460000151846000015188613408565b8096508197505050611a6289606001518360000151600001518589898b8860000151604001518f611a4b9190614558565b611a559190614558565b8751602001516001612a9b565b8451845151929950909650611a7b918b91908989612ac0565b604080518082019091526002815261125560f21b602082015250604080518082019091526002815261494d60f01b602082015250606080840151908a015163ffffffff166000908152609b6020526040902080546001600160801b03928316600160801b0292169190911790556001611af48b806145a8565b611b0290602081019061446f565b3514611b4757815160400151611b189089614558565b84516000908152609e6020526040902080546001600160801b0319166001600160801b03929092169190911790555b6001611b538b806145a8565b611b6190604081019061446f565b3514611bb2578060000151604001518460400151611b7f9190614558565b6020858101516000908152609e9091526040902080546001600160801b0319166001600160801b03929092169190911790555b50611bc19150889050806145a8565b611bcf90602081019061446f565b8151606088015191359163ffffffff167f7c57459d6f4f0fb2fc5b1e298c8c0eb238422944964aa1e249eaa78747f0cca9611c0a8b806145a8565b611c1890602081019061446f565b611c299060408101906020016140c7565b89611c348d806145a8565b611c4290602081019061446f565b611c539060808101906060016145be565b611c5d8e806145a8565b611c6b90602081019061446f565b611c7c9060a08101906080016145be565b60408051600f95860b815293850b602085015267ffffffffffffffff928316908401521660608201526001608082015289820b60a082015288820b60c08201529087900b60e08201526101000160405180910390a450505050505050565b6000806040518060800160405280605281526020016147736052913990506000818051906020012084600001518560200151866040015187606001518860800151604051602001611d64969594939291909586526020860194909452600f92830b6040860152910b606084015267ffffffffffffffff90811660808401521660a082015260c00190565b60405160208183030381529060405280519060200120905060007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f611da860665490565b60675463ffffffff89166000908152609c6020908152604091829020548251918201959095529081019290925260608201524660808201526001600160a01b0390911660a082015260c001604051602081830303815290604052805190602001209050611e50818360405161190160f01b6020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b9695505050505050565b606083811c600090815260a16020908152604080832063ffffffff8716845282528083208151948501825254600781810b8652600160401b8204900b92850192909252600160801b90910460ff169083018190529091908203611edc5750604080516060810182526000815265b5e620f4800060208201526001918101919091525b82611ee8578051611eee565b80602001515b60070b95945050505050565b63ffffffff81166000908152609b6020526040812054611f219060070b633b9aca00614233565b92915050565b6065546001600160a01b03163314611f945760405162461bcd60e51b815260206004820152602a60248201527f53657175656e63657247617465643a2063616c6c6572206973206e6f742074686044820152691948195b991c1bda5b9d60b21b606482015260840161054d565b6001600160a01b038416600090815260a2602052604090205460ff1661201a576001600160a01b038416600081815260a260205260408120805460ff1916600190811790915560a3805491820181559091527f60859188cffe297f44dde29f2d2865634621f26215049caeb304ccba566a8b170180546001600160a01b03191690911790555b63ffffffff831661234557609f54604080516347428e7b60e01b815290516000926001600160a01b0316916347428e7b91600480830192869291908290030181865afa15801561206e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261209691908101906143d5565b9050600060a060009054906101000a90046001600160a01b03166001600160a01b03166347428e7b6040518163ffffffff1660e01b8152600401600060405180830381865afa1580156120ed573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261211591908101906143d5565b905060005b82518163ffffffff16101561224457600063ffffffff16838263ffffffff168151811061214957612149614365565b602002602001015163ffffffff1603156122325760405180606001604052808660070b81526020018560070b8152602001600160ff1681525060a16000896001600160a01b03166001600160a01b031681526020019081526020016000206000858463ffffffff16815181106121c1576121c1614365565b60209081029190910181015163ffffffff1682528181019290925260409081016000208351815493850151949092015160ff16600160801b0260ff60801b1967ffffffffffffffff958616600160401b026001600160801b0319909516959093169490941792909217169190911790555b8061223c8161437b565b91505061211a565b5060005b81518163ffffffff16101561233d5760405180606001604052808660070b81526020018560070b8152602001600160ff1681525060a16000896001600160a01b03166001600160a01b031681526020019081526020016000206000848463ffffffff16815181106122bb576122bb614365565b60209081029190910181015163ffffffff1682528181019290925260409081016000208351815493850151949092015160ff16600160801b0260ff60801b1967ffffffffffffffff958616600160401b026001600160801b031990951695909316949094179290921716919091179055806123358161437b565b915050612248565b5050506123d7565b60408051606081018252600784810b825283900b602080830191825260018385019081526001600160a01b038916600090815260a1835285812063ffffffff8a168252909252939020915182549151935160ff16600160801b0260ff60801b1967ffffffffffffffff958616600160401b026001600160801b0319909416959092169490941791909117169190911790555b50505050565b60606000609f60009054906101000a90046001600160a01b03166001600160a01b03166347428e7b6040518163ffffffff1660e01b8152600401600060405180830381865afa158015612434573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261245c91908101906143d5565b9050600060a060009054906101000a90046001600160a01b03166001600160a01b03166347428e7b6040518163ffffffff1660e01b8152600401600060405180830381865afa1580156124b3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526124db91908101906143d5565b90506000805b83518163ffffffff161015612558578163ffffffff16848263ffffffff168151811061250f5761250f614365565b602002602001015163ffffffff16111561254657838163ffffffff168151811061253b5761253b614365565b602002602001015191505b806125508161437b565b9150506124e1565b5060005b82518163ffffffff1610156125d3578163ffffffff16838263ffffffff168151811061258a5761258a614365565b602002602001015163ffffffff1611156125c157828163ffffffff16815181106125b6576125b6614365565b602002602001015191505b806125cb8161437b565b91505061255c565b5060006125e1826001614318565b63ffffffff1667ffffffffffffffff8111156125ff576125ff613ceb565b604051908082528060200260200182016040528015612628578160200160208202803683370190505b50905060005b8263ffffffff168163ffffffff161161269f5763ffffffff81166000818152609c602052604090205483516001600160a01b03909116918491811061267557612675614365565b6001600160a01b0390921660209283029190910190910152806126978161437b565b91505061262e565b50949350505050565b60606000825184516126ba91906145d9565b67ffffffffffffffff8111156126d2576126d2613ceb565b60405190808252806020026020018201604052801561271d57816020015b60408051606081018252600080825260208083018290529282015282526000199092019101816126f05790505b50905060005b84518110156128385760005b84518110156128255760a1600087848151811061274e5761274e614365565b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020600086838151811061278a5761278a614365565b60209081029190910181015163ffffffff16825281810192909252604090810160002081516060810183529054600781810b8352600160401b8204900b93820193909352600160801b90920460ff16908201528551849083906127ed90866145d9565b6127f791906145f8565b8151811061280757612807614365565b6020026020010181905250808061281d90614610565b91505061272f565b508061283081614610565b915050612723565b509392505050565b63ffffffff81166000908152609b6020526040812054611f2190600160401b900460070b633b9aca00614233565b612876612e2e565b6001600160a01b0381166128f25760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161054d565b6128fb81612e88565b50565b604080516080810182526000808252602082018190529181018290526060810191909152609a5460405163deb14ec360e01b815263ffffffff841660048201526000916001600160a01b03169063deb14ec390602401602060405180830381865afa158015612971573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061299591906143b8565b60a0549091506001600160a01b039081169082168190036129e65750604080516080810182526001600160a01b0390921682526000602083015260019082015263ffffffff90921660608301525090565b5050604080516080810182526000808252609f546001600160a01b031660208301529181019190915263ffffffff90921660608301525090565b600080670de0b6b3a7640000600f85810b9085900b025b0590506f7fffffffffffffffffffffffffffffff198112801590612a62575060016001607f1b038113155b6040518060400160405280600281526020016127a360f11b815250906128385760405162461bcd60e51b815260040161054d919061411c565b600080612aae8a8a8a8a8a8a8a8a6135df565b915091505b9850989650505050505050565b846040015115612b51578451606086015160405163f8a42e5160e01b815263ffffffff909116600482015260248101859052600f84810b604483015283900b60648201526001600160a01b039091169063f8a42e51906084015b600060405180830381600087803b158015612b3457600080fd5b505af1158015612b48573d6000803e3d6000fd5b50505050612ca6565b63ffffffff8416612bb3576020850151606086015160405163f8a42e5160e01b815263ffffffff909116600482015260248101859052600f84810b604483015283900b60648201526001600160a01b039091169063f8a42e5190608401612b1a565b6020850151606086015160405163e0b0621f60e01b815263ffffffff909116600482015260248101859052600f84900b60448201526001600160a01b039091169063e0b0621f90606401600060405180830381600087803b158015612c1757600080fd5b505af1158015612c2b573d6000803e3d6000fd5b505050602086015160405163e0b0621f60e01b815263ffffffff8716600482015260248101869052600f84900b60448201526001600160a01b03909116915063e0b0621f90606401600060405180830381600087803b158015612c8d57600080fd5b505af1158015612ca1573d6000803e3d6000fd5b505050505b5050505050565b609f546001600160a01b0316331480612cd0575060a0546001600160a01b031633145b6113995760405162461bcd60e51b815260206004820152601d60248201527f6f6e6c7920656e67696e652063616e206d6f6469667920636f6e666967000000604482015260640161054d565b600054610100900460ff16612d875760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161054d565b611399613754565b612d97612e2e565b606580546001600160a01b0319166001600160a01b0392909216919091179055565b600054610100900460ff16612e245760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161054d565b61138382826137c8565b6033546001600160a01b031633146113995760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161054d565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b82515160009060001901612ef05750600161310d565b83516000848152609e602052604090819020549082018051600f9290920b91829190612f1d908390614558565b600f0b9052506060820151603d1c6001908116036130d55760008860400151612fc657602089015160608a01518451604051637c1e148760e01b815263ffffffff909216600483015260248201526001600160a01b0390911690637c1e1487906044016040805180830381865afa158015612f9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fc09190614629565b51613046565b885160608a01518451604051637c1e148760e01b815263ffffffff909216600483015260248201526001600160a01b0390911690637c1e148790604401606060405180830381865afa158015613020573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130449190614688565b515b9050600081600f0b13151560008460400151600f0b1315150361306f57600060408401526130d3565b60008360400151600f0b13156130a35761309683604001518261309190614187565b61384d565b600f0b60408401526130d3565b60008360400151600f0b12156130d3576130ca8360400151826130c590614187565b61386b565b600f0b60408401525b505b60008260200151600f0b1380156130f257506040820151600f0b15155b801561310857506131068260600151613880565b155b925050505b95945050505050565b6000808061313361312b600f87900b8861339f565b600f0b6138b1565b90506000846000015160400151600f0b131561320157836000015160200151600f0b81600f0b1315604051806040016040528060048152602001634f43424d60e01b815250906131965760405162461bcd60e51b815260040161054d919061411c565b50600086600f0b1280156131c057506131ae86614187565b600f0b846000015160400151600f0b12155b604051806040016040528060048152602001634f43424d60e01b815250906131fb5760405162461bcd60e51b815260040161054d919061411c565b506132b5565b836000015160200151600f0b81600f0b1215604051806040016040528060048152602001634f43424d60e01b8152509061324e5760405162461bcd60e51b815260040161054d919061411c565b50600086600f0b138015613278575061326686614187565b600f0b846000015160400151600f0b13155b604051806040016040528060048152602001634f43424d60e01b815250906132b35760405162461bcd60e51b815260040161054d919061411c565b505b600087604001516132ca5787602001516132cd565b87515b606089015160405163c7167cf560e01b815263ffffffff9091166004820152600f89810b602483015288900b604482015290915060009081906001600160a01b0384169063c7167cf59060640160408051808303816000875af1158015613338573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061335c91906141ad565b9150915081876000015160400181815161337691906146fa565b600f0b90525061338582614187565b61338e82614187565b955095505050505094509492505050565b600081600f0b600014156040518060400160405280600381526020016222212d60e91b815250906133e35760405162461bcd60e51b815260040161054d919061411c565b50600082600f0b670de0b6b3a7640000600f0b85600f0b0281612a3757612a376140e4565b60008060008560400151600f0b121561343857613431856040015185604001516130c590614187565b9150613469565b60008560400151600f0b131561345e576134318560400151856040015161309190614187565b5060009050806135d5565b604086015161347890836140fa565b6134829083614558565b915060006134a0856020015184600f0b612a2090919063ffffffff16565b90506134ab81614187565b915060006134d2896060015187600001518a876134c790614187565b866000806000612a9b565b809350819250505083876040018181516134ec9190614558565b600f0b9052506040860180518591906135069083906146fa565b600f0b90525087518651613525918b9161351f88614187565b86612ac0565b856000015185602001518a6060015163ffffffff167f7c57459d6f4f0fb2fc5b1e298c8c0eb238422944964aa1e249eaa78747f0cca9896020015189604001518b606001518c608001516000898d61357c90614187565b60408051600f98890b815296880b602088015267ffffffffffffffff9586169087015293909216606085015215156080840152830b60a0830152820b60c08201529087900b60e08201526101000160405180910390a450505b9550959350505050565b60008060001989016135f657506000905084612ab3565b600083156136b15785600f0b60000361363f57602089015161361b90600f0b86612a20565b61362590826146fa565b9050600087600f0b121561363f5761363c81614187565b90505b600061364d89600f0b6138b1565b905060008a6020015161366a8b8a61366591906146fa565b61391b565b6136749190614558565b9050613680818361384d565b9050600081600f0b13156136aa5761369d600f8a900b8284613936565b6136a790846146fa565b92505b50506136be565b6136bb87826146fa565b90505b60006136cb8b8d87611e5a565b6136dd90670de0b6b3a7640000614558565b905060008083600f0b136136fe576136f9600f84900b8361339f565b61370c565b61370c600f84900b83612a20565b9050600061371a8285614558565b9050808c60600181815161372e91906146fa565b600f0b9052508061373f818c614558565b95509550505050509850989650505050505050565b600054610100900460ff166137bf5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161054d565b61139933612e88565b600054610100900460ff166138335760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161054d565b815160209283012081519190920120606691909155606755565b600081600f0b83600f0b126138625781613864565b825b9392505050565b600081600f0b83600f0b136138625781613864565b600061388a6139fb565b6001600160801b0316826703ffffffffffffff1667ffffffffffffffff1611159050919050565b60408051808201909152600281526127a360f11b6020820152600090600f83900b6f7fffffffffffffffffffffffffffffff19036139025760405162461bcd60e51b815260040161054d919061411c565b50600082600f0b126139145781611f21565b5060000390565b60008082600f0b1261392d5781611f21565b611f2182614187565b600081600f0b600014156040518060400160405280600381526020016222212d60e91b8152509061397a5760405162461bcd60e51b815260040161054d919061411c565b50600082600f0b84600f0b86600f0b0281613997576139976140e4565b0590506f7fffffffffffffffffffffffffffffff1981128015906139c2575060016001607f1b038113155b6040518060400160405280600281526020016127a360f11b8152509061269f5760405162461bcd60e51b815260040161054d919061411c565b60655460408051632abf68dd60e11b815290516000926001600160a01b03169163557ed1ba9160048083019260209291908290030181865afa158015613a45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a699190614749565b905090565b63ffffffff811681146128fb57600080fd5b60008060408385031215613a9357600080fd5b823591506020830135613aa581613a6e565b809150509250929050565b600060808284031215613ac257600080fd5b50919050565b600060808284031215613ada57600080fd5b6138648383613ab0565b600060208284031215613af657600080fd5b813561386481613a6e565b6001600160a01b03811681146128fb57600080fd5b80600f0b81146128fb57600080fd5b60008060008060008060c08789031215613b3e57600080fd5b8635613b4981613a6e565b95506020870135613b5981613a6e565b94506040870135613b6981613b01565b93506060870135613b7981613b16565b92506080870135613b8981613b16565b915060a0870135613b9981613b16565b809150509295509295509295565b60008060408385031215613bba57600080fd5b8235613bc581613a6e565b91506020830135613aa581613a6e565b6020808252825182820181905260009190848201906040850190845b81811015613c165783516001600160a01b031683529284019291840191600101613bf1565b50909695505050505050565b600060208284031215613c3457600080fd5b5035919050565b60008060408385031215613c4e57600080fd5b8235613c5981613b01565b91506020830135613aa581613b01565b60008060408385031215613c7c57600080fd5b823567ffffffffffffffff811115613c9357600080fd5b613c9f85828601613ab0565b9250506020830135613aa581613b01565b600060208284031215613cc257600080fd5b813567ffffffffffffffff811115613cd957600080fd5b82016060818503121561386457600080fd5b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715613d2457613d24613ceb565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715613d5357613d53613ceb565b604052919050565b803567ffffffffffffffff81168114613d7357600080fd5b919050565b600060a08284031215613d8a57600080fd5b60405160a0810181811067ffffffffffffffff82111715613dad57613dad613ceb565b604052823581529050806020830135613dc581613b16565b60208201526040830135613dd881613b16565b6040820152613de960608401613d5b565b6060820152613dfa60808401613d5b565b60808201525092915050565b60008060c08385031215613e1957600080fd5b8235613e2481613a6e565b9150613e338460208501613d78565b90509250929050565b600080600060608486031215613e5157600080fd5b833592506020840135613e6381613a6e565b915060408401358015158114613e7857600080fd5b809150509250925092565b8035600781900b8114613d7357600080fd5b60008060008060808587031215613eab57600080fd5b8435613eb681613b01565b93506020850135613ec681613a6e565b9250613ed460408601613e83565b9150613ee260608601613e83565b905092959194509250565b600067ffffffffffffffff821115613f0757613f07613ceb565b5060051b60200190565b600082601f830112613f2257600080fd5b81356020613f37613f3283613eed565b613d2a565b82815260059290921b84018101918181019086841115613f5657600080fd5b8286015b84811015613f7a578035613f6d81613a6e565b8352918301918301613f5a565b509695505050505050565b60008060408385031215613f9857600080fd5b823567ffffffffffffffff80821115613fb057600080fd5b818501915085601f830112613fc457600080fd5b81356020613fd4613f3283613eed565b82815260059290921b84018101918181019089841115613ff357600080fd5b948201945b8386101561401a57853561400b81613b01565b82529482019490820190613ff8565b9650508601359250508082111561403057600080fd5b5061403d85828601613f11565b9150509250929050565b602080825282518282018190526000919060409081850190868401855b8281101561409d5781518051600790810b865287820151900b8786015285015160ff168585015260609093019290850190600101614064565b5091979650505050505050565b6000602082840312156140bc57600080fd5b813561386481613b01565b6000602082840312156140d957600080fd5b813561386481613b16565b634e487b7160e01b600052601260045260246000fd5b600082600f0b8061410d5761410d6140e4565b8083600f0b0791505092915050565b600060208083528351808285015260005b818110156141495785810183015185820160400152820161412d565b8181111561415b576000604083870101525b50601f01601f1916929092016040019392505050565b634e487b7160e01b600052601160045260246000fd5b600081600f0b60016001607f1b031981036141a4576141a4614171565b60000392915050565b600080604083850312156141c057600080fd5b82516141cb81613b16565b6020840151909250613aa581613b16565b634e487b7160e01b600052602160045260246000fd5b8281526040810160038310614209576142096141dc565b8260208301529392505050565b60006020828403121561422857600080fd5b815161386481613b16565b600081600f0b83600f0b60016001607f1b0360008213600084138383048511828216161561426357614263614171565b6f7fffffffffffffffffffffffffffffff19600085128281168783058712161561428f5761428f614171565b600087129250858205871284841616156142ab576142ab614171565b858505871281841616156142c1576142c1614171565b5050509290910295945050505050565b600081600f0b83600f0b806142e8576142e86140e4565b6f7fffffffffffffffffffffffffffffff1982146000198214161561430f5761430f614171565b90059392505050565b600063ffffffff80831681851680830382111561433757614337614171565b01949350505050565b600063ffffffff8381169083168181101561435d5761435d614171565b039392505050565b634e487b7160e01b600052603260045260246000fd5b600063ffffffff80831681810361439457614394614171565b6001019392505050565b60208101600283106143b2576143b26141dc565b91905290565b6000602082840312156143ca57600080fd5b815161386481613b01565b600060208083850312156143e857600080fd5b825167ffffffffffffffff8111156143ff57600080fd5b8301601f8101851361441057600080fd5b805161441e613f3282613eed565b81815260059190911b8201830190838101908783111561443d57600080fd5b928401925b8284101561446457835161445581613a6e565b82529284019290840190614442565b979650505050505050565b6000823560be1983360301811261448557600080fd5b9190910192915050565b600060a082840312156144a157600080fd5b6138648383613d78565b600060c082360312156144bd57600080fd5b6144c5613d01565b6144cf3684613d78565b815260a083013567ffffffffffffffff808211156144ec57600080fd5b9084019036601f8301126144ff57600080fd5b813560208282111561451357614513613ceb565b614525601f8301601f19168201613d2a565b9250818352368183860101111561453b57600080fd5b818185018285013760009183018101919091528301525092915050565b600081600f0b83600f0b600081128160016001607f1b03190183128115161561458357614583614171565b8160016001607f1b0301831381161561459e5761459e614171565b5090039392505050565b60008235605e1983360301811261448557600080fd5b6000602082840312156145d057600080fd5b61386482613d5b565b60008160001904831182151516156145f3576145f3614171565b500290565b6000821982111561460b5761460b614171565b500190565b60006001820161462257614622614171565b5060010190565b60006040828403121561463b57600080fd5b6040516040810181811067ffffffffffffffff8211171561465e5761465e613ceb565b604052825161466c81613b16565b8152602083015161467c81613b16565b60208201529392505050565b60006060828403121561469a57600080fd5b6040516060810181811067ffffffffffffffff821117156146bd576146bd613ceb565b60405282516146cb81613b16565b815260208301516146db81613b16565b602082015260408301516146ee81613b16565b60408201529392505050565b600081600f0b83600f0b600082128260016001607f1b030382138115161561472457614724614171565b8260016001607f1b031903821281161561474057614740614171565b50019392505050565b60006020828403121561475b57600080fd5b81516001600160801b038116811461386457600080fdfe4f7264657228627974657333322073656e6465722c696e743132382070726963655831382c696e7431323820616d6f756e742c75696e7436342065787069726174696f6e2c75696e743634206e6f6e636529a264697066735822122021c143a1fd67561bb17abafc2a8a724f3aa356c5e82ae9d5a21ef14480331cbd64736f6c634300080d0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

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