ETH Price: $2,955.00 (-0.33%)

Token

GSP_2e50e3e1 (GLP)

Overview

Max Total Supply

79.603154 GLP

Holders

586

Market

Price

$0.00 @ 0.000000 ETH

Onchain Market Cap

-

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 6 Decimals)

Balance
0.613737 GLP

Value
$0.00
0x5b8a42fc0c151aa7f4f764b805abea1fc3516921
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information

Contract Source Code Verified (Exact Match)

Contract Name:
GSP

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
/*

    Copyright 2020 DODO ZOO.
    SPDX-License-Identifier: Apache-2.0

*/

pragma solidity 0.8.16;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {GSPTrader} from "./GSPTrader.sol";
import {GSPFunding} from "./GSPFunding.sol";
import {GSPVault} from "./GSPVault.sol";

/**
 * @title DODO GasSavingPool
 * @author DODO Breeder
 *
 * @notice DODO GasSavingPool initialization
 */
contract GSP is GSPTrader, GSPFunding {
    /**
     * @notice Function will be called in factory, init risk should not be included.
     * @param maintainer The dodo's address, who can claim mtFee and own this pool
     * @param admin oracle owner address, who can set price.
     * @param baseTokenAddress The base token address
     * @param quoteTokenAddress The quote token address
     * @param lpFeeRate The rate of lp fee, with 18 decimal
     * @param mtFeeRate The rate of mt fee, with 18 decimal
     * @param i The oracle price, possible to be changed only by maintainer
     * @param k The swap curve parameter
     * @param isOpenTWAP Useless, always false, just for compatible with old version pool
     */
    function init(
        address maintainer,
        address admin,
        address baseTokenAddress,
        address quoteTokenAddress,
        uint256 lpFeeRate,
        uint256 mtFeeRate,
        uint256 i,
        uint256 k,
        bool isOpenTWAP
    ) external {
        // GSP can only be initialized once
        require(!_GSP_INITIALIZED_, "GSP_INITIALIZED");
        // _GSP_INITIALIZED_ is set to true after initialization
        _GSP_INITIALIZED_ = true;
        // baseTokenAddress and quoteTokenAddress should not be the same
        require(baseTokenAddress != quoteTokenAddress, "BASE_QUOTE_CAN_NOT_BE_SAME");
        // _BASE_TOKEN_ and _QUOTE_TOKEN_ should be valid ERC20 tokens
        _BASE_TOKEN_ = IERC20(baseTokenAddress);
        _QUOTE_TOKEN_ = IERC20(quoteTokenAddress);

        // i should be greater than 0 and less than 10**36
        require(i > 0 && i <= 10**36);
        _I_ = i;
        // k should be greater than 0 and less than 10**18
        require(k <= 10**18);
        _K_ = k;

        // _LP_FEE_RATE_ is set when initialization
        _LP_FEE_RATE_ = lpFeeRate;
        // _MT_FEE_RATE_ is set when initialization
        _MT_FEE_RATE_ = mtFeeRate;
        // _MAINTAINER_ is set when initialization, the address receives the fee
        _MAINTAINER_ = maintainer;
        _ADMIN_ = admin;
        // _IS_OPEN_TWAP_ is always false
        _IS_OPEN_TWAP_ = false;


        string memory connect = "_";
        string memory suffix = "GSP";
        // name of the shares is the combination of suffix, connect and string of the GSP
        name = string(abi.encodePacked(suffix, connect, addressToShortString(address(this))));
        // symbol of the shares is GLP
        symbol = "GLP";
        // decimals of the shares is the same as the base token decimals
        decimals = IERC20Metadata(baseTokenAddress).decimals();
        // initialize DOMAIN_SEPARATOR
        buildDomainSeparator();
        // ==========================================================================
    }

    // ============================== Permit ====================================
    /**
     * @notice DOMAIN_SEPARATOR is used for approve by signature
     */
    function buildDomainSeparator() public returns (bytes32){
        string memory connect = "_";
        string memory suffix = "GSP";
        // name of the shares is the combination of suffix, connect and string of the GSP
        string memory name = string(abi.encodePacked(suffix, connect, addressToShortString(address(this))));

        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                // keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
                keccak256(bytes(name)),
                keccak256(bytes("1")),
                block.chainid,
                address(this)
            )
        );
        return DOMAIN_SEPARATOR;
    }

    /**
     * @notice Convert the address to a shorter string
     * @param _addr The address to convert
     * @return A string representation of _addr in hexadecimal
     */
    function addressToShortString(address _addr) public pure returns (string memory) {
        bytes32 value = bytes32(uint256(uint160(_addr)));
        bytes memory alphabet = "0123456789abcdef";

        bytes memory str = new bytes(8);
        for (uint256 i = 0; i < 4; i++) {
            str[i * 2] = alphabet[uint8(value[i + 12] >> 4)];
            str[1 + i * 2] = alphabet[uint8(value[i + 12] & 0x0f)];
        }
        return string(str);
    }

    // ============ Version Control ============
    /**
     * @notice Return the version of DODOGasSavingPool
     * @return The current version is 1.0.1
     */
    function version() external pure returns (string memory) {
        return "GSP 1.0.1";
    }
}

/*

    Copyright 2020 DODO ZOO.
    SPDX-License-Identifier: Apache-2.0

*/

pragma solidity 0.8.16;

import {DecimalMath} from "../../lib/DecimalMath.sol";
import {PMMPricing} from "../../lib/PMMPricing.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {GSPStorage} from "./GSPStorage.sol";

contract GSPVault is GSPStorage {
    using SafeERC20 for IERC20;

    // ============ Modifiers ============
    /// @notice Check whether the caller is maintainer
    modifier onlyMaintainer() {
        require(msg.sender == _MAINTAINER_, "ACCESS_DENIED");
        _;
    }

    /// @notice Check whether the caller is admin
    modifier onlyAdmin() {
        require(msg.sender == _ADMIN_, "ADMIN_ACCESS_DENIED");
        _;
    }

    // ============ Events ============

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    event Mint(address indexed user, uint256 value);

    event Burn(address indexed user, uint256 value);

    event MtFeeRateChange(uint256 newMtFee);

    event IChange(uint256 newI);

    event WithdrawMtFee(address indexed token, uint256 amount);

    // ============ View Functions ============
    /**
     * @notice Get the reserves of the pool
     * @return baseReserve The base token reserve
     * @return quoteReserve The quote token reserve
     */
    function getVaultReserve() external view returns (uint256 baseReserve, uint256 quoteReserve) {
        baseReserve = _BASE_RESERVE_;
        quoteReserve = _QUOTE_RESERVE_;
    }

    /**
     * @notice Get the fee rate of the pool
     * @param user Useless, just keep the same interface with old version pool
     * @return lpFeeRate The lp fee rate
     * @return mtFeeRate The mt fee rate
     */
    function getUserFeeRate(address user) 
        external 
        view 
        returns (uint256 lpFeeRate, uint256 mtFeeRate) 
    {
        lpFeeRate = _LP_FEE_RATE_;
        mtFeeRate = _MT_FEE_RATE_;
    }

    // ============ Asset In ============
    /**
     * @notice Get the amount of base token transferred in
     * @dev The amount of base token input should be the base token reserve minus the mt fee in base token
     * @return input The amount of base token transferred in
     */
    function getBaseInput() public view returns (uint256 input) {
        return _BASE_TOKEN_.balanceOf(address(this)) - uint256(_BASE_RESERVE_) - uint256(_MT_FEE_BASE_);
    }

    /**
     * @notice Get the amount of quote token transferred in
     * @dev The amount of quote token input should be the quote token reserve minus the mt fee in quote token
     * @return input The amount of quote token transferred in
     */
    function getQuoteInput() public view returns (uint256 input) {
        return _QUOTE_TOKEN_.balanceOf(address(this)) - uint256(_QUOTE_RESERVE_) - uint256(_MT_FEE_QUOTE_);
    }

    // ============ Set States ============
    /**
     * @notice Set the reserves of the pool, internal use only
     * @param baseReserve The base token reserve
     * @param quoteReserve The quote token reserve
     */
    function _setReserve(uint256 baseReserve, uint256 quoteReserve) internal {
        // the reserves should be less than the max uint112
        require(baseReserve <= type(uint112).max && quoteReserve <= type(uint112).max, "OVERFLOW");
        _BASE_RESERVE_ = uint112(baseReserve);
        _QUOTE_RESERVE_ = uint112(quoteReserve);
    }

    /**
     * @notice Sync the reserves of the pool, internal use only
     * @dev The balances of the pool should be actual balances minus the mt fee
     */
    function _sync() internal {
        uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)) - uint256(_MT_FEE_BASE_);
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)) - uint256(_MT_FEE_QUOTE_);
        // the reserves should be less than the max uint112
        require(baseBalance <= type(uint112).max && quoteBalance <= type(uint112).max, "OVERFLOW");
        if (baseBalance != _BASE_RESERVE_) {
            _BASE_RESERVE_ = uint112(baseBalance);
        }
        if (quoteBalance != _QUOTE_RESERVE_) {
            _QUOTE_RESERVE_ = uint112(quoteBalance);
        }
    }

    /// @notice Sync the reserves of the pool
    function sync() external nonReentrant {
        _sync();
    }

    /// @notice Correct the rState of the pool, details in pmm algorithm
    function correctRState() public {
        if (_RState_ == uint32(PMMPricing.RState.BELOW_ONE) && _BASE_RESERVE_<_BASE_TARGET_) {
          _RState_ = uint32(PMMPricing.RState.ONE);
          _BASE_TARGET_ = _BASE_RESERVE_;
          _QUOTE_TARGET_ = _QUOTE_RESERVE_;
        }
        if (_RState_ == uint32(PMMPricing.RState.ABOVE_ONE) && _QUOTE_RESERVE_<_QUOTE_TARGET_) {
          _RState_ = uint32(PMMPricing.RState.ONE);
          _BASE_TARGET_ = _BASE_RESERVE_;
          _QUOTE_TARGET_ = _QUOTE_RESERVE_;
        }
    }

    /**
     * @notice PriceLimit is used for oracle change protection
     * @notice It sets a ratio where the relative deviation between the new price and the old price cannot exceed this ratio.
     * @dev The default priceLimit is 1e3, the decimals of priceLimit is 1e6
     * @param priceLimit The new price limit
     */
    function adjustPriceLimit(uint256 priceLimit) external onlyAdmin {
        // the default priceLimit is 1e3
        require(priceLimit <= 1e6, "INVALID_PRICE_LIMIT");
        _PRICE_LIMIT_ = priceLimit;
    }

    /**
     * @notice Adjust oricle price i, only for admin
     */
    function adjustPrice(uint256 i) external onlyAdmin {
        // the difference between i and _I_ should be less than priceLimit
        uint256 offset = i > _I_ ? i - _I_ : _I_ - i;
        require((offset * 1e6 / _I_) <= _PRICE_LIMIT_, "EXCEED_PRICE_LIMIT");
        _I_ = i;
        
        emit IChange(i);
    }

    /**
     * @notice Adjust mtFee rate, only for maintainer
     * @dev The decimals of mtFee rate is 1e18
     * @param mtFeeRate The new mtFee rate
     */
    function adjustMtFeeRate(uint256 mtFeeRate) external onlyMaintainer {
        require(mtFeeRate <= 10**18, "INVALID_MT_FEE_RATE");
        _MT_FEE_RATE_ = mtFeeRate;

        emit MtFeeRateChange(mtFeeRate);
    }

    // ============ Asset Out ============
    /**
     * @notice Transfer base token out, internal use only
     * @param to The address of the receiver
     * @param amount The amount of base token to transfer out
     */
    function _transferBaseOut(address to, uint256 amount) internal {
        if (amount > 0) {
            _BASE_TOKEN_.safeTransfer(to, amount);
        }
    }

    /**
     * @notice Transfer quote token out, internal use only
     * @param to The address of the receiver
     * @param amount The amount of quote token to transfer out
     */
    function _transferQuoteOut(address to, uint256 amount) internal {
        if (amount > 0) {
            _QUOTE_TOKEN_.safeTransfer(to, amount);
        }
    }

    /// @notice Maintainer withdraw mtFee, only for maintainer
    function withdrawMtFeeTotal() external nonReentrant onlyMaintainer {
        uint256 mtFeeQuote = _MT_FEE_QUOTE_;
        uint256 mtFeeBase = _MT_FEE_BASE_;
        _MT_FEE_QUOTE_ = 0;
        _transferQuoteOut(_MAINTAINER_, mtFeeQuote);
        _MT_FEE_BASE_ = 0;
        _transferBaseOut(_MAINTAINER_, mtFeeBase);

        emit WithdrawMtFee(address(_QUOTE_TOKEN_), mtFeeQuote);
        emit WithdrawMtFee(address(_BASE_TOKEN_), mtFeeBase);
    }

    // ============ Shares (ERC20) ============

    /**
     * @dev Transfer token for a specified address
     * @param to The address to transfer to.
     * @param amount The amount to be transferred.
     */
    function transfer(address to, uint256 amount) public returns (bool) {
        require(amount <= _SHARES_[msg.sender], "BALANCE_NOT_ENOUGH");

        _SHARES_[msg.sender] = _SHARES_[msg.sender] - (amount);
        _SHARES_[to] = _SHARES_[to] + amount;
        emit Transfer(msg.sender, to, amount);
        return true;
    }

    /**
     * @dev Gets the balance of the specified address.
     * @param owner The address to query the the balance of.
     * @return balance An uint256 representing the amount owned by the passed address.
     */
    function balanceOf(address owner) external view returns (uint256 balance) {
        return _SHARES_[owner];
    }

    /**
     * @dev Transfer tokens from one address to another
     * @param from address The address which you want to send tokens from
     * @param to address The address which you want to transfer to
     * @param amount uint256 the amount of tokens to be transferred
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public returns (bool) {
        require(amount <= _SHARES_[from], "BALANCE_NOT_ENOUGH");
        require(amount <= _ALLOWED_[from][msg.sender], "ALLOWANCE_NOT_ENOUGH");

        _SHARES_[from] = _SHARES_[from] - amount;
        _SHARES_[to] = _SHARES_[to] + amount;
        _ALLOWED_[from][msg.sender] = _ALLOWED_[from][msg.sender] - amount;
        emit Transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
     * @param spender The address which will spend the funds.
     * @param amount The amount of tokens to be spent.
     */
    function approve(address spender, uint256 amount) public returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) private {
        _ALLOWED_[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Function to check the amount of tokens that an owner _ALLOWED_ to a spender.
     * @param owner address The address which owns the funds.
     * @param spender address The address which will spend the funds.
     * @return A uint256 specifying the amount of tokens still available for the spender.
     */
    function allowance(address owner, address spender) public view returns (uint256) {
        return _ALLOWED_[owner][spender];
    }

    function _mint(address user, uint256 value) internal {
        require(value > 1000, "MINT_AMOUNT_NOT_ENOUGH");
        _SHARES_[user] = _SHARES_[user] + value;
        totalSupply = totalSupply + value;
        emit Mint(user, value);
        emit Transfer(address(0), user, value);
    }

    function _burn(address user, uint256 value) internal {
        _SHARES_[user] = _SHARES_[user] - value;
        totalSupply = totalSupply - value;
        emit Burn(user, value);
        emit Transfer(user, address(0), value);
    }

    // ============================ Permit ======================================

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        require(deadline >= block.timestamp, "DODO_GSP_LP: EXPIRED");
        bytes32 digest =
            keccak256(
                abi.encodePacked(
                    "\x19\x01",
                    DOMAIN_SEPARATOR,
                    keccak256(
                        abi.encode(
                            PERMIT_TYPEHASH,
                            owner,
                            spender,
                            value,
                            nonces[owner]++,
                            deadline
                        )
                    )
                )
            );

        address recoveredAddress = ecrecover(digest, v, r, s);
        require(
            recoveredAddress != address(0) && recoveredAddress == owner,
            "DODO_GSP_LP: INVALID_SIGNATURE"
        );
        _approve(owner, spender, value);
    }
}

/*

    Copyright 2020 DODO ZOO.
    SPDX-License-Identifier: Apache-2.0

*/


pragma solidity 0.8.16;

import {GSPVault} from "./GSPVault.sol";
import {DecimalMath} from "../../lib/DecimalMath.sol";
import {IDODOCallee} from "../../intf/IDODOCallee.sol";

/// @notice this part focus on Lp tokens, mint and burn
contract GSPFunding is GSPVault {
    // ============ Events ============

    event BuyShares(address to, uint256 increaseShares, uint256 totalShares);

    event SellShares(address payer, address to, uint256 decreaseShares, uint256 totalShares);

    // ============ Buy & Sell Shares ============
    
    /// @notice User mint Lp token and deposit tokens, the result is rounded down
    /// @dev User first transfer baseToken and quoteToken to GSP, then call buyShares
    /// @param to The address will receive shares
    /// @return shares The amount of shares user will receive
    /// @return baseInput The amount of baseToken user transfer to GSP
    /// @return quoteInput The amount of quoteToken user transfer to GSP
    function buyShares(address to)
        external
        nonReentrant
        returns (
            uint256 shares,
            uint256 baseInput,
            uint256 quoteInput
        )
    {
        // The balance of baseToken and quoteToken should be the balance minus the fee
        uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)) - _MT_FEE_BASE_;
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)) - _MT_FEE_QUOTE_;
        // The reserve of baseToken and quoteToken
        uint256 baseReserve = _BASE_RESERVE_;
        uint256 quoteReserve = _QUOTE_RESERVE_;

        // The amount of baseToken and quoteToken user transfer to GSP
        baseInput = baseBalance - baseReserve;
        quoteInput = quoteBalance - quoteReserve;

        // BaseToken should be transferred to GSP before calling buyShares
        require(baseInput > 0, "NO_BASE_INPUT");

        // Round down when withdrawing. Therefore, never be a situation occuring balance is 0 but totalsupply is not 0
        // But May Happen,reserve >0 But totalSupply = 0
        if (totalSupply == 0) {
            // case 1. initial supply
            require(quoteBalance > 0, "ZERO_QUOTE_AMOUNT");
            // The shares will be minted to user
            shares = quoteBalance < DecimalMath.mulFloor(baseBalance, _I_)
                ? DecimalMath.divFloor(quoteBalance, _I_)
                : baseBalance;
            // The target will be updated
            _BASE_TARGET_ = uint112(shares);
            _QUOTE_TARGET_ = uint112(DecimalMath.mulFloor(shares, _I_));
            require(_QUOTE_TARGET_ > 0, "QUOTE_TARGET_IS_ZERO");
            // Lock 1001 shares permanently in first deposit 
            require(shares > 2001, "MINT_AMOUNT_NOT_ENOUGH");
            _mint(address(0), 1001);
            shares -= 1001;
        } else if (baseReserve > 0 && quoteReserve > 0) {
            // case 2. normal case
            uint256 baseInputRatio = DecimalMath.divFloor(baseInput, baseReserve);
            uint256 quoteInputRatio = DecimalMath.divFloor(quoteInput, quoteReserve);
            uint256 mintRatio = quoteInputRatio < baseInputRatio ? quoteInputRatio : baseInputRatio;
            // The shares will be minted to user
            shares = DecimalMath.mulFloor(totalSupply, mintRatio);

            // The target will be updated
            _BASE_TARGET_ = uint112(uint256(_BASE_TARGET_) + (DecimalMath.mulFloor(uint256(_BASE_TARGET_), mintRatio)));
            _QUOTE_TARGET_ = uint112(uint256(_QUOTE_TARGET_) + (DecimalMath.mulFloor(uint256(_QUOTE_TARGET_), mintRatio)));
        }
        // The shares will be minted to user
        // The reserve will be updated
        _mint(to, shares);
        _setReserve(baseBalance, quoteBalance);
        emit BuyShares(to, shares, _SHARES_[to]);
    }

    /// @notice User burn their lp and withdraw their tokens, the result is rounded down
    /// @dev User call sellShares, the calculated baseToken and quoteToken amount should geater than minBaseToken and minQuoteToken
    /// @param shareAmount The amount of shares user want to sell
    /// @param to The address will receive baseToken and quoteToken
    /// @param baseMinAmount The minimum amount of baseToken user want to receive
    /// @param quoteMinAmount The minimum amount of quoteToken user want to receive
    /// @param data The data will be passed to callee contract
    /// @param deadline The deadline of this transaction
    function sellShares(
        uint256 shareAmount,
        address to,
        uint256 baseMinAmount,
        uint256 quoteMinAmount,
        bytes calldata data,
        uint256 deadline
    ) external nonReentrant returns (uint256 baseAmount, uint256 quoteAmount) {
        // The deadline should be greater than current timestamp
        require(deadline >= block.timestamp, "TIME_EXPIRED");
        // The amount of shares user want to sell should be less than user's balance
        require(shareAmount <= _SHARES_[msg.sender], "GLP_NOT_ENOUGH");

        // The balance of baseToken and quoteToken should be the balance minus the fee
        uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)) - _MT_FEE_BASE_;
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)) - _MT_FEE_QUOTE_;
        // The total shares of GSP
        uint256 totalShares = totalSupply;

        // The amount of baseToken and quoteToken user will receive is calculated by the ratio of user's shares to total shares
        baseAmount = baseBalance * shareAmount / totalShares;
        quoteAmount = quoteBalance * shareAmount / totalShares;
        
        // The target will be updated
        _BASE_TARGET_ = uint112(uint256(_BASE_TARGET_) - DecimalMath._divCeil((uint256(_BASE_TARGET_) * (shareAmount)), totalShares));
        _QUOTE_TARGET_ = uint112(uint256(_QUOTE_TARGET_) - DecimalMath._divCeil((uint256(_QUOTE_TARGET_) * (shareAmount)), totalShares));
        
        // The calculated baseToken and quoteToken amount should geater than minBaseToken and minQuoteToken
        require(
            baseAmount >= baseMinAmount && quoteAmount >= quoteMinAmount,
            "WITHDRAW_NOT_ENOUGH"
        );

        // The shares will be burned from user
        // The baseToken and quoteToken will be transferred to user
        // The reserve will be synced
        _burn(msg.sender, shareAmount);
        _transferBaseOut(to, baseAmount);
        _transferQuoteOut(to, quoteAmount);
        _sync();

        // If the data is not empty, the callee contract will be called
        if (data.length > 0) {
            //Same as DVM 
            IDODOCallee(to).DVMSellShareCall(
                msg.sender,
                shareAmount,
                baseAmount,
                quoteAmount,
                data
            );
        }

        emit SellShares(msg.sender, to, shareAmount, _SHARES_[msg.sender]);
    }
}

/*

    Copyright 2020 DODO ZOO.
    SPDX-License-Identifier: Apache-2.0

*/

pragma solidity 0.8.16;

import {GSPVault} from "./GSPVault.sol";
import {DecimalMath} from "../../lib/DecimalMath.sol";
import {PMMPricing} from "../../lib/PMMPricing.sol";
import {IDODOCallee} from "../../intf/IDODOCallee.sol";

/// @notice this contract deal with swap
contract GSPTrader is GSPVault {

    // ============ Events ============

    event DODOSwap(
        address fromToken,
        address toToken,
        uint256 fromAmount,
        uint256 toAmount,
        address trader,
        address receiver
    );

    event DODOFlashLoan(address borrower, address assetTo, uint256 baseAmount, uint256 quoteAmount);

    event RChange(PMMPricing.RState newRState);

    // ============ Trade Functions ============
    /**
     * @notice User sell base tokens, user pay tokens first. Must be used with a router
     * @dev The base token balance is the actual balance minus the mt fee
     * @param to The recipient of the output
     * @return receiveQuoteAmount Amount of quote token received
     */
    function sellBase(address to) external nonReentrant returns (uint256 receiveQuoteAmount) {
        uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)) - _MT_FEE_BASE_;
        uint256 baseInput = baseBalance - uint256(_BASE_RESERVE_);
        uint256 mtFee;
        uint256 newBaseTarget;
        PMMPricing.RState newRState;
        // calculate the amount of quote token to receive and mt fee
        (receiveQuoteAmount, mtFee, newRState, newBaseTarget) = querySellBase(tx.origin, baseInput);
        // transfer quote token to recipient
        _transferQuoteOut(to, receiveQuoteAmount);
        // update mt fee in quote token
        _MT_FEE_QUOTE_ = _MT_FEE_QUOTE_ + mtFee;
        

        // update TARGET
        if (_RState_ != uint32(newRState)) {    
            require(newBaseTarget <= type(uint112).max, "OVERFLOW");
            _BASE_TARGET_ = uint112(newBaseTarget);
            _RState_ = uint32(newRState);
            emit RChange(newRState);
        }
        // update reserve
        _setReserve(baseBalance, _QUOTE_TOKEN_.balanceOf(address(this)) - _MT_FEE_QUOTE_);

        emit DODOSwap(
            address(_BASE_TOKEN_),
            address(_QUOTE_TOKEN_),
            baseInput,
            receiveQuoteAmount,
            msg.sender,
            to
        );
    }

    /**
     * @notice User sell quote tokens, user pay tokens first. Must be used with a router
     * @param to The recipient of the output
     * @return receiveBaseAmount Amount of base token received
     */
    function sellQuote(address to) external nonReentrant returns (uint256 receiveBaseAmount) {
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)) - _MT_FEE_QUOTE_;
        uint256 quoteInput = quoteBalance - uint256(_QUOTE_RESERVE_);
        uint256 mtFee;
        uint256 newQuoteTarget;
        PMMPricing.RState newRState;
        // calculate the amount of base token to receive and mt fee
        (receiveBaseAmount, mtFee, newRState, newQuoteTarget) = querySellQuote(
            tx.origin,
            quoteInput
        );
        // transfer base token to recipient
        _transferBaseOut(to, receiveBaseAmount);
        // update mt fee in base token
        _MT_FEE_BASE_ = _MT_FEE_BASE_ + mtFee;

        // update TARGET
        if (_RState_ != uint32(newRState)) {
            require(newQuoteTarget <= type(uint112).max, "OVERFLOW");
            _QUOTE_TARGET_ = uint112(newQuoteTarget);
            _RState_ = uint32(newRState);
            emit RChange(newRState);
        }
        // update reserve
        _setReserve((_BASE_TOKEN_.balanceOf(address(this)) - _MT_FEE_BASE_), quoteBalance);

        emit DODOSwap(
            address(_QUOTE_TOKEN_),
            address(_BASE_TOKEN_),
            quoteInput,
            receiveBaseAmount,
            msg.sender,
            to
        );
    }

    /**
     * @notice inner flashloan, pay tokens out first, call external contract and check tokens left
     * @param baseAmount The base token amount user require
     * @param quoteAmount The quote token amount user require
     * @param assetTo The address who uses above tokens
     * @param data The external contract's callData
     */
    function flashLoan(
        uint256 baseAmount,
        uint256 quoteAmount,
        address assetTo,
        bytes calldata data
    ) external nonReentrant {
        _transferBaseOut(assetTo, baseAmount);
        _transferQuoteOut(assetTo, quoteAmount);

        if (data.length > 0)
            IDODOCallee(assetTo).DSPFlashLoanCall(msg.sender, baseAmount, quoteAmount, data);

        uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)) - _MT_FEE_BASE_;
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)) - _MT_FEE_QUOTE_;

        // no input -> pure loss
        require(
            baseBalance >= _BASE_RESERVE_ || quoteBalance >= _QUOTE_RESERVE_,
            "FLASH_LOAN_FAILED"
        );

        // sell quote case
        // quote input + base output
        if (baseBalance < _BASE_RESERVE_) {
            uint256 quoteInput = quoteBalance - uint256(_QUOTE_RESERVE_);
            (
                uint256 receiveBaseAmount,
                uint256 mtFee,
                PMMPricing.RState newRState,
                uint256 newQuoteTarget
            ) = querySellQuote(tx.origin, quoteInput); // revert if quoteBalance<quoteReserve
            require(
                (uint256(_BASE_RESERVE_) - baseBalance) <= receiveBaseAmount,
                "FLASH_LOAN_FAILED"
            );
            
            _MT_FEE_BASE_ = _MT_FEE_BASE_ + mtFee;
            
            if (_RState_ != uint32(newRState)) {
                require(newQuoteTarget <= type(uint112).max, "OVERFLOW");
                _QUOTE_TARGET_ = uint112(newQuoteTarget);
                _RState_ = uint32(newRState);
                emit RChange(newRState);
            }
            emit DODOSwap(
                address(_QUOTE_TOKEN_),
                address(_BASE_TOKEN_),
                quoteInput,
                receiveBaseAmount,
                msg.sender,
                assetTo
            );
        }

        // sell base case
        // base input + quote output
        if (quoteBalance < _QUOTE_RESERVE_) {
            uint256 baseInput = baseBalance - uint256(_BASE_RESERVE_);
            (
                uint256 receiveQuoteAmount,
                uint256 mtFee,
                PMMPricing.RState newRState,
                uint256 newBaseTarget
            ) = querySellBase(tx.origin, baseInput); // revert if baseBalance<baseReserve
            require(
                (uint256(_QUOTE_RESERVE_) - quoteBalance) <= receiveQuoteAmount,
                "FLASH_LOAN_FAILED"
            );

            _MT_FEE_QUOTE_ = _MT_FEE_QUOTE_ + mtFee;
            
            if (_RState_ != uint32(newRState)) {
                require(newBaseTarget <= type(uint112).max, "OVERFLOW");
                _BASE_TARGET_ = uint112(newBaseTarget);
                _RState_ = uint32(newRState);
                emit RChange(newRState);
            }
            emit DODOSwap(
                address(_BASE_TOKEN_),
                address(_QUOTE_TOKEN_),
                baseInput,
                receiveQuoteAmount,
                msg.sender,
                assetTo
            );
        }

        _sync();

        emit DODOFlashLoan(msg.sender, assetTo, baseAmount, quoteAmount);
    }

    // ============ Query Functions ============
    /**
     * @notice Return swap result, for query, sellBase side. 
     * @param trader Useless, just to keep the same interface with old version pool
     * @param payBaseAmount The amount of base token user want to sell
     * @return receiveQuoteAmount The amount of quote token user will receive
     * @return mtFee The amount of mt fee charged
     * @return newRState The new RState after swap
     * @return newBaseTarget The new base target after swap
     */
    function querySellBase(address trader, uint256 payBaseAmount)
        public
        view
        returns (
            uint256 receiveQuoteAmount,
            uint256 mtFee,
            PMMPricing.RState newRState,
            uint256 newBaseTarget
        )
    {
        PMMPricing.PMMState memory state = getPMMState();
        (receiveQuoteAmount, newRState) = PMMPricing.sellBaseToken(state, payBaseAmount);

        uint256 lpFeeRate = _LP_FEE_RATE_;
        uint256 mtFeeRate = _MT_FEE_RATE_;
        mtFee = DecimalMath.mulFloor(receiveQuoteAmount, mtFeeRate);
        receiveQuoteAmount = receiveQuoteAmount
            - DecimalMath.mulFloor(receiveQuoteAmount, lpFeeRate)
            - mtFee;
        newBaseTarget = state.B0;
    }
    /**
     * @notice Return swap result, for query, sellQuote side
     * @param trader Useless, just for keeping the same interface with old version pool
     * @param payQuoteAmount The amount of quote token user want to sell
     * @return receiveBaseAmount The amount of base token user will receive
     * @return mtFee The amount of mt fee charged
     * @return newRState The new RState after swap
     * @return newQuoteTarget The new quote target after swap
     */
    function querySellQuote(address trader, uint256 payQuoteAmount)
        public
        view
        returns (
            uint256 receiveBaseAmount,
            uint256 mtFee,
            PMMPricing.RState newRState,
            uint256 newQuoteTarget
        )
    {
        PMMPricing.PMMState memory state = getPMMState();
        (receiveBaseAmount, newRState) = PMMPricing.sellQuoteToken(state, payQuoteAmount);

        uint256 lpFeeRate = _LP_FEE_RATE_;
        uint256 mtFeeRate = _MT_FEE_RATE_;
        mtFee = DecimalMath.mulFloor(receiveBaseAmount, mtFeeRate);
        receiveBaseAmount = receiveBaseAmount
            - DecimalMath.mulFloor(receiveBaseAmount, lpFeeRate)
            - mtFee;
        newQuoteTarget = state.Q0;
    }
}

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

/*

    Copyright 2020 DODO ZOO.
    SPDX-License-Identifier: Apache-2.0

*/

pragma solidity 0.8.16;

import {DODOMath} from "../../lib/DODOMath.sol";
import {DecimalMath} from "../../lib/DecimalMath.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {PMMPricing} from "../../lib/PMMPricing.sol";

/// @notice this contract is used for store state and read state
contract GSPStorage is ReentrancyGuard {

    // ============ Storage for Setup ============
    // _GSP_INITIALIZED_ will be set to true when the init function is called
    bool internal _GSP_INITIALIZED_;
    // GSP does not open TWAP by default
    // _IS_OPEN_TWAP_ can be set to true when the init function is called
    bool public _IS_OPEN_TWAP_ = false;
    
    // ============ Core Address ============
    // _MAINTAINER_ is the maintainer of GSP
    address public _MAINTAINER_;
    // _ADMIN_ can set price
    address public _ADMIN_;
    // _BASE_TOKEN_ and _QUOTE_TOKEN_ should be ERC20 token
    IERC20 public _BASE_TOKEN_;
    IERC20 public _QUOTE_TOKEN_;
    // _BASE_RESERVE_ and _QUOTE_RESERVE_ are the current reserves of the GSP
    uint112 public _BASE_RESERVE_;
    uint112 public _QUOTE_RESERVE_;
    // _BLOCK_TIMESTAMP_LAST_ is used when calculating TWAP
    uint32 public _BLOCK_TIMESTAMP_LAST_;
    // _BASE_PRICE_CUMULATIVE_LAST_ is used when calculating TWAP
    uint256 public _BASE_PRICE_CUMULATIVE_LAST_;

    // _BASE_TARGET_ and _QUOTE_TARGET_ are recalculated when the pool state changes
    uint112 public _BASE_TARGET_;
    uint112 public _QUOTE_TARGET_;
    // _RState_ is the current R state of the GSP
    uint32 public _RState_;

    // ============ Shares (ERC20) ============
    // symbol is the symbol of the shares
    string public symbol;
    // decimals is the decimals of the shares
    uint8 public decimals;
    // name is the name of the shares
    string public name;
    // totalSupply is the total supply of the shares
    uint256 public totalSupply;
    // _SHARES_ is the mapping from account to share balance, record the share balance of each account
    mapping(address => uint256) internal _SHARES_;
    mapping(address => mapping(address => uint256)) internal _ALLOWED_;

    // ================= Permit ======================

    bytes32 public DOMAIN_SEPARATOR;
    // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    bytes32 public constant PERMIT_TYPEHASH =
        0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
    mapping(address => uint256) public nonces;

    // ============ Variables for Pricing ============
    // _MT_FEE_RATE_ is the fee rate of mt fee
    uint256 public _MT_FEE_RATE_;
    // _LP_FEE_RATE_ is the fee rate of lp fee
    uint256 public _LP_FEE_RATE_;
    uint256 public _K_;
    uint256 public _I_;
    // _PRICE_LIMIT_ is 1/1000 by default, which is used to limit the setting range of I
    uint256 public _PRICE_LIMIT_ = 1e3;

    // ============ Mt Fee ============
    // _MT_FEE_BASE_ represents the mt fee in base token
    uint256 public _MT_FEE_BASE_;
    // _MT_FEE_QUOTE_ represents the mt fee in quote token
    uint256 public _MT_FEE_QUOTE_;
    // _MT_FEE_RATE_MODEL_ is useless, just for compatible with old version pool
    address public _MT_FEE_RATE_MODEL_ = address(0);

    // ============ Helper Functions ============

    /// @notice Return the PMM state of the pool from inner or outside
    /// @dev B0 and Q0 are calculated in adjustedTarget
    /// @return state The current PMM state
    function getPMMState() public view returns (PMMPricing.PMMState memory state) {
        state.i = _I_;
        state.K = _K_;
        state.B = _BASE_RESERVE_;
        state.Q = _QUOTE_RESERVE_;
        state.B0 = _BASE_TARGET_; // will be calculated in adjustedTarget
        state.Q0 = _QUOTE_TARGET_;
        state.R = PMMPricing.RState(_RState_);
        PMMPricing.adjustedTarget(state);
    }

    /// @notice Return the PMM state variables used for routeHelpers
    /// @return i The price index
    /// @return K The K value
    /// @return B The base token reserve
    /// @return Q The quote token reserve
    /// @return B0 The base token target
    /// @return Q0 The quote token target
    /// @return R The R state of the pool
    function getPMMStateForCall()
        external
        view
        returns (
            uint256 i,
            uint256 K,
            uint256 B,
            uint256 Q,
            uint256 B0,
            uint256 Q0,
            uint256 R
        )
    {
        PMMPricing.PMMState memory state = getPMMState();
        i = state.i;
        K = state.K;
        B = state.B;
        Q = state.Q;
        B0 = state.B0;
        Q0 = state.Q0;
        R = uint256(state.R);
    }

    /// @notice Return the adjusted mid price
    /// @return midPrice The current mid price
    function getMidPrice() public view returns (uint256 midPrice) {
        return PMMPricing.getMidPrice(getPMMState());
    }

    /// @notice Return the total mt fee maintainer can claim
    /// @dev The total mt fee is represented in two types: in base token and in quote token
    /// @return mtFeeBase The total mt fee in base token
    /// @return mtFeeQuote The total mt fee in quote token
    function getMtFeeTotal() public view returns (uint256 mtFeeBase, uint256 mtFeeQuote) {
        mtFeeBase = _MT_FEE_BASE_;
        mtFeeQuote = _MT_FEE_QUOTE_;
    }
}

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

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

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

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

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

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

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

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

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

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

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

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

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

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

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

/*

    Copyright 2020 DODO ZOO.
    SPDX-License-Identifier: Apache-2.0

*/

pragma solidity 0.8.16;
pragma experimental ABIEncoderV2;

import {DecimalMath} from "../lib/DecimalMath.sol";
import {DODOMath} from "../lib/DODOMath.sol";

/**
 * @title Pricing
 * @author DODO Breeder
 *
 * @notice DODO Pricing model
 */

library PMMPricing {

    enum RState {ONE, ABOVE_ONE, BELOW_ONE}

    struct PMMState {
        uint256 i;
        uint256 K;
        uint256 B;
        uint256 Q;
        uint256 B0;
        uint256 Q0;
        RState R;
    }

    // ============ buy & sell ============
    /**
     * @notice Inner calculation based on pmm algorithm, sell base
     * @param state The current PMM state
     * @param payBaseAmount The amount of base token user want to sell
     * @return receiveQuoteAmount The amount of quote token user will receive
     * @return newR The new R status after swap
     */
    function sellBaseToken(PMMState memory state, uint256 payBaseAmount)
        internal
        pure
        returns (uint256 receiveQuoteAmount, RState newR)
    {
        if (state.R == RState.ONE) {
            // case 1: R=1
            // R falls below one
            receiveQuoteAmount = _ROneSellBaseToken(state, payBaseAmount);
            newR = RState.BELOW_ONE;
        } else if (state.R == RState.ABOVE_ONE) {
            uint256 backToOnePayBase = state.B0 - state.B;
            uint256 backToOneReceiveQuote = state.Q - state.Q0;
            // case 2: R>1
            // complex case, R status depends on trading amount
            if (payBaseAmount < backToOnePayBase) {
                // case 2.1: R status do not change
                receiveQuoteAmount = _RAboveSellBaseToken(state, payBaseAmount);
                newR = RState.ABOVE_ONE;
                if (receiveQuoteAmount > backToOneReceiveQuote) {
                    // [Important corner case!] may enter this branch when some precision problem happens. And consequently contribute to negative spare quote amount
                    // to make sure spare quote>=0, mannually set receiveQuote=backToOneReceiveQuote
                    receiveQuoteAmount = backToOneReceiveQuote;
                }
            } else if (payBaseAmount == backToOnePayBase) {
                // case 2.2: R status changes to ONE
                receiveQuoteAmount = backToOneReceiveQuote;
                newR = RState.ONE;
            } else {
                // case 2.3: R status changes to BELOW_ONE
                receiveQuoteAmount = backToOneReceiveQuote + (
                    _ROneSellBaseToken(state, (payBaseAmount - backToOnePayBase))
                );
                newR = RState.BELOW_ONE;
            }
        } else {
            // state.R == RState.BELOW_ONE
            // case 3: R<1
            receiveQuoteAmount = _RBelowSellBaseToken(state, payBaseAmount);
            newR = RState.BELOW_ONE;
        }
    }

    /**
     * @notice Inner calculation based on pmm algorithm, sell quote
     * @param state The current PMM state
     * @param payQuoteAmount The amount of quote token user want to sell
     * @return receiveBaseAmount The amount of base token user will receive
     * @return newR The new R status after swap
     */
    function sellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
        internal
        pure
        returns (uint256 receiveBaseAmount, RState newR)
    {
        if (state.R == RState.ONE) {
            receiveBaseAmount = _ROneSellQuoteToken(state, payQuoteAmount);
            newR = RState.ABOVE_ONE;
        } else if (state.R == RState.ABOVE_ONE) {
            receiveBaseAmount = _RAboveSellQuoteToken(state, payQuoteAmount);
            newR = RState.ABOVE_ONE;
        } else {
            uint256 backToOnePayQuote = state.Q0 - state.Q;
            uint256 backToOneReceiveBase = state.B - state.B0;
            if (payQuoteAmount < backToOnePayQuote) {
                receiveBaseAmount = _RBelowSellQuoteToken(state, payQuoteAmount); 
                newR = RState.BELOW_ONE;
                if (receiveBaseAmount > backToOneReceiveBase) {
                    receiveBaseAmount = backToOneReceiveBase;
                }
            } else if (payQuoteAmount == backToOnePayQuote) {
                receiveBaseAmount = backToOneReceiveBase;
                newR = RState.ONE;
            } else {
                receiveBaseAmount = backToOneReceiveBase + (
                    _ROneSellQuoteToken(state, payQuoteAmount - backToOnePayQuote) 
                );
                newR = RState.ABOVE_ONE;
            }
        }
    }

    // ============ R = 1 cases ============

    function _ROneSellBaseToken(PMMState memory state, uint256 payBaseAmount)
        internal
        pure
        returns (
            uint256 // receiveQuoteToken
        )
    {
        // in theory Q2 <= targetQuoteTokenAmount
        // however when amount is close to 0, precision problems may cause Q2 > targetQuoteTokenAmount
        return
            DODOMath._SolveQuadraticFunctionForTrade(
                state.Q0,
                state.Q0,
                payBaseAmount,
                state.i,
                state.K
            );
    }

    function _ROneSellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
        internal
        pure
        returns (
            uint256 // receiveBaseToken
        )
    {
        return
            DODOMath._SolveQuadraticFunctionForTrade(
                state.B0,
                state.B0,
                payQuoteAmount,
                DecimalMath.reciprocalFloor(state.i),
                state.K
            );
    }

    // ============ R < 1 cases ============

    function _RBelowSellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
        internal
        pure
        returns (
            uint256 // receiveBaseToken
        )
    {
        return
            DODOMath._GeneralIntegrate(
                state.Q0,
                state.Q + payQuoteAmount,
                state.Q,
                DecimalMath.reciprocalFloor(state.i),
                state.K
            );
    }

    function _RBelowSellBaseToken(PMMState memory state, uint256 payBaseAmount)
        internal
        pure
        returns (
            uint256 // receiveQuoteToken
        )
    {
        return
            DODOMath._SolveQuadraticFunctionForTrade(
                state.Q0,
                state.Q,
                payBaseAmount,
                state.i,
                state.K
            );
    }

    // ============ R > 1 cases ============

    function _RAboveSellBaseToken(PMMState memory state, uint256 payBaseAmount)
        internal
        pure
        returns (
            uint256 // receiveQuoteToken
        )
    {
        return
            DODOMath._GeneralIntegrate(
                state.B0,
                state.B + payBaseAmount,
                state.B,
                state.i,
                state.K
            );
    }

    function _RAboveSellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
        internal
        pure
        returns (
            uint256 // receiveBaseToken
        )
    {
        return
            DODOMath._SolveQuadraticFunctionForTrade(
                state.B0,
                state.B,
                payQuoteAmount,
                DecimalMath.reciprocalFloor(state.i),
                state.K
            );
    }

    // ============ Helper functions ============

    function adjustedTarget(PMMState memory state) internal pure {
        if (state.R == RState.BELOW_ONE) {
            state.Q0 = DODOMath._SolveQuadraticFunctionForTarget(
                state.Q,
                state.B - state.B0,
                state.i,
                state.K
            );
        } else if (state.R == RState.ABOVE_ONE) {
            state.B0 = DODOMath._SolveQuadraticFunctionForTarget(
                state.B,
                state.Q - state.Q0,
                DecimalMath.reciprocalFloor(state.i),
                state.K
            );
        }
    }

    function getMidPrice(PMMState memory state) internal pure returns (uint256) {
        if (state.R == RState.BELOW_ONE) {
            uint256 R = DecimalMath.divFloor(state.Q0 * state.Q0 / state.Q, state.Q);
            R = DecimalMath.ONE - state.K + (DecimalMath.mulFloor(state.K, R));
            return DecimalMath.divFloor(state.i, R);
        } else {
            uint256 R = DecimalMath.divFloor(state.B0 * state.B0 / state.B, state.B);
            R = DecimalMath.ONE - state.K + (DecimalMath.mulFloor(state.K, R));
            return DecimalMath.mulFloor(state.i, R);
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;
pragma experimental ABIEncoderV2;

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";

/**
 * @title DecimalMath
 * @author DODO Breeder
 *
 * @notice Functions for fixed point number with 18 decimals
 */

library DecimalMath {
    uint256 internal constant ONE = 10 ** 18;
    uint256 internal constant ONE2 = 10 ** 36;

    function mul(uint256 target, uint256 d) internal pure returns (uint256) {
        return target * d / (10 ** 18);
    }

    function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) {
        return target * d / (10 ** 18);
    }

    function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) {
        return _divCeil(target * d, 10 ** 18);
    }

    function div(uint256 target, uint256 d) internal pure returns (uint256) {
        return target * (10 ** 18) / d;
    }

    function divFloor(uint256 target, uint256 d) internal pure returns (uint256) {
        return target * (10 ** 18) / d;
    }

    function divCeil(uint256 target, uint256 d) internal pure returns (uint256) {
        return _divCeil(target * (10 ** 18), d);
    }

    function reciprocalFloor(uint256 target) internal pure returns (uint256) {
        return uint256(10 ** 36) / target;
    }

    function reciprocalCeil(uint256 target) internal pure returns (uint256) {
        return _divCeil(uint256(10 ** 36), target);
    }

    function sqrt(uint256 target) internal pure returns (uint256) {
        return Math.sqrt(target * ONE);
    }

    function powFloor(uint256 target, uint256 e) internal pure returns (uint256) {
        if (e == 0) {
            return 10 ** 18;
        } else if (e == 1) {
            return target;
        } else {
            uint256 p = powFloor(target, e / 2);
            p = p * p / (10 ** 18);
            if (e % 2 == 1) {
                p = p * target / (10 ** 18);
            }
            return p;
        }
    }

    function _divCeil(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 quotient = a / b;
        uint256 remainder = a - quotient * b;
        if (remainder > 0) {
            return quotient + 1;
        } else {
            return quotient;
        }
    }
}

/*

    Copyright 2020 DODO ZOO.
    SPDX-License-Identifier: Apache-2.0

*/

pragma solidity 0.8.16;
pragma experimental ABIEncoderV2;

interface IDODOCallee {
    function DVMSellShareCall(
        address sender,
        uint256 burnShareAmount,
        uint256 baseAmount,
        uint256 quoteAmount,
        bytes calldata data
    ) external;

    function DVMFlashLoanCall(
        address sender,
        uint256 baseAmount,
        uint256 quoteAmount,
        bytes calldata data
    ) external;

    function DPPFlashLoanCall(
        address sender,
        uint256 baseAmount,
        uint256 quoteAmount,
        bytes calldata data
    ) external;

    function DSPFlashLoanCall(
        address sender,
        uint256 baseAmount,
        uint256 quoteAmount,
        bytes calldata data
    ) external;

    function CPCancelCall(
        address sender,
        uint256 amount,
        bytes calldata data
    ) external;

	function CPClaimBidCall(
        address sender,
        uint256 baseAmount,
        uint256 quoteAmount,
        bytes calldata data
    ) external;

    function NFTRedeemCall(
        address payable assetTo,
        uint256 quoteAmount,
        bytes calldata
    ) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;
pragma experimental ABIEncoderV2;

import {DecimalMath} from "./DecimalMath.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";

/**
 * @title DODOMath
 * @author DODO Breeder
 *
 * @notice Functions for complex calculating. Including ONE Integration and TWO Quadratic solutions
 */
library DODOMath {
    using Math for uint256;
    /*
        Integrate dodo curve from V1 to V2
        require V0>=V1>=V2>0
        res = (1-k)i(V1-V2)+ikV0*V0(1/V2-1/V1)
        let V1-V2=delta
        res = i*delta*(1-k+k(V0^2/V1/V2))

        i is the price of V-res trading pair

        support k=1 & k=0 case

        [round down]
    */
    function _GeneralIntegrate(
        uint256 V0,
        uint256 V1,
        uint256 V2,
        uint256 i,
        uint256 k
    ) internal pure returns (uint256) {
        require(V0 > 0, "TARGET_IS_ZERO");
        uint256 fairAmount = i * (V1 - V2); // i*delta
        if (k == 0) {
            return fairAmount / DecimalMath.ONE;
        }
        uint256 V0V0V1V2 = DecimalMath.divFloor(V0 * V0 / V1, V2);
        uint256 penalty = DecimalMath.mulFloor(k, V0V0V1V2); // k(V0^2/V1/V2)
        return (DecimalMath.ONE - k + penalty) * fairAmount / DecimalMath.ONE2;
    }

    /*
        Follow the integration function above
        i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2)
        Assume Q2=Q0, Given Q1 and deltaB, solve Q0

        i is the price of delta-V trading pair
        give out target of V

        support k=1 & k=0 case

        [round down]
    */
    function _SolveQuadraticFunctionForTarget(
        uint256 V1,
        uint256 delta,
        uint256 i,
        uint256 k
    ) internal pure returns (uint256) {
        if (k == 0) {
            return V1 + DecimalMath.mulFloor(i, delta);
        }
        // V0 = V1*(1+(sqrt-1)/2k)
        // sqrt = √(1+4kidelta/V1)
        // premium = 1+(sqrt-1)/2k
        // uint256 sqrt = (4 * k).mul(i).mul(delta).div(V1).add(DecimalMath.ONE2).sqrt();

        if (V1 == 0) {
            return 0;
        }
        uint256 sqrt;
        uint256 ki = 4 * k * i;

        if (ki == 0) {
            sqrt = DecimalMath.ONE;
        } else if ((ki * delta) / ki == delta) {
            sqrt =((ki * delta) / V1  + DecimalMath.ONE2).sqrt();
        } else {
            sqrt = (ki / V1 * delta + DecimalMath.ONE2).sqrt();
        }
        uint256 premium =
            DecimalMath.divFloor(sqrt - DecimalMath.ONE, k * 2) + DecimalMath.ONE;
        // V0 is greater than or equal to V1 according to the solution
        return DecimalMath.mulFloor(V1, premium);
    }

    /*
        Follow the integration expression above, we have:
        i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2)
        Given Q1 and deltaB, solve Q2
        This is a quadratic function and the standard version is
        aQ2^2 + bQ2 + c = 0, where
        a=1-k
        -b=(1-k)Q1-kQ0^2/Q1+i*deltaB
        c=-kQ0^2 
        and Q2=(-b+sqrt(b^2+4(1-k)kQ0^2))/2(1-k)
        note: another root is negative, abondan

        if deltaBSig=true, then Q2>Q1, user sell Q and receive B
        if deltaBSig=false, then Q2<Q1, user sell B and receive Q
        return |Q1-Q2|

        as we only support sell amount as delta, the deltaB is always negative
        the input ideltaB is actually -ideltaB in the equation

        i is the price of delta-V trading pair

        support k=1 & k=0 case

        [round down]
    */
    function _SolveQuadraticFunctionForTrade(
        uint256 V0,
        uint256 V1,
        uint256 delta,
        uint256 i,
        uint256 k
    ) internal pure returns (uint256) {
        require(V0 > 0, "TARGET_IS_ZERO");
        if (delta == 0) {
            return 0;
        }

        if (k == 0) {
            // why v1
            return DecimalMath.mulFloor(i, delta) > V1 ? V1 : DecimalMath.mulFloor(i, delta);
        }

        if (k == DecimalMath.ONE) {
            // if k==1
            // Q2=Q1/(1+ideltaBQ1/Q0/Q0)
            // temp = ideltaBQ1/Q0/Q0
            // Q2 = Q1/(1+temp)
            // Q1-Q2 = Q1*(1-1/(1+temp)) = Q1*(temp/(1+temp))
            // uint256 temp = i.mul(delta).mul(V1).div(V0.mul(V0));
            uint256 temp;
            uint256 idelta = i * (delta);
            if (idelta == 0) {
                temp = 0;
            } else if ((idelta * V1) / idelta == V1) {
                temp = (idelta * V1) / (V0 * V0);
            } else {
                temp = delta * (V1) / (V0) * (i) / (V0);
            }
            return V1 * (temp) / (temp + (DecimalMath.ONE));
        }

        // calculate -b value and sig
        // b = kQ0^2/Q1-i*deltaB-(1-k)Q1
        // part1 = (1-k)Q1 >=0
        // part2 = kQ0^2/Q1-i*deltaB >=0
        // bAbs = abs(part1-part2)
        // if part1>part2 => b is negative => bSig is false
        // if part2>part1 => b is positive => bSig is true
        uint256 part2 = k * (V0) / (V1) * (V0) + (i * (delta)); // kQ0^2/Q1-i*deltaB
        uint256 bAbs = (DecimalMath.ONE - k) * (V1); // (1-k)Q1

        bool bSig;
        if (bAbs >= part2) {
            bAbs = bAbs - part2;
            bSig = false;
        } else {
            bAbs = part2 - bAbs;
            bSig = true;
        }
        bAbs = bAbs / (DecimalMath.ONE);

        // calculate sqrt
        uint256 squareRoot = DecimalMath.mulFloor((DecimalMath.ONE - k) * (4), DecimalMath.mulFloor(k, V0) * (V0)); // 4(1-k)kQ0^2
        squareRoot = Math.sqrt((bAbs * bAbs) + squareRoot); // sqrt(b*b+4(1-k)kQ0*Q0)

        // final res
        uint256 denominator = (DecimalMath.ONE - k) * 2; // 2(1-k)
        uint256 numerator;
        if (bSig) {
            numerator = squareRoot - bAbs;
            if (numerator == 0) {
                revert("DODOMath: should not be 0");
            }
        } else {
            numerator = bAbs + squareRoot;
        }

        uint256 V2 = DecimalMath.divCeil(numerator, denominator);
        if (V2 > V1) {
            return 0;
        } else {
            return V1 - V2;
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (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) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 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 256, 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 << 3) < value ? 1 : 0);
        }
    }
}

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

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

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

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

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

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

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

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

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

Contract Security Audit

Contract ABI

API
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"increaseShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalShares","type":"uint256"}],"name":"BuyShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"address","name":"assetTo","type":"address"},{"indexed":false,"internalType":"uint256","name":"baseAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quoteAmount","type":"uint256"}],"name":"DODOFlashLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"fromToken","type":"address"},{"indexed":false,"internalType":"address","name":"toToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"DODOSwap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newI","type":"uint256"}],"name":"IChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMtFee","type":"uint256"}],"name":"MtFeeRateChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum PMMPricing.RState","name":"newRState","type":"uint8"}],"name":"RChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"decreaseShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalShares","type":"uint256"}],"name":"SellShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawMtFee","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_ADMIN_","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_BASE_PRICE_CUMULATIVE_LAST_","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_BASE_RESERVE_","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_BASE_TARGET_","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_BASE_TOKEN_","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_BLOCK_TIMESTAMP_LAST_","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_IS_OPEN_TWAP_","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_I_","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_K_","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_LP_FEE_RATE_","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_MAINTAINER_","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_MT_FEE_BASE_","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_MT_FEE_QUOTE_","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_MT_FEE_RATE_","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_MT_FEE_RATE_MODEL_","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_PRICE_LIMIT_","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_QUOTE_RESERVE_","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_QUOTE_TARGET_","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_QUOTE_TOKEN_","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_RState_","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"addressToShortString","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"mtFeeRate","type":"uint256"}],"name":"adjustMtFeeRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"i","type":"uint256"}],"name":"adjustPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"priceLimit","type":"uint256"}],"name":"adjustPriceLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"buildDomainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"buyShares","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"baseInput","type":"uint256"},{"internalType":"uint256","name":"quoteInput","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"correctRState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"baseAmount","type":"uint256"},{"internalType":"uint256","name":"quoteAmount","type":"uint256"},{"internalType":"address","name":"assetTo","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getBaseInput","outputs":[{"internalType":"uint256","name":"input","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMidPrice","outputs":[{"internalType":"uint256","name":"midPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMtFeeTotal","outputs":[{"internalType":"uint256","name":"mtFeeBase","type":"uint256"},{"internalType":"uint256","name":"mtFeeQuote","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPMMState","outputs":[{"components":[{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"B","type":"uint256"},{"internalType":"uint256","name":"Q","type":"uint256"},{"internalType":"uint256","name":"B0","type":"uint256"},{"internalType":"uint256","name":"Q0","type":"uint256"},{"internalType":"enum PMMPricing.RState","name":"R","type":"uint8"}],"internalType":"struct PMMPricing.PMMState","name":"state","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPMMStateForCall","outputs":[{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"B","type":"uint256"},{"internalType":"uint256","name":"Q","type":"uint256"},{"internalType":"uint256","name":"B0","type":"uint256"},{"internalType":"uint256","name":"Q0","type":"uint256"},{"internalType":"uint256","name":"R","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getQuoteInput","outputs":[{"internalType":"uint256","name":"input","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserFeeRate","outputs":[{"internalType":"uint256","name":"lpFeeRate","type":"uint256"},{"internalType":"uint256","name":"mtFeeRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVaultReserve","outputs":[{"internalType":"uint256","name":"baseReserve","type":"uint256"},{"internalType":"uint256","name":"quoteReserve","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"maintainer","type":"address"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"baseTokenAddress","type":"address"},{"internalType":"address","name":"quoteTokenAddress","type":"address"},{"internalType":"uint256","name":"lpFeeRate","type":"uint256"},{"internalType":"uint256","name":"mtFeeRate","type":"uint256"},{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"uint256","name":"k","type":"uint256"},{"internalType":"bool","name":"isOpenTWAP","type":"bool"}],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"trader","type":"address"},{"internalType":"uint256","name":"payBaseAmount","type":"uint256"}],"name":"querySellBase","outputs":[{"internalType":"uint256","name":"receiveQuoteAmount","type":"uint256"},{"internalType":"uint256","name":"mtFee","type":"uint256"},{"internalType":"enum PMMPricing.RState","name":"newRState","type":"uint8"},{"internalType":"uint256","name":"newBaseTarget","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"trader","type":"address"},{"internalType":"uint256","name":"payQuoteAmount","type":"uint256"}],"name":"querySellQuote","outputs":[{"internalType":"uint256","name":"receiveBaseAmount","type":"uint256"},{"internalType":"uint256","name":"mtFee","type":"uint256"},{"internalType":"enum PMMPricing.RState","name":"newRState","type":"uint8"},{"internalType":"uint256","name":"newQuoteTarget","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"sellBase","outputs":[{"internalType":"uint256","name":"receiveQuoteAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"sellQuote","outputs":[{"internalType":"uint256","name":"receiveBaseAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shareAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"baseMinAmount","type":"uint256"},{"internalType":"uint256","name":"quoteMinAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"sellShares","outputs":[{"internalType":"uint256","name":"baseAmount","type":"uint256"},{"internalType":"uint256","name":"quoteAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"withdrawMtFeeTotal","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040526001805461ff00191690556103e8601455601780546001600160a01b031916905534801561003157600080fd5b5060016000556149a4806100466000396000f3fe608060405234801561001057600080fd5b506004361061038e5760003560e01c8063880a4d87116101de578063d505accf1161010f578063ee27c689116100ad578063fcf709f71161007c578063fcf709f714610841578063fd1ed7e91461084c578063fe24cb7f14610889578063fff6cae91461089257600080fd5b8063ee27c68914610815578063f5346aea1461081d578063f6b06e7014610825578063f811d6921461083857600080fd5b8063e539ef49116100e9578063e539ef49146107de578063e6485c8e146107f1578063eb99da6a14610804578063ec2fd46d1461080c57600080fd5b8063d505accf1461077f578063dd62ed3e14610792578063dd93f59a146107cb57600080fd5b8063bbf5ce781161017c578063bfdbd72311610156578063bfdbd72314610747578063c0ffa17814610750578063d0a494e414610759578063d4b970461461076c57600080fd5b8063bbf5ce7814610703578063bd6015b41461071d578063bf357dae1461073057600080fd5b8063a9059cbb116101b8578063a9059cbb146106c1578063ab44a7a3146106d4578063b56ceaa6146106dd578063b59b8555146106f057600080fd5b8063880a4d871461067857806395d89b41146106a4578063a382d1b9146106ac57600080fd5b806347bbad2e116102c357806370a082311161026157806377f586571161023057806377f586571461060057806379a04876146106325780637d721504146106455780637ecebe001461065857600080fd5b806370a08231146105b457806371f9100c146105dd57806372bf079e146105e557806373d74cf8146105f857600080fd5b806354fd4d501161029d57806354fd4d501461055157806363ab59931461057657806365f6fcbb1461058957806366410a211461059157600080fd5b806347bbad2e146105075780634a248d2a146105105780634c85b4251461052357600080fd5b806330adf81f116103305780633644e5151161030a5780633644e515146104ab5780633afdfeba146104b45780634322ec83146104bd57806344096609146104ee57600080fd5b806330adf81f14610438578063313ce5671461045f57806336223ce91461047e57600080fd5b8063171019401161036c57806317101940146103e957806318160ddd146103fc57806323b872dd146104135780632df6cb481461042657600080fd5b806304b621bc1461039357806306fdde03146103a8578063095ea7b3146103c6575b600080fd5b6103a66103a1366004614103565b61089a565b005b6103b0610b2e565b6040516103bd91906141b7565b60405180910390f35b6103d96103d43660046141ea565b610bbc565b60405190151581526020016103bd565b6103b06103f7366004614214565b610bd3565b610405600b5481565b6040519081526020016103bd565b6103d961042136600461422f565b610d56565b6001546103d990610100900460ff1681565b6104057f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b60095461046c9060ff1681565b60405160ff90911681526020016103bd565b6005546001600160701b0380821691600160701b9004165b604080519283526020830191909152016103bd565b610405600e5481565b61040560145481565b6001546104d6906201000090046001600160a01b031681565b6040516001600160a01b0390911681526020016103bd565b6104966104fc366004614214565b506011546010549091565b61040560165481565b6003546104d6906001600160a01b031681565b610536610531366004614214565b610f06565b604080519384526020840192909252908201526060016103bd565b60408051808201909152600981526847535020312e302e3160b81b60208201526103b0565b6002546104d6906001600160a01b031681565b610405611370565b6105a461059f3660046141ea565b611408565b6040516103bd94939291906142a3565b6104056105c2366004614214565b6001600160a01b03166000908152600c602052604090205490565b610405611471565b6103a66105f33660046142cd565b6114bf565b6103a66115da565b60075461061a90600160701b90046001600160701b031681565b6040516001600160701b0390911681526020016103bd565b6105a46106403660046141ea565b611691565b60055461061a906001600160701b031681565b610405610666366004614214565b600f6020526000908152604090205481565b60055461068f90600160e01b900463ffffffff1681565b60405163ffffffff90911681526020016103bd565b6103b06116fa565b6106b4611707565b6040516103bd91906142e6565b6103d96106cf3660046141ea565b6117a9565b61040560115481565b6104966106eb36600461437f565b611895565b6103a66106fe3660046142cd565b611c6f565b60055461061a90600160701b90046001600160701b031681565b61040561072b366004614214565b611d48565b60075461068f90600160e01b900463ffffffff1681565b61040560155481565b61040560105481565b6103a66107673660046143f9565b611fc1565b6004546104d6906001600160a01b031681565b6103a661078d366004614470565b612581565b6104056107a03660046144dd565b6001600160a01b039182166000908152600d6020908152604080832093909416825291909152205490565b6104056107d9366004614214565b612797565b60075461061a906001600160701b031681565b6103a66107ff3660046142cd565b612a04565b6103a6612aa2565b61040560125481565b610405612bd6565b610405612be8565b6017546104d6906001600160a01b031681565b61040560135481565b601554601654610496565b610854612cf0565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e0016103bd565b61040560065481565b6103a6612d52565b60015460ff16156108e45760405162461bcd60e51b815260206004820152600f60248201526e11d4d417d253925512505312569151608a1b60448201526064015b60405180910390fd5b6001805460ff1916811790556001600160a01b038087169088160361094b5760405162461bcd60e51b815260206004820152601a60248201527f424153455f51554f54455f43414e5f4e4f545f42455f53414d4500000000000060448201526064016108db565b600380546001600160a01b03808a166001600160a01b0319928316179092556004805492891692909116919091179055821580159061099957506ec097ce7bc90715b34b9f10000000008311155b6109a257600080fd5b6013839055670de0b6b3a76400008211156109bc57600080fd5b60128290556011859055601084905560018054600280546001600160a01b038c81166001600160a01b03199092169190911790915561ff0019908c16620100000216610100600160b01b0319909116178155604080518082018252918252605f60f81b6020808401919091528151808301909252600382526204753560ec1b908201528082610a4a30610bd3565b604051602001610a5c93929190614510565b604051602081830303815290604052600a9081610a7991906145f1565b506040805180820190915260038152620474c560ec1b6020820152600890610aa190826145f1565b50886001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ae0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0491906146b1565b6009805460ff191660ff92909216919091179055610b20612be8565b505050505050505050505050565b600a8054610b3b90614569565b80601f0160208091040260200160405190810160405280929190818152602001828054610b6790614569565b8015610bb45780601f10610b8957610100808354040283529160200191610bb4565b820191906000526020600020905b815481529060010190602001808311610b9757829003601f168201915b505050505081565b6000610bc9338484612d6c565b5060015b92915050565b604080518082018252601081526f181899199a1a9b1b9c1cb0b131b232b360811b6020820152815160088082528184019093526060926001600160a01b038516929160009160208201818036833701905050905060005b6004811015610d4d5782600485610c4284600c6146e4565b60208110610c5257610c526146f7565b1a60f81b6001600160f81b031916901c60f81c60ff1681518110610c7857610c786146f7565b01602001516001600160f81b03191682610c9383600261470d565b81518110610ca357610ca36146f7565b60200101906001600160f81b031916908160001a9053508284610cc783600c6146e4565b60208110610cd757610cd76146f7565b825191901a600f16908110610cee57610cee6146f7565b01602001516001600160f81b03191682610d0983600261470d565b610d149060016146e4565b81518110610d2457610d246146f7565b60200101906001600160f81b031916908160001a90535080610d458161472c565b915050610c2a565b50949350505050565b6001600160a01b0383166000908152600c6020526040812054821115610db35760405162461bcd60e51b81526020600482015260126024820152710848298829c868abe9c9ea8be8a9c9eaa8e960731b60448201526064016108db565b6001600160a01b0384166000908152600d60209081526040808320338452909152902054821115610e1d5760405162461bcd60e51b815260206004820152601460248201527308298989eae829c868abe9c9ea8be8a9c9eaa8e960631b60448201526064016108db565b6001600160a01b0384166000908152600c6020526040902054610e41908390614745565b6001600160a01b038086166000908152600c60205260408082209390935590851681522054610e719083906146e4565b6001600160a01b038085166000908152600c60209081526040808320949094559187168152600d82528281203382529091522054610eb0908390614745565b6001600160a01b038581166000818152600d60209081526040808320338452825291829020949094555185815291861692909160008051602061490f833981519152910160405180910390a35060019392505050565b6000806000610f13612dcd565b6015546003546040516370a0823160e01b8152306004820152600092916001600160a01b0316906370a0823190602401602060405180830381865afa158015610f60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f849190614758565b610f8e9190614745565b601654600480546040516370a0823160e01b815230928101929092529293506000926001600160a01b0316906370a0823190602401602060405180830381865afa158015610fe0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110049190614758565b61100e9190614745565b6005549091506001600160701b0380821691600160701b9004166110328285614745565b955061103e8184614745565b9450600086116110805760405162461bcd60e51b815260206004820152600d60248201526c1393d7d09054d157d253941555609a1b60448201526064016108db565b600b546000036111f957600083116110ce5760405162461bcd60e51b815260206004820152601160248201527016915493d7d45553d51157d05353d55395607a1b60448201526064016108db565b6110da84601354612e26565b83106110e657836110f2565b6110f283601354612e4c565b600780546001600160701b0319166001600160701b03831617905560135490975061111e908890612e26565b60078054600160701b600160e01b031916600160701b6001600160701b039384168102919091179182905590041661118f5760405162461bcd60e51b815260206004820152601460248201527351554f54455f5441524745545f49535f5a45524f60601b60448201526064016108db565b6107d187116111d95760405162461bcd60e51b815260206004820152601660248201527509a929ca8be829a9eaa9ca8be9c9ea8be8a9c9eaa8e960531b60448201526064016108db565b6111e660006103e9612e61565b6111f26103e988614745565b96506112f0565b6000821180156112095750600081115b156112f057600061121a8784612e4c565b905060006112288784612e4c565b90506000828210611239578261123b565b815b9050611249600b5482612e26565b600754909a50611262906001600160701b031682612e26565b60075461127891906001600160701b03166146e4565b600780546001600160701b0319166001600160701b0392831617908190556112a991600160701b9091041682612e26565b6007546112c69190600160701b90046001600160701b03166146e4565b6007600e6101000a8154816001600160701b0302191690836001600160701b031602179055505050505b6112fa8888612e61565b6113048484612f6b565b6001600160a01b0388166000818152600c60209081526040918290205482519384529083018a905282820152517f1c172440bdebb59cd92a7f08f4227903a3305ab6f880cb25f93eddb66843a1029181900360600190a1505050506113696001600055565b9193909250565b6015546005546003546040516370a0823160e01b8152306004820152600093926001600160701b0316916001600160a01b0316906370a08231906024015b602060405180830381865afa1580156113cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ef9190614758565b6113f99190614745565b6114039190614745565b905090565b6000806000806000611418611707565b90506114248187612fd6565b6011546010549297509094509061143b8782612e26565b9550856114488884612e26565b6114529089614745565b61145c9190614745565b96508260a00151935050505092959194509250565b601654600554600480546040516370a0823160e01b8152309281019290925260009392600160701b90046001600160701b0316916001600160a01b03909116906370a08231906024016113ae565b6002546001600160a01b0316331461150f5760405162461bcd60e51b8152602060048201526013602482015272105113525397d050d0d154d4d7d11153925151606a1b60448201526064016108db565b6000601354821161152d57816013546115289190614745565b61153a565b60135461153a9083614745565b905060145460135482620f4240611551919061470d565b61155b9190614787565b111561159e5760405162461bcd60e51b8152602060048201526012602482015271115610d1515117d4149250d157d31253525560721b60448201526064016108db565b60138290556040518281527f0caf6249df528dcb5afe9adf7a20744e990b338c3d1ca3adedd6362d2db048449060200160405180910390a15050565b60075463ffffffff600160e01b90910416600214801561160a57506007546005546001600160701b039182169116105b1561162f57600554600160701b8082046001600160701b039081169091029116176007555b60075463ffffffff600160e01b90910416600114801561166a57506007546005546001600160701b03600160701b9283900481169290910416105b1561168f57600554600160701b8082046001600160701b039081169091029116176007555b565b60008060008060006116a1611707565b90506116ad81876130cb565b601154601054929750909450906116c48782612e26565b9550856116d18884612e26565b6116db9089614745565b6116e59190614745565b96508260800151935050505092959194509250565b60088054610b3b90614569565b61170f614091565b601354815260125460208201526005546001600160701b038082166040840152600160701b918290048116606084015260075480821660808501529182041660a0830152600160e01b900463ffffffff1660028111156117715761177161426b565b8160c0019060028111156117875761178761426b565b9081600281111561179a5761179a61426b565b9052506117a6816131c8565b90565b336000908152600c60205260408120548211156117fd5760405162461bcd60e51b81526020600482015260126024820152710848298829c868abe9c9ea8be8a9c9eaa8e960731b60448201526064016108db565b336000908152600c6020526040902054611818908390614745565b336000908152600c6020526040808220929092556001600160a01b038516815220546118459083906146e4565b6001600160a01b0384166000818152600c602052604090819020929092559051339060008051602061490f833981519152906118849086815260200190565b60405180910390a350600192915050565b6000806118a0612dcd565b428310156118df5760405162461bcd60e51b815260206004820152600c60248201526b1512535157d156141254915160a21b60448201526064016108db565b336000908152600c602052604090205489111561192f5760405162461bcd60e51b815260206004820152600e60248201526d08e98a0be9c9ea8be8a9c9eaa8e960931b60448201526064016108db565b6015546003546040516370a0823160e01b8152306004820152600092916001600160a01b0316906370a0823190602401602060405180830381865afa15801561197c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a09190614758565b6119aa9190614745565b601654600480546040516370a0823160e01b815230928101929092529293506000926001600160a01b0316906370a0823190602401602060405180830381865afa1580156119fc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a209190614758565b611a2a9190614745565b600b5490915080611a3b8d8561470d565b611a459190614787565b945080611a528d8461470d565b611a5c9190614787565b600754909450611a8090611a7a908e906001600160701b031661470d565b8261326c565b600754611a9691906001600160701b0316614745565b600780546001600160701b0319166001600160701b039283161790819055611acc91611a7a918f91600160701b9091041661470d565b600754611ae99190600160701b90046001600160701b0316614745565b6007600e6101000a8154816001600160701b0302191690836001600160701b03160217905550898510158015611b1f5750888410155b611b615760405162461bcd60e51b81526020600482015260136024820152720ae92a89088a482aebe9c9ea8be8a9c9eaa8e9606b1b60448201526064016108db565b611b6b338d6132b5565b611b758b8661336d565b611b7f8b8561338e565b611b876133ab565b8615611bf6578a6001600160a01b0316632411d338338e88888d8d6040518763ffffffff1660e01b8152600401611bc3969594939291906147d2565b600060405180830381600087803b158015611bdd57600080fd5b505af1158015611bf1573d6000803e3d6000fd5b505050505b336000818152600c60209081526040918290205482519384526001600160a01b038f16918401919091528282018f90526060830152517f55caccde83781f39bfc1296eff45655b6496729443a7d48958b18b3b685600a59181900360800190a1505050611c636001600055565b97509795505050505050565b6001546201000090046001600160a01b03163314611cbf5760405162461bcd60e51b815260206004820152600d60248201526c1050d0d154d4d7d11153925151609a1b60448201526064016108db565b670de0b6b3a7640000811115611d0d5760405162461bcd60e51b8152602060048201526013602482015272494e56414c49445f4d545f4645455f5241544560681b60448201526064016108db565b60108190556040518181527fc9ec0c7a5c9e8424f73dce9c8a6ad565757953517e4f472e243c0955aa11322e9060200160405180910390a150565b6000611d52612dcd565b6015546003546040516370a0823160e01b8152306004820152600092916001600160a01b0316906370a0823190602401602060405180830381865afa158015611d9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dc39190614758565b611dcd9190614745565b600554909150600090611de9906001600160701b031683614745565b90506000806000611dfa3285611691565b9298509094509092509050611e0f878761338e565b82601654611e1d91906146e4565b601655806002811115611e3257611e3261426b565b600754600160e01b900463ffffffff908116911614611ee5576001600160701b03821115611e725760405162461bcd60e51b81526004016108db90614812565b600780546001600160701b0319166001600160701b038416179055806002811115611e9f57611e9f61426b565b6007601c6101000a81548163ffffffff021916908363ffffffff16021790555060008051602061492f83398151915281604051611edc9190614834565b60405180910390a15b601654600480546040516370a0823160e01b81523092810192909252611f6c92889290916001600160a01b0316906370a0823190602401602060405180830381865afa158015611f39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f5d9190614758565b611f679190614745565b612f6b565b60035460045460405160008051602061494f83398151915292611fa5926001600160a01b039182169291169088908b9033908e90614842565b60405180910390a15050505050611fbc6001600055565b919050565b611fc9612dcd565b611fd3838661336d565b611fdd838561338e565b801561204a5760405163d5b9979760e01b81526001600160a01b0384169063d5b9979790612017903390899089908890889060040161487c565b600060405180830381600087803b15801561203157600080fd5b505af1158015612045573d6000803e3d6000fd5b505050505b6015546003546040516370a0823160e01b8152306004820152600092916001600160a01b0316906370a0823190602401602060405180830381865afa158015612097573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120bb9190614758565b6120c59190614745565b601654600480546040516370a0823160e01b815230928101929092529293506000926001600160a01b0316906370a0823190602401602060405180830381865afa158015612117573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061213b9190614758565b6121459190614745565b6005549091506001600160701b0316821015806121745750600554600160701b90046001600160701b03168110155b6121905760405162461bcd60e51b81526004016108db906148aa565b6005546001600160701b0316821015612354576005546000906121c390600160701b90046001600160701b031683614745565b90506000806000806121d53286611408565b6005549397509195509350915084906121f89089906001600160701b0316614745565b11156122165760405162461bcd60e51b81526004016108db906148aa565b8260155461222491906146e4565b6015558160028111156122395761223961426b565b600754600160e01b900463ffffffff9081169116146122f5576001600160701b038111156122795760405162461bcd60e51b81526004016108db90614812565b60078054600160701b600160e01b031916600160701b6001600160701b038416021790558160028111156122af576122af61426b565b6007601c6101000a81548163ffffffff021916908363ffffffff16021790555060008051602061492f833981519152826040516122ec9190614834565b60405180910390a15b60008051602061494f833981519152600460009054906101000a90046001600160a01b0316600360009054906101000a90046001600160a01b03168787338f60405161234696959493929190614842565b60405180910390a150505050505b600554600160701b90046001600160701b031681101561251657600554600090612387906001600160701b031684614745565b90506000806000806123993286611691565b6005549397509195509350915084906123c3908890600160701b90046001600160701b0316614745565b11156123e15760405162461bcd60e51b81526004016108db906148aa565b826016546123ef91906146e4565b6016558160028111156124045761240461426b565b600754600160e01b900463ffffffff9081169116146124b7576001600160701b038111156124445760405162461bcd60e51b81526004016108db90614812565b600780546001600160701b0319166001600160701b0383161790558160028111156124715761247161426b565b6007601c6101000a81548163ffffffff021916908363ffffffff16021790555060008051602061492f833981519152826040516124ae9190614834565b60405180910390a15b60008051602061494f833981519152600360009054906101000a90046001600160a01b0316600460009054906101000a90046001600160a01b03168787338f60405161250896959493929190614842565b60405180910390a150505050505b61251e6133ab565b604080513381526001600160a01b0387166020820152908101889052606081018790527f0b82e93068db15abd9fbb2682c65462ea8a0a10582dce93a5664818e296f54eb9060800160405180910390a1505061257a6001600055565b5050505050565b428410156125c85760405162461bcd60e51b81526020600482015260146024820152731113d113d7d1d4d417d3140e881156141254915160621b60448201526064016108db565b600e546001600160a01b0388166000908152600f6020526040812080549192917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918b918b918b91908761261b8361472c565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810187905260e0016040516020818303038152906040528051906020012060405160200161269492919061190160f01b81526002810192909252602282015260420190565b60408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa1580156126ff573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906127355750886001600160a01b0316816001600160a01b0316145b6127815760405162461bcd60e51b815260206004820152601e60248201527f444f444f5f4753505f4c503a20494e56414c49445f5349474e4154555245000060448201526064016108db565b61278c898989612d6c565b505050505050505050565b60006127a1612dcd565b601654600480546040516370a0823160e01b81523092810192909252600092916001600160a01b03909116906370a0823190602401602060405180830381865afa1580156127f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128179190614758565b6128219190614745565b60055490915060009061284490600160701b90046001600160701b031683614745565b905060008060006128553285611408565b929850909450909250905061286a878761336d565b8260155461287891906146e4565b60155580600281111561288d5761288d61426b565b600754600160e01b900463ffffffff908116911614612949576001600160701b038211156128cd5760405162461bcd60e51b81526004016108db90614812565b60078054600160701b600160e01b031916600160701b6001600160701b038516021790558060028111156129035761290361426b565b6007601c6101000a81548163ffffffff021916908363ffffffff16021790555060008051602061492f833981519152816040516129409190614834565b60405180910390a15b6015546003546040516370a0823160e01b81523060048201526129cb92916001600160a01b0316906370a0823190602401602060405180830381865afa158015612997573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129bb9190614758565b6129c59190614745565b86612f6b565b60045460035460405160008051602061494f83398151915292611fa5926001600160a01b039182169291169088908b9033908e90614842565b6002546001600160a01b03163314612a545760405162461bcd60e51b8152602060048201526013602482015272105113525397d050d0d154d4d7d11153925151606a1b60448201526064016108db565b620f4240811115612a9d5760405162461bcd60e51b81526020600482015260136024820152721253959053125117d4149250d157d312535255606a1b60448201526064016108db565b601455565b612aaa612dcd565b6001546201000090046001600160a01b03163314612afa5760405162461bcd60e51b815260206004820152600d60248201526c1050d0d154d4d7d11153925151609a1b60448201526064016108db565b601680546015546000909255600154909190612b25906201000090046001600160a01b03168361338e565b6000601555600154612b46906201000090046001600160a01b03168261336d565b6004546040518381526001600160a01b03909116907fe9b6b9af52fdb56b6ac34a0639bcf8e407008374741c7a0e187da490755b6bfc9060200160405180910390a26003546040518281526001600160a01b03909116907fe9b6b9af52fdb56b6ac34a0639bcf8e407008374741c7a0e187da490755b6bfc9060200160405180910390a2505061168f6001600055565b6000611403612be3611707565b613553565b60408051808201825260018152605f60f81b6020808301919091528251808401909352600383526204753560ec1b90830152600091828183612c2930610bd3565b604051602001612c3b93929190614510565b60408051601f198184030181528282528051602091820120838301835260018452603160f81b9382019390935281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81830152808301939093527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608401524660808401523060a0808501919091528251808503909101815260c09093019091528151910120600e819055949350505050565b600080600080600080600080612d04611707565b905080600001519750806020015196508060400151955080606001519450806080015193508060a0015192508060c001516002811115612d4657612d4661426b565b91505090919293949596565b612d5a612dcd565b612d626133ab565b61168f6001600055565b6001600160a01b038381166000818152600d602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b600260005403612e1f5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016108db565b6002600055565b6000670de0b6b3a7640000612e3b838561470d565b612e459190614787565b9392505050565b600081612e3b84670de0b6b3a764000061470d565b6103e88111612eab5760405162461bcd60e51b815260206004820152601660248201527509a929ca8be829a9eaa9ca8be9c9ea8be8a9c9eaa8e960531b60448201526064016108db565b6001600160a01b0382166000908152600c6020526040902054612ecf9082906146e4565b6001600160a01b0383166000908152600c6020526040902055600b54612ef69082906146e4565b600b556040518181526001600160a01b038316907f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968859060200160405180910390a26040518181526001600160a01b0383169060009060008051602061490f833981519152906020015b60405180910390a35050565b6001600160701b038211801590612f8957506001600160701b038111155b612fa55760405162461bcd60e51b81526004016108db90614812565b600580546001600160701b03928316600160701b026001600160e01b03199091169290931691909117919091179055565b600080808460c001516002811115612ff057612ff061426b565b0361300a57612fff8484613652565b9150600190506130c4565b60018460c0015160028111156130225761302261426b565b0361303157612fff8484613679565b600084606001518560a001516130479190614745565b905060008560800151866040015161305f9190614745565b90508185101561308a576130738686613696565b93506002925080841115613085578093505b6130c1565b81850361309d57809350600092506130c1565b6130b0866130ab8488614745565b613652565b6130ba90826146e4565b9350600192505b50505b9250929050565b600080808460c0015160028111156130e5576130e561426b565b036130ff576130f484846136ca565b9150600290506130c4565b60018460c0015160028111156131175761311761426b565b036131b3576000846040015185608001516131329190614745565b905060008560a00151866060015161314a9190614745565b9050818510156131755761315e86866136e9565b93506001925080841115613170578093505b6131ac565b81850361318857809350600092506131ac565b61319b866131968488614745565b6136ca565b6131a590826146e4565b9350600292505b50506130c4565b6131bd8484613714565b946002945092505050565b60028160c0015160028111156131e0576131e061426b565b036132155761320d8160600151826080015183604001516132019190614745565b83516020850151613733565b60a082015250565b60018160c00151600281111561322d5761322d61426b565b036132695761326381604001518260a00151836060015161324e9190614745565b83516132599061386b565b8460200151613733565b60808201525b50565b6000806132798385614787565b90506000613287848361470d565b6132919086614745565b905080156132ad576132a48260016146e4565b92505050610bcd565b509050610bcd565b6001600160a01b0382166000908152600c60205260409020546132d9908290614745565b6001600160a01b0383166000908152600c6020526040902055600b54613300908290614745565b600b556040518181526001600160a01b038316907fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca59060200160405180910390a26040518181526000906001600160a01b0384169060008051602061490f83398151915290602001612f5f565b801561338a5760035461338a906001600160a01b03168383613886565b5050565b801561338a5760045461338a906001600160a01b03168383613886565b6015546003546040516370a0823160e01b8152306004820152600092916001600160a01b0316906370a0823190602401602060405180830381865afa1580156133f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061341c9190614758565b6134269190614745565b601654600480546040516370a0823160e01b815230928101929092529293506000926001600160a01b0316906370a0823190602401602060405180830381865afa158015613478573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061349c9190614758565b6134a69190614745565b90506001600160701b0382118015906134c657506001600160701b038111155b6134e25760405162461bcd60e51b81526004016108db90614812565b6005546001600160701b0316821461351057600580546001600160701b0319166001600160701b0384161790555b600554600160701b90046001600160701b0316811461338a57600580546001600160701b038316600160701b02600160701b600160e01b03199091161790555050565b600060028260c00151600281111561356d5761356d61426b565b036135e257606082015160a08301516000916135a19161358d908061470d565b6135979190614787565b8460600151612e4c565b90506135b1836020015182612e26565b60208401516135c890670de0b6b3a7640000614745565b6135d291906146e4565b9050612e45836000015182612e4c565b60408201516080830151600091613611916135fd908061470d565b6136079190614787565b8460400151612e4c565b9050613621836020015182612e26565b602084015161363890670de0b6b3a7640000614745565b61364291906146e4565b9050612e45836000015182612e26565b6000612e45836080015184608001518461366f876000015161386b565b87602001516138dd565b6000612e45836080015184604001518461366f876000015161386b565b6000612e458360a001518385606001516136b091906146e4565b606086015186516136c09061386b565b8760200151613bef565b6000612e458360a001518460a0015184866000015187602001516138dd565b6000612e45836080015183856040015161370391906146e4565b604086015186516020880151613bef565b6000612e458360a00151846060015184866000015187602001516138dd565b600081600003613758576137478385612e26565b61375190866146e4565b9050613863565b8460000361376857506000613863565b6000808461377785600461470d565b613781919061470d565b90508060000361379b57670de0b6b3a7640000915061381f565b85816137a7828261470d565b6137b19190614787565b036137f4576137ed6ec097ce7bc90715b34b9f1000000000886137d4898561470d565b6137de9190614787565b6137e891906146e4565b613cea565b915061381f565b61381c6ec097ce7bc90715b34b9f1000000000876138128a85614787565b6137de919061470d565b91505b6000670de0b6b3a76400006138476138378286614745565b61384288600261470d565b612e4c565b61385191906146e4565b905061385d8882612e26565b93505050505b949350505050565b6000610bcd826ec097ce7bc90715b34b9f1000000000614787565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526138d8908490613dd2565b505050565b600080861161391f5760405162461bcd60e51b815260206004820152600e60248201526d5441524745545f49535f5a45524f60901b60448201526064016108db565b8360000361392f57506000613be6565b8160000361395f57846139428486612e26565b11613956576139518385612e26565b613958565b845b9050613be6565b670de0b6b3a76400008203613a2b5760008061397b868661470d565b90508060000361398e57600091506139fc565b868161399a828261470d565b6139a49190614787565b036139ce576139b3888061470d565b6139bd888361470d565b6139c79190614787565b91506139fc565b8785816139db8a8a61470d565b6139e59190614787565b6139ef919061470d565b6139f99190614787565b91505b613a0e670de0b6b3a7640000836146e4565b613a18838961470d565b613a229190614787565b92505050613be6565b6000613a37858561470d565b8787613a43828761470d565b613a4d9190614787565b613a57919061470d565b613a6191906146e4565b9050600086613a7885670de0b6b3a7640000614745565b613a82919061470d565b90506000828210613aa257613a978383614745565b915060009050613ab3565b613aac8284614745565b9150600190505b613ac5670de0b6b3a764000083614787565b91506000613b03613ade87670de0b6b3a7640000614745565b613ae990600461470d565b8b613af4898e612e26565b613afe919061470d565b612e26565b9050613b13816137de858061470d565b90506000613b2987670de0b6b3a7640000614745565b613b3490600261470d565b905060008315613b9f57613b488584614745565b905080600003613b9a5760405162461bcd60e51b815260206004820152601960248201527f444f444f4d6174683a2073686f756c64206e6f7420626520300000000000000060448201526064016108db565b613bac565b613ba983866146e4565b90505b6000613bb88284613ea7565b90508b811115613bd2576000975050505050505050613be6565b613bdc818d614745565b9750505050505050505b95945050505050565b6000808611613c315760405162461bcd60e51b815260206004820152600e60248201526d5441524745545f49535f5a45524f60901b60448201526064016108db565b6000613c3d8587614745565b613c47908561470d565b905082600003613c6b57613c63670de0b6b3a764000082614787565b915050613be6565b6000613c8b87613c7b8a8061470d565b613c859190614787565b87612e4c565b90506000613c998583612e26565b90506ec097ce7bc90715b34b9f10000000008382613cbf88670de0b6b3a7640000614745565b613cc991906146e4565b613cd3919061470d565b613cdd9190614787565b9998505050505050505050565b600081600003613cfc57506000919050565b60006001613d0984613ec4565b901c6001901b90506001818481613d2257613d22614771565b048201901c90506001818481613d3a57613d3a614771565b048201901c90506001818481613d5257613d52614771565b048201901c90506001818481613d6a57613d6a614771565b048201901c90506001818481613d8257613d82614771565b048201901c90506001818481613d9a57613d9a614771565b048201901c90506001818481613db257613db2614771565b048201901c9050612e4581828581613dcc57613dcc614771565b04613f58565b6000613e27826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613f6e9092919063ffffffff16565b9050805160001480613e48575080806020019051810190613e4891906148d5565b6138d85760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016108db565b6000612e45613ebe84670de0b6b3a764000061470d565b8361326c565b600080608083901c15613ed957608092831c92015b604083901c15613eeb57604092831c92015b602083901c15613efd57602092831c92015b601083901c15613f0f57601092831c92015b600883901c15613f2157600892831c92015b600483901c15613f3357600492831c92015b600283901c15613f4557600292831c92015b600183901c15610bcd5760010192915050565b6000818310613f675781612e45565b5090919050565b6060613863848460008585600080866001600160a01b03168587604051613f9591906148f2565b60006040518083038185875af1925050503d8060008114613fd2576040519150601f19603f3d011682016040523d82523d6000602084013e613fd7565b606091505b5091509150613fe887838387613ff3565b979650505050505050565b6060831561406257825160000361405b576001600160a01b0385163b61405b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016108db565b5081613863565b61386383838151156140775781518083602001fd5b8060405162461bcd60e51b81526004016108db91906141b7565b6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600060028111156140d9576140d961426b565b905290565b80356001600160a01b0381168114611fbc57600080fd5b801515811461326957600080fd5b60008060008060008060008060006101208a8c03121561412257600080fd5b61412b8a6140de565b985061413960208b016140de565b975061414760408b016140de565b965061415560608b016140de565b955060808a0135945060a08a0135935060c08a0135925060e08a013591506101008a0135614182816140f5565b809150509295985092959850929598565b60005b838110156141ae578181015183820152602001614196565b50506000910152565b60208152600082518060208401526141d6816040850160208701614193565b601f01601f19169190910160400192915050565b600080604083850312156141fd57600080fd5b614206836140de565b946020939093013593505050565b60006020828403121561422657600080fd5b612e45826140de565b60008060006060848603121561424457600080fd5b61424d846140de565b925061425b602085016140de565b9150604084013590509250925092565b634e487b7160e01b600052602160045260246000fd5b6003811061429f57634e487b7160e01b600052602160045260246000fd5b9052565b84815260208101849052608081016142be6040830185614281565b82606083015295945050505050565b6000602082840312156142df57600080fd5b5035919050565b600060e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015161433660c0840182614281565b5092915050565b60008083601f84011261434f57600080fd5b50813567ffffffffffffffff81111561436757600080fd5b6020830191508360208285010111156130c457600080fd5b600080600080600080600060c0888a03121561439a57600080fd5b873596506143aa602089016140de565b95506040880135945060608801359350608088013567ffffffffffffffff8111156143d457600080fd5b6143e08a828b0161433d565b989b979a5095989497959660a090950135949350505050565b60008060008060006080868803121561441157600080fd5b8535945060208601359350614428604087016140de565b9250606086013567ffffffffffffffff81111561444457600080fd5b6144508882890161433d565b969995985093965092949392505050565b60ff8116811461326957600080fd5b600080600080600080600060e0888a03121561448b57600080fd5b614494886140de565b96506144a2602089016140de565b9550604088013594506060880135935060808801356144c081614461565b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156144f057600080fd5b6144f9836140de565b9150614507602084016140de565b90509250929050565b60008451614522818460208901614193565b845190830190614536818360208901614193565b8451910190614549818360208801614193565b0195945050505050565b634e487b7160e01b600052604160045260246000fd5b600181811c9082168061457d57607f821691505b60208210810361459d57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156138d857600081815260208120601f850160051c810160208610156145ca5750805b601f850160051c820191505b818110156145e9578281556001016145d6565b505050505050565b815167ffffffffffffffff81111561460b5761460b614553565b61461f816146198454614569565b846145a3565b602080601f831160018114614654576000841561463c5750858301515b600019600386901b1c1916600185901b1785556145e9565b600085815260208120601f198616915b8281101561468357888601518255948401946001909101908401614664565b50858210156146a15787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6000602082840312156146c357600080fd5b8151612e4581614461565b634e487b7160e01b600052601160045260246000fd5b80820180821115610bcd57610bcd6146ce565b634e487b7160e01b600052603260045260246000fd5b6000816000190483118215151615614727576147276146ce565b500290565b60006001820161473e5761473e6146ce565b5060010190565b81810381811115610bcd57610bcd6146ce565b60006020828403121561476a57600080fd5b5051919050565b634e487b7160e01b600052601260045260246000fd5b6000826147a457634e487b7160e01b600052601260045260246000fd5b500490565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60018060a01b038716815285602082015284604082015283606082015260a06080820152600061480660a0830184866147a9565b98975050505050505050565b6020808252600890820152674f564552464c4f5760c01b604082015260600190565b60208101610bcd8284614281565b6001600160a01b0396871681529486166020860152604085019390935260608401919091528316608083015290911660a082015260c00190565b60018060a01b0386168152846020820152836040820152608060608201526000613fe86080830184866147a9565b60208082526011908201527011931054d217d313d05397d19052531151607a1b604082015260600190565b6000602082840312156148e757600080fd5b8151612e45816140f5565b60008251614904818460208701614193565b919091019291505056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efdf176ad18be4f9f32efaa32f06e9d1175476504739a745f1399a6d3fa4b75917c2c0245e056d5fb095f04cd6373bc770802ebd1e6c918eb78fdef843cdb37b0fa264697066735822122051895c0ab736fc3a00a4811727eba097895a7a6aafd866b9c8cbf018d5eb73f264736f6c63430008100033

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061038e5760003560e01c8063880a4d87116101de578063d505accf1161010f578063ee27c689116100ad578063fcf709f71161007c578063fcf709f714610841578063fd1ed7e91461084c578063fe24cb7f14610889578063fff6cae91461089257600080fd5b8063ee27c68914610815578063f5346aea1461081d578063f6b06e7014610825578063f811d6921461083857600080fd5b8063e539ef49116100e9578063e539ef49146107de578063e6485c8e146107f1578063eb99da6a14610804578063ec2fd46d1461080c57600080fd5b8063d505accf1461077f578063dd62ed3e14610792578063dd93f59a146107cb57600080fd5b8063bbf5ce781161017c578063bfdbd72311610156578063bfdbd72314610747578063c0ffa17814610750578063d0a494e414610759578063d4b970461461076c57600080fd5b8063bbf5ce7814610703578063bd6015b41461071d578063bf357dae1461073057600080fd5b8063a9059cbb116101b8578063a9059cbb146106c1578063ab44a7a3146106d4578063b56ceaa6146106dd578063b59b8555146106f057600080fd5b8063880a4d871461067857806395d89b41146106a4578063a382d1b9146106ac57600080fd5b806347bbad2e116102c357806370a082311161026157806377f586571161023057806377f586571461060057806379a04876146106325780637d721504146106455780637ecebe001461065857600080fd5b806370a08231146105b457806371f9100c146105dd57806372bf079e146105e557806373d74cf8146105f857600080fd5b806354fd4d501161029d57806354fd4d501461055157806363ab59931461057657806365f6fcbb1461058957806366410a211461059157600080fd5b806347bbad2e146105075780634a248d2a146105105780634c85b4251461052357600080fd5b806330adf81f116103305780633644e5151161030a5780633644e515146104ab5780633afdfeba146104b45780634322ec83146104bd57806344096609146104ee57600080fd5b806330adf81f14610438578063313ce5671461045f57806336223ce91461047e57600080fd5b8063171019401161036c57806317101940146103e957806318160ddd146103fc57806323b872dd146104135780632df6cb481461042657600080fd5b806304b621bc1461039357806306fdde03146103a8578063095ea7b3146103c6575b600080fd5b6103a66103a1366004614103565b61089a565b005b6103b0610b2e565b6040516103bd91906141b7565b60405180910390f35b6103d96103d43660046141ea565b610bbc565b60405190151581526020016103bd565b6103b06103f7366004614214565b610bd3565b610405600b5481565b6040519081526020016103bd565b6103d961042136600461422f565b610d56565b6001546103d990610100900460ff1681565b6104057f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b60095461046c9060ff1681565b60405160ff90911681526020016103bd565b6005546001600160701b0380821691600160701b9004165b604080519283526020830191909152016103bd565b610405600e5481565b61040560145481565b6001546104d6906201000090046001600160a01b031681565b6040516001600160a01b0390911681526020016103bd565b6104966104fc366004614214565b506011546010549091565b61040560165481565b6003546104d6906001600160a01b031681565b610536610531366004614214565b610f06565b604080519384526020840192909252908201526060016103bd565b60408051808201909152600981526847535020312e302e3160b81b60208201526103b0565b6002546104d6906001600160a01b031681565b610405611370565b6105a461059f3660046141ea565b611408565b6040516103bd94939291906142a3565b6104056105c2366004614214565b6001600160a01b03166000908152600c602052604090205490565b610405611471565b6103a66105f33660046142cd565b6114bf565b6103a66115da565b60075461061a90600160701b90046001600160701b031681565b6040516001600160701b0390911681526020016103bd565b6105a46106403660046141ea565b611691565b60055461061a906001600160701b031681565b610405610666366004614214565b600f6020526000908152604090205481565b60055461068f90600160e01b900463ffffffff1681565b60405163ffffffff90911681526020016103bd565b6103b06116fa565b6106b4611707565b6040516103bd91906142e6565b6103d96106cf3660046141ea565b6117a9565b61040560115481565b6104966106eb36600461437f565b611895565b6103a66106fe3660046142cd565b611c6f565b60055461061a90600160701b90046001600160701b031681565b61040561072b366004614214565b611d48565b60075461068f90600160e01b900463ffffffff1681565b61040560155481565b61040560105481565b6103a66107673660046143f9565b611fc1565b6004546104d6906001600160a01b031681565b6103a661078d366004614470565b612581565b6104056107a03660046144dd565b6001600160a01b039182166000908152600d6020908152604080832093909416825291909152205490565b6104056107d9366004614214565b612797565b60075461061a906001600160701b031681565b6103a66107ff3660046142cd565b612a04565b6103a6612aa2565b61040560125481565b610405612bd6565b610405612be8565b6017546104d6906001600160a01b031681565b61040560135481565b601554601654610496565b610854612cf0565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e0016103bd565b61040560065481565b6103a6612d52565b60015460ff16156108e45760405162461bcd60e51b815260206004820152600f60248201526e11d4d417d253925512505312569151608a1b60448201526064015b60405180910390fd5b6001805460ff1916811790556001600160a01b038087169088160361094b5760405162461bcd60e51b815260206004820152601a60248201527f424153455f51554f54455f43414e5f4e4f545f42455f53414d4500000000000060448201526064016108db565b600380546001600160a01b03808a166001600160a01b0319928316179092556004805492891692909116919091179055821580159061099957506ec097ce7bc90715b34b9f10000000008311155b6109a257600080fd5b6013839055670de0b6b3a76400008211156109bc57600080fd5b60128290556011859055601084905560018054600280546001600160a01b038c81166001600160a01b03199092169190911790915561ff0019908c16620100000216610100600160b01b0319909116178155604080518082018252918252605f60f81b6020808401919091528151808301909252600382526204753560ec1b908201528082610a4a30610bd3565b604051602001610a5c93929190614510565b604051602081830303815290604052600a9081610a7991906145f1565b506040805180820190915260038152620474c560ec1b6020820152600890610aa190826145f1565b50886001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ae0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0491906146b1565b6009805460ff191660ff92909216919091179055610b20612be8565b505050505050505050505050565b600a8054610b3b90614569565b80601f0160208091040260200160405190810160405280929190818152602001828054610b6790614569565b8015610bb45780601f10610b8957610100808354040283529160200191610bb4565b820191906000526020600020905b815481529060010190602001808311610b9757829003601f168201915b505050505081565b6000610bc9338484612d6c565b5060015b92915050565b604080518082018252601081526f181899199a1a9b1b9c1cb0b131b232b360811b6020820152815160088082528184019093526060926001600160a01b038516929160009160208201818036833701905050905060005b6004811015610d4d5782600485610c4284600c6146e4565b60208110610c5257610c526146f7565b1a60f81b6001600160f81b031916901c60f81c60ff1681518110610c7857610c786146f7565b01602001516001600160f81b03191682610c9383600261470d565b81518110610ca357610ca36146f7565b60200101906001600160f81b031916908160001a9053508284610cc783600c6146e4565b60208110610cd757610cd76146f7565b825191901a600f16908110610cee57610cee6146f7565b01602001516001600160f81b03191682610d0983600261470d565b610d149060016146e4565b81518110610d2457610d246146f7565b60200101906001600160f81b031916908160001a90535080610d458161472c565b915050610c2a565b50949350505050565b6001600160a01b0383166000908152600c6020526040812054821115610db35760405162461bcd60e51b81526020600482015260126024820152710848298829c868abe9c9ea8be8a9c9eaa8e960731b60448201526064016108db565b6001600160a01b0384166000908152600d60209081526040808320338452909152902054821115610e1d5760405162461bcd60e51b815260206004820152601460248201527308298989eae829c868abe9c9ea8be8a9c9eaa8e960631b60448201526064016108db565b6001600160a01b0384166000908152600c6020526040902054610e41908390614745565b6001600160a01b038086166000908152600c60205260408082209390935590851681522054610e719083906146e4565b6001600160a01b038085166000908152600c60209081526040808320949094559187168152600d82528281203382529091522054610eb0908390614745565b6001600160a01b038581166000818152600d60209081526040808320338452825291829020949094555185815291861692909160008051602061490f833981519152910160405180910390a35060019392505050565b6000806000610f13612dcd565b6015546003546040516370a0823160e01b8152306004820152600092916001600160a01b0316906370a0823190602401602060405180830381865afa158015610f60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f849190614758565b610f8e9190614745565b601654600480546040516370a0823160e01b815230928101929092529293506000926001600160a01b0316906370a0823190602401602060405180830381865afa158015610fe0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110049190614758565b61100e9190614745565b6005549091506001600160701b0380821691600160701b9004166110328285614745565b955061103e8184614745565b9450600086116110805760405162461bcd60e51b815260206004820152600d60248201526c1393d7d09054d157d253941555609a1b60448201526064016108db565b600b546000036111f957600083116110ce5760405162461bcd60e51b815260206004820152601160248201527016915493d7d45553d51157d05353d55395607a1b60448201526064016108db565b6110da84601354612e26565b83106110e657836110f2565b6110f283601354612e4c565b600780546001600160701b0319166001600160701b03831617905560135490975061111e908890612e26565b60078054600160701b600160e01b031916600160701b6001600160701b039384168102919091179182905590041661118f5760405162461bcd60e51b815260206004820152601460248201527351554f54455f5441524745545f49535f5a45524f60601b60448201526064016108db565b6107d187116111d95760405162461bcd60e51b815260206004820152601660248201527509a929ca8be829a9eaa9ca8be9c9ea8be8a9c9eaa8e960531b60448201526064016108db565b6111e660006103e9612e61565b6111f26103e988614745565b96506112f0565b6000821180156112095750600081115b156112f057600061121a8784612e4c565b905060006112288784612e4c565b90506000828210611239578261123b565b815b9050611249600b5482612e26565b600754909a50611262906001600160701b031682612e26565b60075461127891906001600160701b03166146e4565b600780546001600160701b0319166001600160701b0392831617908190556112a991600160701b9091041682612e26565b6007546112c69190600160701b90046001600160701b03166146e4565b6007600e6101000a8154816001600160701b0302191690836001600160701b031602179055505050505b6112fa8888612e61565b6113048484612f6b565b6001600160a01b0388166000818152600c60209081526040918290205482519384529083018a905282820152517f1c172440bdebb59cd92a7f08f4227903a3305ab6f880cb25f93eddb66843a1029181900360600190a1505050506113696001600055565b9193909250565b6015546005546003546040516370a0823160e01b8152306004820152600093926001600160701b0316916001600160a01b0316906370a08231906024015b602060405180830381865afa1580156113cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ef9190614758565b6113f99190614745565b6114039190614745565b905090565b6000806000806000611418611707565b90506114248187612fd6565b6011546010549297509094509061143b8782612e26565b9550856114488884612e26565b6114529089614745565b61145c9190614745565b96508260a00151935050505092959194509250565b601654600554600480546040516370a0823160e01b8152309281019290925260009392600160701b90046001600160701b0316916001600160a01b03909116906370a08231906024016113ae565b6002546001600160a01b0316331461150f5760405162461bcd60e51b8152602060048201526013602482015272105113525397d050d0d154d4d7d11153925151606a1b60448201526064016108db565b6000601354821161152d57816013546115289190614745565b61153a565b60135461153a9083614745565b905060145460135482620f4240611551919061470d565b61155b9190614787565b111561159e5760405162461bcd60e51b8152602060048201526012602482015271115610d1515117d4149250d157d31253525560721b60448201526064016108db565b60138290556040518281527f0caf6249df528dcb5afe9adf7a20744e990b338c3d1ca3adedd6362d2db048449060200160405180910390a15050565b60075463ffffffff600160e01b90910416600214801561160a57506007546005546001600160701b039182169116105b1561162f57600554600160701b8082046001600160701b039081169091029116176007555b60075463ffffffff600160e01b90910416600114801561166a57506007546005546001600160701b03600160701b9283900481169290910416105b1561168f57600554600160701b8082046001600160701b039081169091029116176007555b565b60008060008060006116a1611707565b90506116ad81876130cb565b601154601054929750909450906116c48782612e26565b9550856116d18884612e26565b6116db9089614745565b6116e59190614745565b96508260800151935050505092959194509250565b60088054610b3b90614569565b61170f614091565b601354815260125460208201526005546001600160701b038082166040840152600160701b918290048116606084015260075480821660808501529182041660a0830152600160e01b900463ffffffff1660028111156117715761177161426b565b8160c0019060028111156117875761178761426b565b9081600281111561179a5761179a61426b565b9052506117a6816131c8565b90565b336000908152600c60205260408120548211156117fd5760405162461bcd60e51b81526020600482015260126024820152710848298829c868abe9c9ea8be8a9c9eaa8e960731b60448201526064016108db565b336000908152600c6020526040902054611818908390614745565b336000908152600c6020526040808220929092556001600160a01b038516815220546118459083906146e4565b6001600160a01b0384166000818152600c602052604090819020929092559051339060008051602061490f833981519152906118849086815260200190565b60405180910390a350600192915050565b6000806118a0612dcd565b428310156118df5760405162461bcd60e51b815260206004820152600c60248201526b1512535157d156141254915160a21b60448201526064016108db565b336000908152600c602052604090205489111561192f5760405162461bcd60e51b815260206004820152600e60248201526d08e98a0be9c9ea8be8a9c9eaa8e960931b60448201526064016108db565b6015546003546040516370a0823160e01b8152306004820152600092916001600160a01b0316906370a0823190602401602060405180830381865afa15801561197c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a09190614758565b6119aa9190614745565b601654600480546040516370a0823160e01b815230928101929092529293506000926001600160a01b0316906370a0823190602401602060405180830381865afa1580156119fc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a209190614758565b611a2a9190614745565b600b5490915080611a3b8d8561470d565b611a459190614787565b945080611a528d8461470d565b611a5c9190614787565b600754909450611a8090611a7a908e906001600160701b031661470d565b8261326c565b600754611a9691906001600160701b0316614745565b600780546001600160701b0319166001600160701b039283161790819055611acc91611a7a918f91600160701b9091041661470d565b600754611ae99190600160701b90046001600160701b0316614745565b6007600e6101000a8154816001600160701b0302191690836001600160701b03160217905550898510158015611b1f5750888410155b611b615760405162461bcd60e51b81526020600482015260136024820152720ae92a89088a482aebe9c9ea8be8a9c9eaa8e9606b1b60448201526064016108db565b611b6b338d6132b5565b611b758b8661336d565b611b7f8b8561338e565b611b876133ab565b8615611bf6578a6001600160a01b0316632411d338338e88888d8d6040518763ffffffff1660e01b8152600401611bc3969594939291906147d2565b600060405180830381600087803b158015611bdd57600080fd5b505af1158015611bf1573d6000803e3d6000fd5b505050505b336000818152600c60209081526040918290205482519384526001600160a01b038f16918401919091528282018f90526060830152517f55caccde83781f39bfc1296eff45655b6496729443a7d48958b18b3b685600a59181900360800190a1505050611c636001600055565b97509795505050505050565b6001546201000090046001600160a01b03163314611cbf5760405162461bcd60e51b815260206004820152600d60248201526c1050d0d154d4d7d11153925151609a1b60448201526064016108db565b670de0b6b3a7640000811115611d0d5760405162461bcd60e51b8152602060048201526013602482015272494e56414c49445f4d545f4645455f5241544560681b60448201526064016108db565b60108190556040518181527fc9ec0c7a5c9e8424f73dce9c8a6ad565757953517e4f472e243c0955aa11322e9060200160405180910390a150565b6000611d52612dcd565b6015546003546040516370a0823160e01b8152306004820152600092916001600160a01b0316906370a0823190602401602060405180830381865afa158015611d9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dc39190614758565b611dcd9190614745565b600554909150600090611de9906001600160701b031683614745565b90506000806000611dfa3285611691565b9298509094509092509050611e0f878761338e565b82601654611e1d91906146e4565b601655806002811115611e3257611e3261426b565b600754600160e01b900463ffffffff908116911614611ee5576001600160701b03821115611e725760405162461bcd60e51b81526004016108db90614812565b600780546001600160701b0319166001600160701b038416179055806002811115611e9f57611e9f61426b565b6007601c6101000a81548163ffffffff021916908363ffffffff16021790555060008051602061492f83398151915281604051611edc9190614834565b60405180910390a15b601654600480546040516370a0823160e01b81523092810192909252611f6c92889290916001600160a01b0316906370a0823190602401602060405180830381865afa158015611f39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f5d9190614758565b611f679190614745565b612f6b565b60035460045460405160008051602061494f83398151915292611fa5926001600160a01b039182169291169088908b9033908e90614842565b60405180910390a15050505050611fbc6001600055565b919050565b611fc9612dcd565b611fd3838661336d565b611fdd838561338e565b801561204a5760405163d5b9979760e01b81526001600160a01b0384169063d5b9979790612017903390899089908890889060040161487c565b600060405180830381600087803b15801561203157600080fd5b505af1158015612045573d6000803e3d6000fd5b505050505b6015546003546040516370a0823160e01b8152306004820152600092916001600160a01b0316906370a0823190602401602060405180830381865afa158015612097573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120bb9190614758565b6120c59190614745565b601654600480546040516370a0823160e01b815230928101929092529293506000926001600160a01b0316906370a0823190602401602060405180830381865afa158015612117573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061213b9190614758565b6121459190614745565b6005549091506001600160701b0316821015806121745750600554600160701b90046001600160701b03168110155b6121905760405162461bcd60e51b81526004016108db906148aa565b6005546001600160701b0316821015612354576005546000906121c390600160701b90046001600160701b031683614745565b90506000806000806121d53286611408565b6005549397509195509350915084906121f89089906001600160701b0316614745565b11156122165760405162461bcd60e51b81526004016108db906148aa565b8260155461222491906146e4565b6015558160028111156122395761223961426b565b600754600160e01b900463ffffffff9081169116146122f5576001600160701b038111156122795760405162461bcd60e51b81526004016108db90614812565b60078054600160701b600160e01b031916600160701b6001600160701b038416021790558160028111156122af576122af61426b565b6007601c6101000a81548163ffffffff021916908363ffffffff16021790555060008051602061492f833981519152826040516122ec9190614834565b60405180910390a15b60008051602061494f833981519152600460009054906101000a90046001600160a01b0316600360009054906101000a90046001600160a01b03168787338f60405161234696959493929190614842565b60405180910390a150505050505b600554600160701b90046001600160701b031681101561251657600554600090612387906001600160701b031684614745565b90506000806000806123993286611691565b6005549397509195509350915084906123c3908890600160701b90046001600160701b0316614745565b11156123e15760405162461bcd60e51b81526004016108db906148aa565b826016546123ef91906146e4565b6016558160028111156124045761240461426b565b600754600160e01b900463ffffffff9081169116146124b7576001600160701b038111156124445760405162461bcd60e51b81526004016108db90614812565b600780546001600160701b0319166001600160701b0383161790558160028111156124715761247161426b565b6007601c6101000a81548163ffffffff021916908363ffffffff16021790555060008051602061492f833981519152826040516124ae9190614834565b60405180910390a15b60008051602061494f833981519152600360009054906101000a90046001600160a01b0316600460009054906101000a90046001600160a01b03168787338f60405161250896959493929190614842565b60405180910390a150505050505b61251e6133ab565b604080513381526001600160a01b0387166020820152908101889052606081018790527f0b82e93068db15abd9fbb2682c65462ea8a0a10582dce93a5664818e296f54eb9060800160405180910390a1505061257a6001600055565b5050505050565b428410156125c85760405162461bcd60e51b81526020600482015260146024820152731113d113d7d1d4d417d3140e881156141254915160621b60448201526064016108db565b600e546001600160a01b0388166000908152600f6020526040812080549192917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918b918b918b91908761261b8361472c565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810187905260e0016040516020818303038152906040528051906020012060405160200161269492919061190160f01b81526002810192909252602282015260420190565b60408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa1580156126ff573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906127355750886001600160a01b0316816001600160a01b0316145b6127815760405162461bcd60e51b815260206004820152601e60248201527f444f444f5f4753505f4c503a20494e56414c49445f5349474e4154555245000060448201526064016108db565b61278c898989612d6c565b505050505050505050565b60006127a1612dcd565b601654600480546040516370a0823160e01b81523092810192909252600092916001600160a01b03909116906370a0823190602401602060405180830381865afa1580156127f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128179190614758565b6128219190614745565b60055490915060009061284490600160701b90046001600160701b031683614745565b905060008060006128553285611408565b929850909450909250905061286a878761336d565b8260155461287891906146e4565b60155580600281111561288d5761288d61426b565b600754600160e01b900463ffffffff908116911614612949576001600160701b038211156128cd5760405162461bcd60e51b81526004016108db90614812565b60078054600160701b600160e01b031916600160701b6001600160701b038516021790558060028111156129035761290361426b565b6007601c6101000a81548163ffffffff021916908363ffffffff16021790555060008051602061492f833981519152816040516129409190614834565b60405180910390a15b6015546003546040516370a0823160e01b81523060048201526129cb92916001600160a01b0316906370a0823190602401602060405180830381865afa158015612997573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129bb9190614758565b6129c59190614745565b86612f6b565b60045460035460405160008051602061494f83398151915292611fa5926001600160a01b039182169291169088908b9033908e90614842565b6002546001600160a01b03163314612a545760405162461bcd60e51b8152602060048201526013602482015272105113525397d050d0d154d4d7d11153925151606a1b60448201526064016108db565b620f4240811115612a9d5760405162461bcd60e51b81526020600482015260136024820152721253959053125117d4149250d157d312535255606a1b60448201526064016108db565b601455565b612aaa612dcd565b6001546201000090046001600160a01b03163314612afa5760405162461bcd60e51b815260206004820152600d60248201526c1050d0d154d4d7d11153925151609a1b60448201526064016108db565b601680546015546000909255600154909190612b25906201000090046001600160a01b03168361338e565b6000601555600154612b46906201000090046001600160a01b03168261336d565b6004546040518381526001600160a01b03909116907fe9b6b9af52fdb56b6ac34a0639bcf8e407008374741c7a0e187da490755b6bfc9060200160405180910390a26003546040518281526001600160a01b03909116907fe9b6b9af52fdb56b6ac34a0639bcf8e407008374741c7a0e187da490755b6bfc9060200160405180910390a2505061168f6001600055565b6000611403612be3611707565b613553565b60408051808201825260018152605f60f81b6020808301919091528251808401909352600383526204753560ec1b90830152600091828183612c2930610bd3565b604051602001612c3b93929190614510565b60408051601f198184030181528282528051602091820120838301835260018452603160f81b9382019390935281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81830152808301939093527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608401524660808401523060a0808501919091528251808503909101815260c09093019091528151910120600e819055949350505050565b600080600080600080600080612d04611707565b905080600001519750806020015196508060400151955080606001519450806080015193508060a0015192508060c001516002811115612d4657612d4661426b565b91505090919293949596565b612d5a612dcd565b612d626133ab565b61168f6001600055565b6001600160a01b038381166000818152600d602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b600260005403612e1f5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016108db565b6002600055565b6000670de0b6b3a7640000612e3b838561470d565b612e459190614787565b9392505050565b600081612e3b84670de0b6b3a764000061470d565b6103e88111612eab5760405162461bcd60e51b815260206004820152601660248201527509a929ca8be829a9eaa9ca8be9c9ea8be8a9c9eaa8e960531b60448201526064016108db565b6001600160a01b0382166000908152600c6020526040902054612ecf9082906146e4565b6001600160a01b0383166000908152600c6020526040902055600b54612ef69082906146e4565b600b556040518181526001600160a01b038316907f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968859060200160405180910390a26040518181526001600160a01b0383169060009060008051602061490f833981519152906020015b60405180910390a35050565b6001600160701b038211801590612f8957506001600160701b038111155b612fa55760405162461bcd60e51b81526004016108db90614812565b600580546001600160701b03928316600160701b026001600160e01b03199091169290931691909117919091179055565b600080808460c001516002811115612ff057612ff061426b565b0361300a57612fff8484613652565b9150600190506130c4565b60018460c0015160028111156130225761302261426b565b0361303157612fff8484613679565b600084606001518560a001516130479190614745565b905060008560800151866040015161305f9190614745565b90508185101561308a576130738686613696565b93506002925080841115613085578093505b6130c1565b81850361309d57809350600092506130c1565b6130b0866130ab8488614745565b613652565b6130ba90826146e4565b9350600192505b50505b9250929050565b600080808460c0015160028111156130e5576130e561426b565b036130ff576130f484846136ca565b9150600290506130c4565b60018460c0015160028111156131175761311761426b565b036131b3576000846040015185608001516131329190614745565b905060008560a00151866060015161314a9190614745565b9050818510156131755761315e86866136e9565b93506001925080841115613170578093505b6131ac565b81850361318857809350600092506131ac565b61319b866131968488614745565b6136ca565b6131a590826146e4565b9350600292505b50506130c4565b6131bd8484613714565b946002945092505050565b60028160c0015160028111156131e0576131e061426b565b036132155761320d8160600151826080015183604001516132019190614745565b83516020850151613733565b60a082015250565b60018160c00151600281111561322d5761322d61426b565b036132695761326381604001518260a00151836060015161324e9190614745565b83516132599061386b565b8460200151613733565b60808201525b50565b6000806132798385614787565b90506000613287848361470d565b6132919086614745565b905080156132ad576132a48260016146e4565b92505050610bcd565b509050610bcd565b6001600160a01b0382166000908152600c60205260409020546132d9908290614745565b6001600160a01b0383166000908152600c6020526040902055600b54613300908290614745565b600b556040518181526001600160a01b038316907fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca59060200160405180910390a26040518181526000906001600160a01b0384169060008051602061490f83398151915290602001612f5f565b801561338a5760035461338a906001600160a01b03168383613886565b5050565b801561338a5760045461338a906001600160a01b03168383613886565b6015546003546040516370a0823160e01b8152306004820152600092916001600160a01b0316906370a0823190602401602060405180830381865afa1580156133f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061341c9190614758565b6134269190614745565b601654600480546040516370a0823160e01b815230928101929092529293506000926001600160a01b0316906370a0823190602401602060405180830381865afa158015613478573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061349c9190614758565b6134a69190614745565b90506001600160701b0382118015906134c657506001600160701b038111155b6134e25760405162461bcd60e51b81526004016108db90614812565b6005546001600160701b0316821461351057600580546001600160701b0319166001600160701b0384161790555b600554600160701b90046001600160701b0316811461338a57600580546001600160701b038316600160701b02600160701b600160e01b03199091161790555050565b600060028260c00151600281111561356d5761356d61426b565b036135e257606082015160a08301516000916135a19161358d908061470d565b6135979190614787565b8460600151612e4c565b90506135b1836020015182612e26565b60208401516135c890670de0b6b3a7640000614745565b6135d291906146e4565b9050612e45836000015182612e4c565b60408201516080830151600091613611916135fd908061470d565b6136079190614787565b8460400151612e4c565b9050613621836020015182612e26565b602084015161363890670de0b6b3a7640000614745565b61364291906146e4565b9050612e45836000015182612e26565b6000612e45836080015184608001518461366f876000015161386b565b87602001516138dd565b6000612e45836080015184604001518461366f876000015161386b565b6000612e458360a001518385606001516136b091906146e4565b606086015186516136c09061386b565b8760200151613bef565b6000612e458360a001518460a0015184866000015187602001516138dd565b6000612e45836080015183856040015161370391906146e4565b604086015186516020880151613bef565b6000612e458360a00151846060015184866000015187602001516138dd565b600081600003613758576137478385612e26565b61375190866146e4565b9050613863565b8460000361376857506000613863565b6000808461377785600461470d565b613781919061470d565b90508060000361379b57670de0b6b3a7640000915061381f565b85816137a7828261470d565b6137b19190614787565b036137f4576137ed6ec097ce7bc90715b34b9f1000000000886137d4898561470d565b6137de9190614787565b6137e891906146e4565b613cea565b915061381f565b61381c6ec097ce7bc90715b34b9f1000000000876138128a85614787565b6137de919061470d565b91505b6000670de0b6b3a76400006138476138378286614745565b61384288600261470d565b612e4c565b61385191906146e4565b905061385d8882612e26565b93505050505b949350505050565b6000610bcd826ec097ce7bc90715b34b9f1000000000614787565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526138d8908490613dd2565b505050565b600080861161391f5760405162461bcd60e51b815260206004820152600e60248201526d5441524745545f49535f5a45524f60901b60448201526064016108db565b8360000361392f57506000613be6565b8160000361395f57846139428486612e26565b11613956576139518385612e26565b613958565b845b9050613be6565b670de0b6b3a76400008203613a2b5760008061397b868661470d565b90508060000361398e57600091506139fc565b868161399a828261470d565b6139a49190614787565b036139ce576139b3888061470d565b6139bd888361470d565b6139c79190614787565b91506139fc565b8785816139db8a8a61470d565b6139e59190614787565b6139ef919061470d565b6139f99190614787565b91505b613a0e670de0b6b3a7640000836146e4565b613a18838961470d565b613a229190614787565b92505050613be6565b6000613a37858561470d565b8787613a43828761470d565b613a4d9190614787565b613a57919061470d565b613a6191906146e4565b9050600086613a7885670de0b6b3a7640000614745565b613a82919061470d565b90506000828210613aa257613a978383614745565b915060009050613ab3565b613aac8284614745565b9150600190505b613ac5670de0b6b3a764000083614787565b91506000613b03613ade87670de0b6b3a7640000614745565b613ae990600461470d565b8b613af4898e612e26565b613afe919061470d565b612e26565b9050613b13816137de858061470d565b90506000613b2987670de0b6b3a7640000614745565b613b3490600261470d565b905060008315613b9f57613b488584614745565b905080600003613b9a5760405162461bcd60e51b815260206004820152601960248201527f444f444f4d6174683a2073686f756c64206e6f7420626520300000000000000060448201526064016108db565b613bac565b613ba983866146e4565b90505b6000613bb88284613ea7565b90508b811115613bd2576000975050505050505050613be6565b613bdc818d614745565b9750505050505050505b95945050505050565b6000808611613c315760405162461bcd60e51b815260206004820152600e60248201526d5441524745545f49535f5a45524f60901b60448201526064016108db565b6000613c3d8587614745565b613c47908561470d565b905082600003613c6b57613c63670de0b6b3a764000082614787565b915050613be6565b6000613c8b87613c7b8a8061470d565b613c859190614787565b87612e4c565b90506000613c998583612e26565b90506ec097ce7bc90715b34b9f10000000008382613cbf88670de0b6b3a7640000614745565b613cc991906146e4565b613cd3919061470d565b613cdd9190614787565b9998505050505050505050565b600081600003613cfc57506000919050565b60006001613d0984613ec4565b901c6001901b90506001818481613d2257613d22614771565b048201901c90506001818481613d3a57613d3a614771565b048201901c90506001818481613d5257613d52614771565b048201901c90506001818481613d6a57613d6a614771565b048201901c90506001818481613d8257613d82614771565b048201901c90506001818481613d9a57613d9a614771565b048201901c90506001818481613db257613db2614771565b048201901c9050612e4581828581613dcc57613dcc614771565b04613f58565b6000613e27826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613f6e9092919063ffffffff16565b9050805160001480613e48575080806020019051810190613e4891906148d5565b6138d85760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016108db565b6000612e45613ebe84670de0b6b3a764000061470d565b8361326c565b600080608083901c15613ed957608092831c92015b604083901c15613eeb57604092831c92015b602083901c15613efd57602092831c92015b601083901c15613f0f57601092831c92015b600883901c15613f2157600892831c92015b600483901c15613f3357600492831c92015b600283901c15613f4557600292831c92015b600183901c15610bcd5760010192915050565b6000818310613f675781612e45565b5090919050565b6060613863848460008585600080866001600160a01b03168587604051613f9591906148f2565b60006040518083038185875af1925050503d8060008114613fd2576040519150601f19603f3d011682016040523d82523d6000602084013e613fd7565b606091505b5091509150613fe887838387613ff3565b979650505050505050565b6060831561406257825160000361405b576001600160a01b0385163b61405b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016108db565b5081613863565b61386383838151156140775781518083602001fd5b8060405162461bcd60e51b81526004016108db91906141b7565b6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600060028111156140d9576140d961426b565b905290565b80356001600160a01b0381168114611fbc57600080fd5b801515811461326957600080fd5b60008060008060008060008060006101208a8c03121561412257600080fd5b61412b8a6140de565b985061413960208b016140de565b975061414760408b016140de565b965061415560608b016140de565b955060808a0135945060a08a0135935060c08a0135925060e08a013591506101008a0135614182816140f5565b809150509295985092959850929598565b60005b838110156141ae578181015183820152602001614196565b50506000910152565b60208152600082518060208401526141d6816040850160208701614193565b601f01601f19169190910160400192915050565b600080604083850312156141fd57600080fd5b614206836140de565b946020939093013593505050565b60006020828403121561422657600080fd5b612e45826140de565b60008060006060848603121561424457600080fd5b61424d846140de565b925061425b602085016140de565b9150604084013590509250925092565b634e487b7160e01b600052602160045260246000fd5b6003811061429f57634e487b7160e01b600052602160045260246000fd5b9052565b84815260208101849052608081016142be6040830185614281565b82606083015295945050505050565b6000602082840312156142df57600080fd5b5035919050565b600060e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015161433660c0840182614281565b5092915050565b60008083601f84011261434f57600080fd5b50813567ffffffffffffffff81111561436757600080fd5b6020830191508360208285010111156130c457600080fd5b600080600080600080600060c0888a03121561439a57600080fd5b873596506143aa602089016140de565b95506040880135945060608801359350608088013567ffffffffffffffff8111156143d457600080fd5b6143e08a828b0161433d565b989b979a5095989497959660a090950135949350505050565b60008060008060006080868803121561441157600080fd5b8535945060208601359350614428604087016140de565b9250606086013567ffffffffffffffff81111561444457600080fd5b6144508882890161433d565b969995985093965092949392505050565b60ff8116811461326957600080fd5b600080600080600080600060e0888a03121561448b57600080fd5b614494886140de565b96506144a2602089016140de565b9550604088013594506060880135935060808801356144c081614461565b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156144f057600080fd5b6144f9836140de565b9150614507602084016140de565b90509250929050565b60008451614522818460208901614193565b845190830190614536818360208901614193565b8451910190614549818360208801614193565b0195945050505050565b634e487b7160e01b600052604160045260246000fd5b600181811c9082168061457d57607f821691505b60208210810361459d57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156138d857600081815260208120601f850160051c810160208610156145ca5750805b601f850160051c820191505b818110156145e9578281556001016145d6565b505050505050565b815167ffffffffffffffff81111561460b5761460b614553565b61461f816146198454614569565b846145a3565b602080601f831160018114614654576000841561463c5750858301515b600019600386901b1c1916600185901b1785556145e9565b600085815260208120601f198616915b8281101561468357888601518255948401946001909101908401614664565b50858210156146a15787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6000602082840312156146c357600080fd5b8151612e4581614461565b634e487b7160e01b600052601160045260246000fd5b80820180821115610bcd57610bcd6146ce565b634e487b7160e01b600052603260045260246000fd5b6000816000190483118215151615614727576147276146ce565b500290565b60006001820161473e5761473e6146ce565b5060010190565b81810381811115610bcd57610bcd6146ce565b60006020828403121561476a57600080fd5b5051919050565b634e487b7160e01b600052601260045260246000fd5b6000826147a457634e487b7160e01b600052601260045260246000fd5b500490565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60018060a01b038716815285602082015284604082015283606082015260a06080820152600061480660a0830184866147a9565b98975050505050505050565b6020808252600890820152674f564552464c4f5760c01b604082015260600190565b60208101610bcd8284614281565b6001600160a01b0396871681529486166020860152604085019390935260608401919091528316608083015290911660a082015260c00190565b60018060a01b0386168152846020820152836040820152608060608201526000613fe86080830184866147a9565b60208082526011908201527011931054d217d313d05397d19052531151607a1b604082015260600190565b6000602082840312156148e757600080fd5b8151612e45816140f5565b60008251614904818460208701614193565b919091019291505056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efdf176ad18be4f9f32efaa32f06e9d1175476504739a745f1399a6d3fa4b75917c2c0245e056d5fb095f04cd6373bc770802ebd1e6c918eb78fdef843cdb37b0fa264697066735822122051895c0ab736fc3a00a4811727eba097895a7a6aafd866b9c8cbf018d5eb73f264736f6c63430008100033

Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.