false
true
0

Contract Address Details

0x59cED1f6d84c3C99268A141Acbe2313cB7Ac43fa

Contract Name
NineinchStableSwapTwoPoolInfo
Creator
0x615470–83d2bf at 0x445358–b2921d
Balance
0 PLS ( )
Tokens
Fetching tokens...
Transactions
0 Transactions
Transfers
0 Transfers
Gas Used
Fetching gas used...
Last Balance Update
25939804
Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB 0x7f4070eefc5d7e076defe9690a1c1dbfcd8f855b.
All metadata displayed below is from that contract. In order to verify current contract, click Verify & Publish button
Verify & Publish
Contract name:
NineinchStableSwapTwoPoolInfo




Optimization enabled
true
Compiler version
v0.8.10+commit.fc410830




Optimization runs
1000000
Verified at
2024-05-07T17:53:18.627524Z

contracts/NineinchStableSwapTwoPoolInfo.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./interfaces/INineinchStableSwap.sol";

contract NineinchStableSwapTwoPoolInfo {
    uint256 public constant N_COINS = 2;
    uint256 public constant FEE_DENOMINATOR = 1e10;
    uint256 public constant PRECISION = 1e18;

    function token(address _swap) public view returns (IERC20) {
        return IERC20(INineinchStableSwap(_swap).token());
    }

    function balances(address _swap) public view returns (uint256[N_COINS] memory swapBalances) {
        for (uint256 i = 0; i < N_COINS; i++) {
            swapBalances[i] = INineinchStableSwap(_swap).balances(i);
        }
    }

    function RATES(address _swap) public view returns (uint256[N_COINS] memory swapRATES) {
        for (uint256 i = 0; i < N_COINS; i++) {
            swapRATES[i] = INineinchStableSwap(_swap).RATES(i);
        }
    }

    function PRECISION_MUL(address _swap) public view returns (uint256[N_COINS] memory swapPRECISION_MUL) {
        for (uint256 i = 0; i < N_COINS; i++) {
            swapPRECISION_MUL[i] = INineinchStableSwap(_swap).PRECISION_MUL(i);
        }
    }

    function calc_coins_amount(address _swap, uint256 _amount) external view returns (uint256[N_COINS] memory) {
        uint256 total_supply = token(_swap).totalSupply();
        uint256[N_COINS] memory amounts;

        for (uint256 i = 0; i < N_COINS; i++) {
            uint256 value = (INineinchStableSwap(_swap).balances(i) * _amount) / total_supply;
            amounts[i] = value;
        }
        return amounts;
    }

    function get_D_mem(
        address _swap,
        uint256[N_COINS] memory _balances,
        uint256 amp
    ) public view returns (uint256) {
        return get_D(_xp_mem(_swap, _balances), amp);
    }

    function get_add_liquidity_mint_amount(address _swap, uint256[N_COINS] memory amounts)
        external
        view
        returns (uint256)
    {
        INineinchStableSwap swap = INineinchStableSwap(_swap);
        uint256[N_COINS] memory fees;
        uint256 _fee = (swap.fee() * N_COINS) / (4 * (N_COINS - 1));
        uint256 amp = swap.A();

        uint256 token_supply = token(_swap).totalSupply();
        //Initial invariant
        uint256 D0;
        uint256[N_COINS] memory old_balances = balances(_swap);
        if (token_supply > 0) {
            D0 = get_D_mem(_swap, old_balances, amp);
        }
        uint256[N_COINS] memory new_balances = [old_balances[0], old_balances[1]];

        for (uint256 i = 0; i < N_COINS; i++) {
            if (token_supply == 0) {
                require(amounts[i] > 0, "Initial deposit requires all coins");
            }
            // balances store amounts of c-tokens
            new_balances[i] = old_balances[i] + amounts[i];
        }

        // Invariant after change
        uint256 D1 = get_D_mem(_swap, new_balances, amp);
        require(D1 > D0, "D1 must be greater than D0");

        // We need to recalculate the invariant accounting for fees
        // to calculate fair user's share
        uint256 D2 = D1;
        if (token_supply > 0) {
            // Only account for fees if we are not the first to deposit
            for (uint256 i = 0; i < N_COINS; i++) {
                uint256 ideal_balance = (D1 * old_balances[i]) / D0;
                uint256 difference;
                if (ideal_balance > new_balances[i]) {
                    difference = ideal_balance - new_balances[i];
                } else {
                    difference = new_balances[i] - ideal_balance;
                }

                fees[i] = (_fee * difference) / FEE_DENOMINATOR;
                new_balances[i] -= fees[i];
            }
            D2 = get_D_mem(_swap, new_balances, amp);
        }

        // Calculate, how much pool tokens to mint
        uint256 mint_amount;
        if (token_supply == 0) {
            mint_amount = D1; // Take the dust if there was any
        } else {
            mint_amount = (token_supply * (D2 - D0)) / D0;
        }
        return mint_amount;
    }

    function get_add_liquidity_fee(address _swap, uint256[N_COINS] memory amounts)
        external
        view
        returns (uint256[N_COINS] memory liquidityFee)
    {
        INineinchStableSwap swap = INineinchStableSwap(_swap);
        uint256 _fee = (swap.fee() * N_COINS) / (4 * (N_COINS - 1));
        uint256 _admin_fee = swap.admin_fee();
        uint256 amp = swap.A();

        uint256 token_supply = token(_swap).totalSupply();
        //Initial invariant
        uint256 D0;
        uint256[N_COINS] memory old_balances = balances(_swap);

        if (token_supply > 0) {
            D0 = get_D_mem(_swap, old_balances, amp);
        }
        uint256[N_COINS] memory new_balances = [old_balances[0], old_balances[1]];

        for (uint256 i = 0; i < N_COINS; i++) {
            if (token_supply == 0) {
                require(amounts[i] > 0, "Initial deposit requires all coins");
            }
            new_balances[i] = old_balances[i] + amounts[i];
        }

        // Invariant after change
        uint256 D1 = get_D_mem(_swap, new_balances, amp);
        require(D1 > D0, "D1 must be greater than D0");
        if (token_supply > 0) {
            // Only account for fees if we are not the first to deposit
            for (uint256 i = 0; i < N_COINS; i++) {
                uint256 ideal_balance = (D1 * old_balances[i]) / D0;
                uint256 difference;
                if (ideal_balance > new_balances[i]) {
                    difference = ideal_balance - new_balances[i];
                } else {
                    difference = new_balances[i] - ideal_balance;
                }
                uint256 coinFee;
                coinFee = (_fee * difference) / FEE_DENOMINATOR;
                liquidityFee[i] = ((coinFee * _admin_fee) / FEE_DENOMINATOR);
            }
        }
    }

    function get_remove_liquidity_imbalance_fee(address _swap, uint256[N_COINS] memory amounts)
        external
        view
        returns (uint256[N_COINS] memory liquidityFee)
    {
        INineinchStableSwap swap = INineinchStableSwap(_swap);
        uint256 _fee = (swap.fee() * N_COINS) / (4 * (N_COINS - 1));
        uint256 _admin_fee = swap.admin_fee();
        uint256 amp = swap.A();

        uint256[N_COINS] memory old_balances = balances(_swap);
        uint256[N_COINS] memory new_balances = [old_balances[0], old_balances[1]];
        uint256 D0 = get_D_mem(_swap, old_balances, amp);
        for (uint256 i = 0; i < N_COINS; i++) {
            new_balances[i] -= amounts[i];
        }
        uint256 D1 = get_D_mem(_swap, new_balances, amp);
        for (uint256 i = 0; i < N_COINS; i++) {
            uint256 ideal_balance = (D1 * old_balances[i]) / D0;
            uint256 difference;
            if (ideal_balance > new_balances[i]) {
                difference = ideal_balance - new_balances[i];
            } else {
                difference = new_balances[i] - ideal_balance;
            }
            uint256 coinFee;
            coinFee = (_fee * difference) / FEE_DENOMINATOR;
            liquidityFee[i] = ((coinFee * _admin_fee) / FEE_DENOMINATOR);
        }
    }

    function _xp_mem(address _swap, uint256[N_COINS] memory _balances)
        public
        view
        returns (uint256[N_COINS] memory result)
    {
        result = RATES(_swap);
        for (uint256 i = 0; i < N_COINS; i++) {
            result[i] = (result[i] * _balances[i]) / PRECISION;
        }
    }

    function get_D(uint256[N_COINS] memory xp, uint256 amp) internal pure returns (uint256) {
        uint256 S;
        for (uint256 i = 0; i < N_COINS; i++) {
            S += xp[i];
        }
        if (S == 0) {
            return 0;
        }

        uint256 Dprev;
        uint256 D = S;
        uint256 Ann = amp * N_COINS;
        for (uint256 j = 0; j < 255; j++) {
            uint256 D_P = D;
            for (uint256 k = 0; k < N_COINS; k++) {
                D_P = (D_P * D) / (xp[k] * N_COINS); // If division by 0, this will be borked: only withdrawal will work. And that is good
            }
            Dprev = D;
            D = ((Ann * S + D_P * N_COINS) * D) / ((Ann - 1) * D + (N_COINS + 1) * D_P);
            // Equality with the precision of 1
            if (D > Dprev) {
                if (D - Dprev <= 1) {
                    break;
                }
            } else {
                if (Dprev - D <= 1) {
                    break;
                }
            }
        }
        return D;
    }

    function get_y(
        address _swap,
        uint256 i,
        uint256 j,
        uint256 x,
        uint256[N_COINS] memory xp_
    ) internal view returns (uint256) {
        INineinchStableSwap swap = INineinchStableSwap(_swap);
        uint256 amp = swap.A();
        uint256 D = get_D(xp_, amp);
        uint256 c = D;
        uint256 S_;
        uint256 Ann = amp * N_COINS;

        uint256 _x;
        for (uint256 k = 0; k < N_COINS; k++) {
            if (k == i) {
                _x = x;
            } else if (k != j) {
                _x = xp_[k];
            } else {
                continue;
            }
            S_ += _x;
            c = (c * D) / (_x * N_COINS);
        }
        c = (c * D) / (Ann * N_COINS);
        uint256 b = S_ + D / Ann; // - D
        uint256 y_prev;
        uint256 y = D;

        for (uint256 m = 0; m < 255; m++) {
            y_prev = y;
            y = (y * y + c) / (2 * y + b - D);
            // Equality with the precision of 1
            if (y > y_prev) {
                if (y - y_prev <= 1) {
                    break;
                }
            } else {
                if (y_prev - y <= 1) {
                    break;
                }
            }
        }
        return y;
    }

    function get_exchange_fee(
        address _swap,
        uint256 i,
        uint256 j,
        uint256 dx
    ) external view returns (uint256 exFee, uint256 exAdminFee) {
        INineinchStableSwap swap = INineinchStableSwap(_swap);

        uint256[N_COINS] memory old_balances = balances(_swap);
        uint256[N_COINS] memory xp = _xp_mem(_swap, old_balances);
        uint256[N_COINS] memory rates = RATES(_swap);
        uint256 x = xp[i] + (dx * rates[i]) / PRECISION;
        uint256 y = get_y(_swap, i, j, x, xp);

        uint256 dy = xp[j] - y - 1; //  -1 just in case there were some rounding errors
        uint256 dy_fee = (dy * swap.fee()) / FEE_DENOMINATOR;

        uint256 dy_admin_fee = (dy_fee * swap.admin_fee()) / FEE_DENOMINATOR;
        dy_fee = (dy_fee * PRECISION) / rates[j];
        dy_admin_fee = (dy_admin_fee * PRECISION) / rates[j];
        exFee = dy_fee;
        exAdminFee = dy_admin_fee;
    }

    function _xp(address _swap) internal view returns (uint256[N_COINS] memory result) {
        result = RATES(_swap);
        for (uint256 i = 0; i < N_COINS; i++) {
            result[i] = (result[i] * INineinchStableSwap(_swap).balances(i)) / PRECISION;
        }
    }

    function get_y_D(
        uint256 A_,
        uint256 i,
        uint256[N_COINS] memory xp,
        uint256 D
    ) internal pure returns (uint256) {
        /**
        Calculate x[i] if one reduces D from being calculated for xp to D

        Done by solving quadratic equation iteratively.
        x_1**2 + x1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
        x_1**2 + b*x_1 = c

        x_1 = (x_1**2 + c) / (2*x_1 + b)
        */
        // x in the input is converted to the same price/precision
        require(i < N_COINS, "dev: i above N_COINS");
        uint256 c = D;
        uint256 S_;
        uint256 Ann = A_ * N_COINS;

        uint256 _x;
        for (uint256 k = 0; k < N_COINS; k++) {
            if (k != i) {
                _x = xp[k];
            } else {
                continue;
            }
            S_ += _x;
            c = (c * D) / (_x * N_COINS);
        }
        c = (c * D) / (Ann * N_COINS);
        uint256 b = S_ + D / Ann;
        uint256 y_prev;
        uint256 y = D;

        for (uint256 k = 0; k < 255; k++) {
            y_prev = y;
            y = (y * y + c) / (2 * y + b - D);
            // Equality with the precision of 1
            if (y > y_prev) {
                if (y - y_prev <= 1) {
                    break;
                }
            } else {
                if (y_prev - y <= 1) {
                    break;
                }
            }
        }
        return y;
    }

    function _calc_withdraw_one_coin(
        address _swap,
        uint256 _token_amount,
        uint256 i
    ) internal view returns (uint256, uint256) {
        INineinchStableSwap swap = INineinchStableSwap(_swap);
        uint256 amp = swap.A();
        uint256 _fee = (swap.fee() * N_COINS) / (4 * (N_COINS - 1));
        uint256[N_COINS] memory precisions = PRECISION_MUL(_swap);

        uint256[N_COINS] memory xp = _xp(_swap);

        uint256 D0 = get_D(xp, amp);
        uint256 D1 = D0 - (_token_amount * D0) / (token(_swap).totalSupply());
        uint256[N_COINS] memory xp_reduced = xp;

        uint256 new_y = get_y_D(amp, i, xp, D1);
        uint256 dy_0 = (xp[i] - new_y) / precisions[i]; // w/o fees

        for (uint256 k = 0; k < N_COINS; k++) {
            uint256 dx_expected;
            if (k == i) {
                dx_expected = (xp[k] * D1) / D0 - new_y;
            } else {
                dx_expected = xp[k] - (xp[k] * D1) / D0;
            }
            xp_reduced[k] -= (_fee * dx_expected) / FEE_DENOMINATOR;
        }
        uint256 dy = xp_reduced[i] - get_y_D(amp, i, xp_reduced, D1);
        dy = (dy - 1) / precisions[i]; // Withdraw less to account for rounding errors

        return (dy, dy_0 - dy);
    }

    function get_remove_liquidity_one_coin_fee(
        address _swap,
        uint256 _token_amount,
        uint256 i
    ) external view returns (uint256 adminFee) {
        INineinchStableSwap swap = INineinchStableSwap(_swap);
        (, uint256 dy_fee) = _calc_withdraw_one_coin(_swap, _token_amount, i);
        adminFee = (dy_fee * swap.admin_fee()) / FEE_DENOMINATOR;
    }

    function get_dx(
        address _swap,
        uint256 i,
        uint256 j,
        uint256 dy,
        uint256 max_dx
    ) external view returns (uint256) {
        INineinchStableSwap swap = INineinchStableSwap(_swap);
        uint256[N_COINS] memory old_balances = balances(_swap);
        uint256[N_COINS] memory xp = _xp_mem(_swap, old_balances);

        uint256 dy_with_fee = (dy * FEE_DENOMINATOR) / (FEE_DENOMINATOR - swap.fee());
        require(dy_with_fee < old_balances[j], "Excess balance");
        uint256[N_COINS] memory rates = RATES(_swap);
        uint256 y = xp[j] - (dy_with_fee * rates[j]) / PRECISION;
        uint256 x = get_y(_swap, j, i, y, xp);

        uint256 dx = x - xp[i];

        // Convert all to real units
        dx = (dx * PRECISION) / rates[i] + 1; // +1 for round lose.
        require(dx <= max_dx, "Exchange resulted in fewer coins than expected");
        return dx;
    }
}
        

contracts/interfaces/INineinchStableSwap.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface INineinchStableSwap {
    function token() external view returns (address);

    function balances(uint256 i) external view returns (uint256);

    function N_COINS() external view returns (uint256);

    function RATES(uint256 i) external view returns (uint256);

    function coins(uint256 i) external view returns (address);

    function PRECISION_MUL(uint256 i) external view returns (uint256);

    function fee() external view returns (uint256);

    function admin_fee() external view returns (uint256);

    function A() external view returns (uint256);

    function get_D_mem(uint256[2] memory _balances, uint256 amp) external view returns (uint256);

    function get_y(
        uint256 i,
        uint256 j,
        uint256 x,
        uint256[2] memory xp_
    ) external view returns (uint256);

    function calc_withdraw_one_coin(uint256 _token_amount, uint256 i) external view returns (uint256);
}
          

@openzeppelin/contracts/token/ERC20/IERC20.sol

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

@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol

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

pragma solidity ^0.8.0;

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

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

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

@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol

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

@openzeppelin/contracts/utils/Address.sol

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

Compiler Settings

{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":1000000,"enabled":true},"metadata":{"bytecodeHash":"none"},"libraries":{},"evmVersion":"istanbul"}
              

Contract ABI

[{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"FEE_DENOMINATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"N_COINS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"PRECISION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[2]","name":"swapPRECISION_MUL","internalType":"uint256[2]"}],"name":"PRECISION_MUL","inputs":[{"type":"address","name":"_swap","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[2]","name":"swapRATES","internalType":"uint256[2]"}],"name":"RATES","inputs":[{"type":"address","name":"_swap","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[2]","name":"result","internalType":"uint256[2]"}],"name":"_xp_mem","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256[2]","name":"_balances","internalType":"uint256[2]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[2]","name":"swapBalances","internalType":"uint256[2]"}],"name":"balances","inputs":[{"type":"address","name":"_swap","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[2]","name":"","internalType":"uint256[2]"}],"name":"calc_coins_amount","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"get_D_mem","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256[2]","name":"_balances","internalType":"uint256[2]"},{"type":"uint256","name":"amp","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[2]","name":"liquidityFee","internalType":"uint256[2]"}],"name":"get_add_liquidity_fee","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256[2]","name":"amounts","internalType":"uint256[2]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"get_add_liquidity_mint_amount","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256[2]","name":"amounts","internalType":"uint256[2]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"get_dx","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256","name":"i","internalType":"uint256"},{"type":"uint256","name":"j","internalType":"uint256"},{"type":"uint256","name":"dy","internalType":"uint256"},{"type":"uint256","name":"max_dx","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"exFee","internalType":"uint256"},{"type":"uint256","name":"exAdminFee","internalType":"uint256"}],"name":"get_exchange_fee","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256","name":"i","internalType":"uint256"},{"type":"uint256","name":"j","internalType":"uint256"},{"type":"uint256","name":"dx","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[2]","name":"liquidityFee","internalType":"uint256[2]"}],"name":"get_remove_liquidity_imbalance_fee","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256[2]","name":"amounts","internalType":"uint256[2]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"adminFee","internalType":"uint256"}],"name":"get_remove_liquidity_one_coin_fee","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256","name":"_token_amount","internalType":"uint256"},{"type":"uint256","name":"i","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"token","inputs":[{"type":"address","name":"_swap","internalType":"address"}]}]
              

Contract Creation Code

Verify & Publish
0x608060405234801561001057600080fd5b5061290a806100206000396000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c80636c92118e11610097578063b6f03ac111610066578063b6f03ac114610238578063ca4bc7141461024b578063d73792a91461025e578063f74d92e71461026a57600080fd5b80636c92118e146101cb5780636d46a1db146101de578063aaf5eb6814610216578063ae6452b41461022557600080fd5b8063279a07cc116100d3578063279a07cc1461017c57806327e235e31461018f57806329357750146101a25780635779dcd8146101b857600080fd5b80630d098fa91461010557806315a4d7fe1461012e5780631f38b0c7146101415780632494acbd14610169575b600080fd5b6101186101133660046125df565b61027d565b6040516101259190612615565b60405180910390f35b61011861013c366004612646565b6107ab565b61015461014f366004612672565b61090e565b60408051928352602083019190915201610125565b6101186101773660046126ad565b610b6d565b61011861018a3660046126ad565b610c3f565b61011861019d3660046126ad565b610d0b565b6101aa600281565b604051908152602001610125565b6101aa6101c63660046126d1565b610dd7565b6101186101d93660046125df565b610e7c565b6101f16101ec3660046126ad565b6111e7565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610125565b6101aa670de0b6b3a764000081565b6101186102333660046125df565b611258565b6101aa6102463660046125df565b6112f2565b6101aa610259366004612706565b611808565b6101aa6402540be40081565b6101aa61027836600461274a565b611aab565b610285612505565b826000610294600160026127b8565b61029f9060046127cf565b60028373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610310919061280c565b61031a91906127cf565b6103249190612825565b905060008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610373573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610397919061280c565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061040a919061280c565b90506000610417886111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610461573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610485919061280c565b90506000806104938a610d0b565b905082156104a9576104a68a8286611aab565b91505b60006040518060400160405280836000600281106104c9576104c9612860565b60200201518152602001836001600281106104e6576104e6612860565b60200201519052905060005b600281101561060c57846105ab5760008b826002811061051457610514612860565b6020020151116105ab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f6960448201527f6e7300000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b8a81600281106105bd576105bd612860565b60200201518382600281106105d4576105d4612860565b60200201516105e3919061288f565b8282600281106105f5576105f5612860565b602002015280610604816128a7565b9150506104f2565b50600061061a8c8388611aab565b9050838111610685576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e20443000000000000060448201526064016105a2565b841561079c5760005b600281101561079a576000858583600281106106ac576106ac612860565b60200201516106bb90856127cf565b6106c59190612825565b905060008483600281106106db576106db612860565b602002015182111561070f578483600281106106f9576106f9612860565b602002015161070890836127b8565b9050610734565b8185846002811061072257610722612860565b602002015161073191906127b8565b90505b60006402540be400610746838e6127cf565b6107509190612825565b90506402540be4006107628c836127cf565b61076c9190612825565b8e856002811061077e5761077e612860565b6020020152508291506107929050816128a7565b91505061068e565b505b50505050505050505092915050565b6107b3612505565b60006107be846111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610808573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061082c919061280c565b9050610836612505565b60005b600281101561090357600083868873ffffffffffffffffffffffffffffffffffffffff16634903b0d1856040518263ffffffff1660e01b815260040161088191815260200190565b602060405180830381865afa15801561089e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c2919061280c565b6108cc91906127cf565b6108d69190612825565b9050808383600281106108eb576108eb612860565b602002015250806108fb816128a7565b915050610839565b509150505b92915050565b600080858161091c82610d0b565b9050600061092a8983611258565b905060006109378a610b6d565b90506000670de0b6b3a7640000828b6002811061095657610956612860565b6020020151610965908a6127cf565b61096f9190612825565b838b6002811061098157610981612860565b6020020151610990919061288f565b905060006109a18c8c8c8588611ac8565b90506000600182868d600281106109ba576109ba612860565b60200201516109c991906127b8565b6109d391906127b8565b905060006402540be4008873ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4c919061280c565b610a5690846127cf565b610a609190612825565b905060006402540be4008973ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ab5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad9919061280c565b610ae390846127cf565b610aed9190612825565b9050858d60028110610b0157610b01612860565b6020020151610b18670de0b6b3a7640000846127cf565b610b229190612825565b9150858d60028110610b3657610b36612860565b6020020151610b4d670de0b6b3a7640000836127cf565b610b579190612825565b919f919e50909c50505050505050505050505050565b610b75612505565b60005b6002811015610c39576040517f62203d740000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff8416906362203d7490602401602060405180830381865afa158015610bec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c10919061280c565b828260028110610c2257610c22612860565b602002015280610c31816128a7565b915050610b78565b50919050565b610c47612505565b60005b6002811015610c39576040517f7dafa3640000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff841690637dafa36490602401602060405180830381865afa158015610cbe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ce2919061280c565b828260028110610cf457610cf4612860565b602002015280610d03816128a7565b915050610c4a565b610d13612505565b60005b6002811015610c39576040517f4903b0d10000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff841690634903b0d190602401602060405180830381865afa158015610d8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dae919061280c565b828260028110610dc057610dc0612860565b602002015280610dcf816128a7565b915050610d16565b60008381610de6828686611cd5565b9150506402540be4008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5e919061280c565b610e6890836127cf565b610e729190612825565b9695505050505050565b610e84612505565b826000610e93600160026127b8565b610e9e9060046127cf565b60028373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eeb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f0f919061280c565b610f1991906127cf565b610f239190612825565b905060008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f96919061280c565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fe5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611009919061280c565b9050600061101688610d0b565b9050600060405180604001604052808360006002811061103857611038612860565b602002015181526020018360016002811061105557611055612860565b602002015190529050600061106b8a8486611aab565b905060005b60028110156110c85789816002811061108b5761108b612860565b60200201518382600281106110a2576110a2612860565b602002018181516110b391906127b8565b905250806110c0816128a7565b915050611070565b5060006110d68b8487611aab565b905060005b600281101561079c576000838683600281106110f9576110f9612860565b602002015161110890856127cf565b6111129190612825565b9050600085836002811061112857611128612860565b602002015182111561115c5785836002811061114657611146612860565b602002015161115590836127b8565b9050611181565b8186846002811061116f5761116f612860565b602002015161117e91906127b8565b90505b60006402540be400611193838d6127cf565b61119d9190612825565b90506402540be4006111af8b836127cf565b6111b99190612825565b8d85600281106111cb576111cb612860565b6020020152508291506111df9050816128a7565b9150506110db565b60008173ffffffffffffffffffffffffffffffffffffffff1663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611234573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061090891906128e0565b611260612505565b61126983610b6d565b905060005b60028110156112eb57670de0b6b3a764000083826002811061129257611292612860565b60200201518383600281106112a9576112a9612860565b60200201516112b891906127cf565b6112c29190612825565b8282600281106112d4576112d4612860565b6020020152806112e3816128a7565b91505061126e565b5092915050565b6000826112fd612505565b600061130b600160026127b8565b6113169060046127cf565b60028473ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611363573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611387919061280c565b61139191906127cf565b61139b9190612825565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061140e919061280c565b9050600061141b886111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611465573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611489919061280c565b90506000806114978a610d0b565b905082156114ad576114aa8a8286611aab565b91505b60006040518060400160405280836000600281106114cd576114cd612860565b60200201518152602001836001600281106114ea576114ea612860565b60200201519052905060005b600281101561160b57846115aa5760008b826002811061151857611518612860565b6020020151116115aa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f6960448201527f6e7300000000000000000000000000000000000000000000000000000000000060648201526084016105a2565b8a81600281106115bc576115bc612860565b60200201518382600281106115d3576115d3612860565b60200201516115e2919061288f565b8282600281106115f4576115f4612860565b602002015280611603816128a7565b9150506114f6565b5060006116198c8388611aab565b9050838111611684576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e20443000000000000060448201526064016105a2565b8085156117c75760005b60028110156117b8576000868683600281106116ac576116ac612860565b60200201516116bb90866127cf565b6116c59190612825565b905060008583600281106116db576116db612860565b602002015182111561170f578583600281106116f9576116f9612860565b602002015161170890836127b8565b9050611734565b8186846002811061172257611722612860565b602002015161173191906127b8565b90505b6402540be400611744828d6127cf565b61174e9190612825565b8c846002811061176057611760612860565b60200201528b836002811061177757611777612860565b602002015186846002811061178e5761178e612860565b6020020181815161179f91906127b8565b9052508291506117b09050816128a7565b91505061168e565b506117c48d8489611aab565b90505b6000866117d55750816117f7565b856117e081846127b8565b6117ea90896127cf565b6117f49190612825565b90505b9d9c50505050505050505050505050565b6000858161181582610d0b565b905060006118238983611258565b905060008373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611872573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611896919061280c565b6118a5906402540be4006127b8565b6118b46402540be400896127cf565b6118be9190612825565b90508288600281106118d2576118d2612860565b6020020151811061193f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f4578636573732062616c616e636500000000000000000000000000000000000060448201526064016105a2565b600061194a8b610b6d565b90506000670de0b6b3a7640000828b6002811061196957611969612860565b602002015161197890856127cf565b6119829190612825565b848b6002811061199457611994612860565b60200201516119a391906127b8565b905060006119b48d8c8e8589611ac8565b90506000858d600281106119ca576119ca612860565b60200201516119d990836127b8565b9050838d600281106119ed576119ed612860565b6020020151611a04670de0b6b3a7640000836127cf565b611a0e9190612825565b611a1990600161288f565b9050898111156117f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f45786368616e676520726573756c74656420696e20666577657220636f696e7360448201527f207468616e20657870656374656400000000000000000000000000000000000060648201526084016105a2565b6000611ac0611aba8585611258565b83612085565b949350505050565b60008086905060008173ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b3f919061280c565b90506000611b4d8583612085565b905080600080611b5e6002866127cf565b90506000805b6002811015611be7578c811415611b7d578a9150611ba7565b8b8114611ba257898160028110611b9657611b96612860565b60200201519150611ba7565b611bd5565b611bb1828561288f565b9350611bbe6002836127cf565b611bc887876127cf565b611bd29190612825565b94505b80611bdf816128a7565b915050611b64565b50611bf36002836127cf565b611bfd86866127cf565b611c079190612825565b93506000611c158387612825565b611c1f908561288f565b9050600086815b60ff811015611cc1578192508884836002611c4191906127cf565b611c4b919061288f565b611c5591906127b8565b88611c6084806127cf565b611c6a919061288f565b611c749190612825565b915082821115611c99576001611c8a84846127b8565b11611c9457611cc1565b611caf565b6001611ca583856127b8565b11611caf57611cc1565b80611cb9816128a7565b915050611c26565b509f9e505050505050505050505050505050565b600080600085905060008173ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d4e919061280c565b90506000611d5e600160026127b8565b611d699060046127cf565b60028473ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611db6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dda919061280c565b611de491906127cf565b611dee9190612825565b90506000611dfb89610c3f565b90506000611e088a61221b565b90506000611e168286612085565b90506000611e238c6111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e91919061280c565b611e9b838d6127cf565b611ea59190612825565b611eaf90836127b8565b9050826000611ec0888d8486612327565b90506000868d60028110611ed657611ed6612860565b602002015182878f60028110611eee57611eee612860565b6020020151611efd91906127b8565b611f079190612825565b905060005b60028110156120035760008e821415611f5e578387878a8560028110611f3457611f34612860565b6020020151611f4391906127cf565b611f4d9190612825565b611f5791906127b8565b9050611faf565b8686898460028110611f7257611f72612860565b6020020151611f8191906127cf565b611f8b9190612825565b888360028110611f9d57611f9d612860565b6020020151611fac91906127b8565b90505b6402540be400611fbf828c6127cf565b611fc99190612825565b858360028110611fdb57611fdb612860565b60200201818151611fec91906127b8565b905250819050611ffb816128a7565b915050611f0c565b5060006120128a8f8688612327565b848f6002811061202457612024612860565b602002015161203391906127b8565b9050878e6002811061204757612047612860565b60200201516120576001836127b8565b6120619190612825565b90508061206e81846127b8565b9c509c505050505050505050505050935093915050565b60008060005b60028110156120c9578481600281106120a6576120a6612860565b60200201516120b5908361288f565b9150806120c1816128a7565b91505061208b565b50806120d9576000915050610908565b600081816120e86002876127cf565b905060005b60ff81101561220f578260005b600281101561214e5760028a826002811061211757612117612860565b602002015161212691906127cf565b61213086846127cf565b61213a9190612825565b915080612146816128a7565b9150506120fa565b508394508060026001612161919061288f565b61216b91906127cf565b846121776001866127b8565b61218191906127cf565b61218b919061288f565b846121976002846127cf565b6121a189876127cf565b6121ab919061288f565b6121b591906127cf565b6121bf9190612825565b9350848411156121e55760016121d586866127b8565b116121e0575061220f565b6121fc565b60016121f185876127b8565b116121fc575061220f565b5080612207816128a7565b9150506120ed565b50909695505050505050565b612223612505565b61222c82610b6d565b905060005b6002811015610c39576040517f4903b0d100000000000000000000000000000000000000000000000000000000815260048101829052670de0b6b3a76400009073ffffffffffffffffffffffffffffffffffffffff851690634903b0d190602401602060405180830381865afa1580156122af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122d3919061280c565b8383600281106122e5576122e5612860565b60200201516122f491906127cf565b6122fe9190612825565b82826002811061231057612310612860565b60200201528061231f816128a7565b915050612231565b600060028410612393576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6465763a20692061626f7665204e5f434f494e5300000000000000000000000060448201526064016105a2565b816000806123a26002896127cf565b90506000805b600281101561241b578881146123d6578781600281106123ca576123ca612860565b602002015191506123db565b612409565b6123e5828561288f565b93506123f26002836127cf565b6123fc88876127cf565b6124069190612825565b94505b80612413816128a7565b9150506123a8565b506124276002836127cf565b61243187866127cf565b61243b9190612825565b935060006124498388612825565b612453908561288f565b9050600087815b60ff8110156124f557819250898483600261247591906127cf565b61247f919061288f565b61248991906127b8565b8861249484806127cf565b61249e919061288f565b6124a89190612825565b9150828211156124cd5760016124be84846127b8565b116124c8576124f5565b6124e3565b60016124d983856127b8565b116124e3576124f5565b806124ed816128a7565b91505061245a565b509b9a5050505050505050505050565b60405180604001604052806002906020820280368337509192915050565b73ffffffffffffffffffffffffffffffffffffffff8116811461254557600080fd5b50565b600082601f83011261255957600080fd5b6040516040810181811067ffffffffffffffff821117156125a3577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b80604052508060408401858111156125ba57600080fd5b845b818110156125d45780358352602092830192016125bc565b509195945050505050565b600080606083850312156125f257600080fd5b82356125fd81612523565b915061260c8460208501612548565b90509250929050565b60408101818360005b600281101561263d57815183526020928301929091019060010161261e565b50505092915050565b6000806040838503121561265957600080fd5b823561266481612523565b946020939093013593505050565b6000806000806080858703121561268857600080fd5b843561269381612523565b966020860135965060408601359560600135945092505050565b6000602082840312156126bf57600080fd5b81356126ca81612523565b9392505050565b6000806000606084860312156126e657600080fd5b83356126f181612523565b95602085013595506040909401359392505050565b600080600080600060a0868803121561271e57600080fd5b853561272981612523565b97602087013597506040870135966060810135965060800135945092505050565b60008060006080848603121561275f57600080fd5b833561276a81612523565b92506127798560208601612548565b9150606084013590509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156127ca576127ca612789565b500390565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561280757612807612789565b500290565b60006020828403121561281e57600080fd5b5051919050565b60008261285b577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082198211156128a2576128a2612789565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156128d9576128d9612789565b5060010190565b6000602082840312156128f257600080fd5b81516126ca8161252356fea164736f6c634300080a000a

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106101005760003560e01c80636c92118e11610097578063b6f03ac111610066578063b6f03ac114610238578063ca4bc7141461024b578063d73792a91461025e578063f74d92e71461026a57600080fd5b80636c92118e146101cb5780636d46a1db146101de578063aaf5eb6814610216578063ae6452b41461022557600080fd5b8063279a07cc116100d3578063279a07cc1461017c57806327e235e31461018f57806329357750146101a25780635779dcd8146101b857600080fd5b80630d098fa91461010557806315a4d7fe1461012e5780631f38b0c7146101415780632494acbd14610169575b600080fd5b6101186101133660046125df565b61027d565b6040516101259190612615565b60405180910390f35b61011861013c366004612646565b6107ab565b61015461014f366004612672565b61090e565b60408051928352602083019190915201610125565b6101186101773660046126ad565b610b6d565b61011861018a3660046126ad565b610c3f565b61011861019d3660046126ad565b610d0b565b6101aa600281565b604051908152602001610125565b6101aa6101c63660046126d1565b610dd7565b6101186101d93660046125df565b610e7c565b6101f16101ec3660046126ad565b6111e7565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610125565b6101aa670de0b6b3a764000081565b6101186102333660046125df565b611258565b6101aa6102463660046125df565b6112f2565b6101aa610259366004612706565b611808565b6101aa6402540be40081565b6101aa61027836600461274a565b611aab565b610285612505565b826000610294600160026127b8565b61029f9060046127cf565b60028373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610310919061280c565b61031a91906127cf565b6103249190612825565b905060008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610373573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610397919061280c565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061040a919061280c565b90506000610417886111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610461573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610485919061280c565b90506000806104938a610d0b565b905082156104a9576104a68a8286611aab565b91505b60006040518060400160405280836000600281106104c9576104c9612860565b60200201518152602001836001600281106104e6576104e6612860565b60200201519052905060005b600281101561060c57846105ab5760008b826002811061051457610514612860565b6020020151116105ab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f6960448201527f6e7300000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b8a81600281106105bd576105bd612860565b60200201518382600281106105d4576105d4612860565b60200201516105e3919061288f565b8282600281106105f5576105f5612860565b602002015280610604816128a7565b9150506104f2565b50600061061a8c8388611aab565b9050838111610685576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e20443000000000000060448201526064016105a2565b841561079c5760005b600281101561079a576000858583600281106106ac576106ac612860565b60200201516106bb90856127cf565b6106c59190612825565b905060008483600281106106db576106db612860565b602002015182111561070f578483600281106106f9576106f9612860565b602002015161070890836127b8565b9050610734565b8185846002811061072257610722612860565b602002015161073191906127b8565b90505b60006402540be400610746838e6127cf565b6107509190612825565b90506402540be4006107628c836127cf565b61076c9190612825565b8e856002811061077e5761077e612860565b6020020152508291506107929050816128a7565b91505061068e565b505b50505050505050505092915050565b6107b3612505565b60006107be846111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610808573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061082c919061280c565b9050610836612505565b60005b600281101561090357600083868873ffffffffffffffffffffffffffffffffffffffff16634903b0d1856040518263ffffffff1660e01b815260040161088191815260200190565b602060405180830381865afa15801561089e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c2919061280c565b6108cc91906127cf565b6108d69190612825565b9050808383600281106108eb576108eb612860565b602002015250806108fb816128a7565b915050610839565b509150505b92915050565b600080858161091c82610d0b565b9050600061092a8983611258565b905060006109378a610b6d565b90506000670de0b6b3a7640000828b6002811061095657610956612860565b6020020151610965908a6127cf565b61096f9190612825565b838b6002811061098157610981612860565b6020020151610990919061288f565b905060006109a18c8c8c8588611ac8565b90506000600182868d600281106109ba576109ba612860565b60200201516109c991906127b8565b6109d391906127b8565b905060006402540be4008873ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4c919061280c565b610a5690846127cf565b610a609190612825565b905060006402540be4008973ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ab5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad9919061280c565b610ae390846127cf565b610aed9190612825565b9050858d60028110610b0157610b01612860565b6020020151610b18670de0b6b3a7640000846127cf565b610b229190612825565b9150858d60028110610b3657610b36612860565b6020020151610b4d670de0b6b3a7640000836127cf565b610b579190612825565b919f919e50909c50505050505050505050505050565b610b75612505565b60005b6002811015610c39576040517f62203d740000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff8416906362203d7490602401602060405180830381865afa158015610bec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c10919061280c565b828260028110610c2257610c22612860565b602002015280610c31816128a7565b915050610b78565b50919050565b610c47612505565b60005b6002811015610c39576040517f7dafa3640000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff841690637dafa36490602401602060405180830381865afa158015610cbe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ce2919061280c565b828260028110610cf457610cf4612860565b602002015280610d03816128a7565b915050610c4a565b610d13612505565b60005b6002811015610c39576040517f4903b0d10000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff841690634903b0d190602401602060405180830381865afa158015610d8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dae919061280c565b828260028110610dc057610dc0612860565b602002015280610dcf816128a7565b915050610d16565b60008381610de6828686611cd5565b9150506402540be4008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5e919061280c565b610e6890836127cf565b610e729190612825565b9695505050505050565b610e84612505565b826000610e93600160026127b8565b610e9e9060046127cf565b60028373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eeb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f0f919061280c565b610f1991906127cf565b610f239190612825565b905060008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f96919061280c565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fe5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611009919061280c565b9050600061101688610d0b565b9050600060405180604001604052808360006002811061103857611038612860565b602002015181526020018360016002811061105557611055612860565b602002015190529050600061106b8a8486611aab565b905060005b60028110156110c85789816002811061108b5761108b612860565b60200201518382600281106110a2576110a2612860565b602002018181516110b391906127b8565b905250806110c0816128a7565b915050611070565b5060006110d68b8487611aab565b905060005b600281101561079c576000838683600281106110f9576110f9612860565b602002015161110890856127cf565b6111129190612825565b9050600085836002811061112857611128612860565b602002015182111561115c5785836002811061114657611146612860565b602002015161115590836127b8565b9050611181565b8186846002811061116f5761116f612860565b602002015161117e91906127b8565b90505b60006402540be400611193838d6127cf565b61119d9190612825565b90506402540be4006111af8b836127cf565b6111b99190612825565b8d85600281106111cb576111cb612860565b6020020152508291506111df9050816128a7565b9150506110db565b60008173ffffffffffffffffffffffffffffffffffffffff1663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611234573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061090891906128e0565b611260612505565b61126983610b6d565b905060005b60028110156112eb57670de0b6b3a764000083826002811061129257611292612860565b60200201518383600281106112a9576112a9612860565b60200201516112b891906127cf565b6112c29190612825565b8282600281106112d4576112d4612860565b6020020152806112e3816128a7565b91505061126e565b5092915050565b6000826112fd612505565b600061130b600160026127b8565b6113169060046127cf565b60028473ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611363573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611387919061280c565b61139191906127cf565b61139b9190612825565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061140e919061280c565b9050600061141b886111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611465573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611489919061280c565b90506000806114978a610d0b565b905082156114ad576114aa8a8286611aab565b91505b60006040518060400160405280836000600281106114cd576114cd612860565b60200201518152602001836001600281106114ea576114ea612860565b60200201519052905060005b600281101561160b57846115aa5760008b826002811061151857611518612860565b6020020151116115aa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f6960448201527f6e7300000000000000000000000000000000000000000000000000000000000060648201526084016105a2565b8a81600281106115bc576115bc612860565b60200201518382600281106115d3576115d3612860565b60200201516115e2919061288f565b8282600281106115f4576115f4612860565b602002015280611603816128a7565b9150506114f6565b5060006116198c8388611aab565b9050838111611684576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e20443000000000000060448201526064016105a2565b8085156117c75760005b60028110156117b8576000868683600281106116ac576116ac612860565b60200201516116bb90866127cf565b6116c59190612825565b905060008583600281106116db576116db612860565b602002015182111561170f578583600281106116f9576116f9612860565b602002015161170890836127b8565b9050611734565b8186846002811061172257611722612860565b602002015161173191906127b8565b90505b6402540be400611744828d6127cf565b61174e9190612825565b8c846002811061176057611760612860565b60200201528b836002811061177757611777612860565b602002015186846002811061178e5761178e612860565b6020020181815161179f91906127b8565b9052508291506117b09050816128a7565b91505061168e565b506117c48d8489611aab565b90505b6000866117d55750816117f7565b856117e081846127b8565b6117ea90896127cf565b6117f49190612825565b90505b9d9c50505050505050505050505050565b6000858161181582610d0b565b905060006118238983611258565b905060008373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611872573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611896919061280c565b6118a5906402540be4006127b8565b6118b46402540be400896127cf565b6118be9190612825565b90508288600281106118d2576118d2612860565b6020020151811061193f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f4578636573732062616c616e636500000000000000000000000000000000000060448201526064016105a2565b600061194a8b610b6d565b90506000670de0b6b3a7640000828b6002811061196957611969612860565b602002015161197890856127cf565b6119829190612825565b848b6002811061199457611994612860565b60200201516119a391906127b8565b905060006119b48d8c8e8589611ac8565b90506000858d600281106119ca576119ca612860565b60200201516119d990836127b8565b9050838d600281106119ed576119ed612860565b6020020151611a04670de0b6b3a7640000836127cf565b611a0e9190612825565b611a1990600161288f565b9050898111156117f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f45786368616e676520726573756c74656420696e20666577657220636f696e7360448201527f207468616e20657870656374656400000000000000000000000000000000000060648201526084016105a2565b6000611ac0611aba8585611258565b83612085565b949350505050565b60008086905060008173ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b3f919061280c565b90506000611b4d8583612085565b905080600080611b5e6002866127cf565b90506000805b6002811015611be7578c811415611b7d578a9150611ba7565b8b8114611ba257898160028110611b9657611b96612860565b60200201519150611ba7565b611bd5565b611bb1828561288f565b9350611bbe6002836127cf565b611bc887876127cf565b611bd29190612825565b94505b80611bdf816128a7565b915050611b64565b50611bf36002836127cf565b611bfd86866127cf565b611c079190612825565b93506000611c158387612825565b611c1f908561288f565b9050600086815b60ff811015611cc1578192508884836002611c4191906127cf565b611c4b919061288f565b611c5591906127b8565b88611c6084806127cf565b611c6a919061288f565b611c749190612825565b915082821115611c99576001611c8a84846127b8565b11611c9457611cc1565b611caf565b6001611ca583856127b8565b11611caf57611cc1565b80611cb9816128a7565b915050611c26565b509f9e505050505050505050505050505050565b600080600085905060008173ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d4e919061280c565b90506000611d5e600160026127b8565b611d699060046127cf565b60028473ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611db6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dda919061280c565b611de491906127cf565b611dee9190612825565b90506000611dfb89610c3f565b90506000611e088a61221b565b90506000611e168286612085565b90506000611e238c6111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e91919061280c565b611e9b838d6127cf565b611ea59190612825565b611eaf90836127b8565b9050826000611ec0888d8486612327565b90506000868d60028110611ed657611ed6612860565b602002015182878f60028110611eee57611eee612860565b6020020151611efd91906127b8565b611f079190612825565b905060005b60028110156120035760008e821415611f5e578387878a8560028110611f3457611f34612860565b6020020151611f4391906127cf565b611f4d9190612825565b611f5791906127b8565b9050611faf565b8686898460028110611f7257611f72612860565b6020020151611f8191906127cf565b611f8b9190612825565b888360028110611f9d57611f9d612860565b6020020151611fac91906127b8565b90505b6402540be400611fbf828c6127cf565b611fc99190612825565b858360028110611fdb57611fdb612860565b60200201818151611fec91906127b8565b905250819050611ffb816128a7565b915050611f0c565b5060006120128a8f8688612327565b848f6002811061202457612024612860565b602002015161203391906127b8565b9050878e6002811061204757612047612860565b60200201516120576001836127b8565b6120619190612825565b90508061206e81846127b8565b9c509c505050505050505050505050935093915050565b60008060005b60028110156120c9578481600281106120a6576120a6612860565b60200201516120b5908361288f565b9150806120c1816128a7565b91505061208b565b50806120d9576000915050610908565b600081816120e86002876127cf565b905060005b60ff81101561220f578260005b600281101561214e5760028a826002811061211757612117612860565b602002015161212691906127cf565b61213086846127cf565b61213a9190612825565b915080612146816128a7565b9150506120fa565b508394508060026001612161919061288f565b61216b91906127cf565b846121776001866127b8565b61218191906127cf565b61218b919061288f565b846121976002846127cf565b6121a189876127cf565b6121ab919061288f565b6121b591906127cf565b6121bf9190612825565b9350848411156121e55760016121d586866127b8565b116121e0575061220f565b6121fc565b60016121f185876127b8565b116121fc575061220f565b5080612207816128a7565b9150506120ed565b50909695505050505050565b612223612505565b61222c82610b6d565b905060005b6002811015610c39576040517f4903b0d100000000000000000000000000000000000000000000000000000000815260048101829052670de0b6b3a76400009073ffffffffffffffffffffffffffffffffffffffff851690634903b0d190602401602060405180830381865afa1580156122af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122d3919061280c565b8383600281106122e5576122e5612860565b60200201516122f491906127cf565b6122fe9190612825565b82826002811061231057612310612860565b60200201528061231f816128a7565b915050612231565b600060028410612393576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6465763a20692061626f7665204e5f434f494e5300000000000000000000000060448201526064016105a2565b816000806123a26002896127cf565b90506000805b600281101561241b578881146123d6578781600281106123ca576123ca612860565b602002015191506123db565b612409565b6123e5828561288f565b93506123f26002836127cf565b6123fc88876127cf565b6124069190612825565b94505b80612413816128a7565b9150506123a8565b506124276002836127cf565b61243187866127cf565b61243b9190612825565b935060006124498388612825565b612453908561288f565b9050600087815b60ff8110156124f557819250898483600261247591906127cf565b61247f919061288f565b61248991906127b8565b8861249484806127cf565b61249e919061288f565b6124a89190612825565b9150828211156124cd5760016124be84846127b8565b116124c8576124f5565b6124e3565b60016124d983856127b8565b116124e3576124f5565b806124ed816128a7565b91505061245a565b509b9a5050505050505050505050565b60405180604001604052806002906020820280368337509192915050565b73ffffffffffffffffffffffffffffffffffffffff8116811461254557600080fd5b50565b600082601f83011261255957600080fd5b6040516040810181811067ffffffffffffffff821117156125a3577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b80604052508060408401858111156125ba57600080fd5b845b818110156125d45780358352602092830192016125bc565b509195945050505050565b600080606083850312156125f257600080fd5b82356125fd81612523565b915061260c8460208501612548565b90509250929050565b60408101818360005b600281101561263d57815183526020928301929091019060010161261e565b50505092915050565b6000806040838503121561265957600080fd5b823561266481612523565b946020939093013593505050565b6000806000806080858703121561268857600080fd5b843561269381612523565b966020860135965060408601359560600135945092505050565b6000602082840312156126bf57600080fd5b81356126ca81612523565b9392505050565b6000806000606084860312156126e657600080fd5b83356126f181612523565b95602085013595506040909401359392505050565b600080600080600060a0868803121561271e57600080fd5b853561272981612523565b97602087013597506040870135966060810135965060800135945092505050565b60008060006080848603121561275f57600080fd5b833561276a81612523565b92506127798560208601612548565b9150606084013590509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156127ca576127ca612789565b500390565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561280757612807612789565b500290565b60006020828403121561281e57600080fd5b5051919050565b60008261285b577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082198211156128a2576128a2612789565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156128d9576128d9612789565b5060010190565b6000602082840312156128f257600080fd5b81516126ca8161252356fea164736f6c634300080a000a