ETH Price: $2,814.71 (-4.56%)

Token

Stable2 (S2)

Overview

Max Total Supply

0 S2

Holders

0

Transfers

-
0

Market

Price

$0.00 @ 0.000000 ETH

Onchain Market Cap

-

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 0 Decimals)

Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information

Contract Source Code Verified (Exact Match)

Contract Name:
Stable2

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 200 runs

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

pragma solidity ^0.8.20;

import {IBeanstalkWellFunction, IMultiFlowPumpWellFunction} from "src/interfaces/IBeanstalkWellFunction.sol";
import {ILookupTable} from "src/interfaces/ILookupTable.sol";
import {ProportionalLPToken2} from "src/functions/ProportionalLPToken2.sol";
import {IERC20} from "forge-std/interfaces/IERC20.sol";

/**
 * @author brean, deadmanwalking
 * @title Gas efficient Like-valued token pricing function for Wells with 2 tokens.
 *
 * Stableswap Wells with 2 tokens use the formula:
 *  `4 * A * (b_0+b_1) + D = 4 * A * D + D^3/(4 * b_0 * b_1)`
 *
 * Where:
 *  `A` is the Amplication parameter.
 *  `D` is the supply of LP tokens
 *  `b_i` is the reserve at index `i`
 *
 * @dev Limited to tokens with a maximum of 18 decimals.
 */
contract Stable2 is ProportionalLPToken2, IBeanstalkWellFunction {
    struct PriceData {
        uint256 targetPrice;
        uint256 currentPrice;
        uint256 newPrice;
        uint256 maxStepSize;
        ILookupTable.PriceData lutData;
    }

    // 2 token Pool.
    uint256 constant N = 2;

    // A precision
    uint256 constant A_PRECISION = 100;

    // price precision.
    uint256 constant PRICE_PRECISION = 1e6;

    // price threshold. more accurate pricing requires a lower threshold,
    // at the cost of higher execution costs.
    uint256 constant PRICE_THRESHOLD = 10; // 0.001%

    address immutable lookupTable;
    uint256 immutable a;

    // Errors
    error InvalidTokenDecimals();
    error InvalidLUT();

    // Due to the complexity of `calcReserveAtRatioLiquidity` and `calcReserveAtRatioSwap`,
    // a LUT is used to reduce the complexity of the calculations on chain.
    // the lookup table contract implements 3 functions:
    // 1. getRatiosFromPriceLiquidity(uint256) -> PriceData memory
    // 2. getRatiosFromPriceSwap(uint256) -> PriceData memory
    // 3. getAParameter() -> uint256
    // Lookup tables are a function of the A parameter.
    constructor(
        address lut
    ) {
        if (lut == address(0)) revert InvalidLUT();
        lookupTable = lut;
        a = ILookupTable(lut).getAParameter();
    }

    /**
     * @notice Calculate the amount of lp tokens given reserves.
     * D invariant calculation in non-overflowing integer operations iteratively
     * A * sum(x_i) * n**n + D = A * D * n**n + D**(n+1) / (n**n * prod(x_i))
     *
     * Converging solution:
     * D[j+1] = (4 * A * sum(b_i) - (D[j] ** 3) / (4 * prod(b_i))) / (4 * A - 1)
     */
    function calcLpTokenSupply(
        uint256[] memory reserves,
        bytes memory data
    ) public view returns (uint256 lpTokenSupply) {
        if (reserves[0] == 0 && reserves[1] == 0) return 0;
        uint256[] memory decimals = decodeWellData(data);
        // scale reserves to 18 decimals.
        uint256[] memory scaledReserves = getScaledReserves(reserves, decimals);

        uint256 Ann = a * N * N;

        uint256 sumReserves = scaledReserves[0] + scaledReserves[1];
        lpTokenSupply = sumReserves;
        for (uint256 i = 0; i < 255; i++) {
            bool stableOscillation;
            uint256 dP = lpTokenSupply;
            // If division by 0, this will be borked: only withdrawal will work. And that is good
            dP = dP * lpTokenSupply / (scaledReserves[0] * N);
            dP = dP * lpTokenSupply / (scaledReserves[1] * N);
            uint256 prevReserves = lpTokenSupply;
            lpTokenSupply = (Ann * sumReserves / A_PRECISION + (dP * N)) * lpTokenSupply
                / (((Ann - A_PRECISION) * lpTokenSupply / A_PRECISION) + ((N + 1) * dP));

            // Equality with the precision of 1
            // If the difference between the current lpTokenSupply and the previous lpTokenSupply is 2,
            // Check that the oscillation is stable, and if so, return the average between the two.
            if (lpTokenSupply > prevReserves) {
                if (lpTokenSupply - prevReserves == 2) {
                    if (stableOscillation) {
                        return lpTokenSupply - 1;
                    }
                    stableOscillation = true;
                }
                if (lpTokenSupply - prevReserves <= 1) return lpTokenSupply;
            } else {
                if (prevReserves - lpTokenSupply == 2) {
                    if (stableOscillation) {
                        return lpTokenSupply + 1;
                    }
                    stableOscillation = true;
                }
                if (prevReserves - lpTokenSupply <= 1) return lpTokenSupply;
            }
        }
        revert("Non convergence: calcLpTokenSupply");
    }

    /**
     * @notice Calculate x[i] if one reduces D from being calculated for reserves to D
     * Done by solving quadratic equation iteratively.
     * x_1**2 + x_1 * (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)
     * @dev This function has a precision of +/- 1,
     * which may round in favor of the well or the user.
     */
    function calcReserve(
        uint256[] memory reserves,
        uint256 j,
        uint256 lpTokenSupply,
        bytes memory data
    ) public view returns (uint256 reserve) {
        uint256[] memory decimals = decodeWellData(data);
        uint256[] memory scaledReserves = getScaledReserves(reserves, decimals);

        // avoid stack too deep errors.
        (uint256 c, uint256 b) = getBandC(a * N * N, lpTokenSupply, j == 0 ? scaledReserves[1] : scaledReserves[0]);
        reserve = lpTokenSupply;
        uint256 prevReserve;

        for (uint256 i; i < 255; ++i) {
            prevReserve = reserve;
            reserve = _calcReserve(reserve, b, c, lpTokenSupply);
            // Equality with the precision of 1
            // scale reserve down to original precision
            if (reserve > prevReserve) {
                if (reserve - prevReserve <= 1) {
                    return reserve / (10 ** (18 - decimals[j]));
                }
            } else {
                if (prevReserve - reserve <= 1) {
                    return reserve / (10 ** (18 - decimals[j]));
                }
            }
        }
        revert("Non convergence: calcReserve");
    }

    /**
     * @inheritdoc IMultiFlowPumpWellFunction
     * @dev Returns a rate with  decimal precision.
     * Requires a minimum scaled reserves of 1e12.
     * 6 decimals was chosen as higher decimals would require a higher minimum scaled reserve,
     * which is prohibtive for large value tokens.
     */
    function calcRate(
        uint256[] memory reserves,
        uint256 i,
        uint256 j,
        bytes memory data
    ) public view returns (uint256 rate) {
        uint256[] memory decimals = decodeWellData(data);
        uint256[] memory scaledReserves = getScaledReserves(reserves, decimals);

        // calc lp token supply (note: `scaledReserves` is scaled up, and does not require bytes).
        uint256 lpTokenSupply = calcLpTokenSupply(scaledReserves, abi.encode(18, 18));

        rate = _calcRate(scaledReserves, i, j, lpTokenSupply);
    }

    /**
     * @inheritdoc IMultiFlowPumpWellFunction
     * @dev `calcReserveAtRatioSwap` fetches the closes approximate ratios from the target price,
     * and performs newtons method in order to converge into a reserve.
     */
    function calcReserveAtRatioSwap(
        uint256[] memory reserves,
        uint256 j,
        uint256[] memory ratios,
        bytes calldata data
    ) external view returns (uint256 reserve) {
        uint256 i = j == 1 ? 0 : 1;
        // scale reserves and ratios:
        uint256[] memory decimals = decodeWellData(data);
        uint256[] memory scaledReserves = getScaledReserves(reserves, decimals);

        PriceData memory pd;
        uint256[] memory scaledRatios = getScaledReserves(ratios, decimals);
        // calc target price with 6 decimal precision:
        pd.targetPrice = scaledRatios[i] * PRICE_PRECISION / scaledRatios[j];

        // get ratios and price from the closest highest and lowest price from targetPrice:
        pd.lutData = ILookupTable(lookupTable).getRatiosFromPriceSwap(pd.targetPrice);

        // calculate lp token supply:
        uint256 lpTokenSupply = calcLpTokenSupply(scaledReserves, abi.encode(18, 18));

        // lpTokenSupply / 2 gives the reserves at parity:
        uint256 parityReserve = lpTokenSupply / 2;

        // update `scaledReserves` based on whether targetPrice is closer to low or high price:
        if (percentDiff(pd.lutData.highPrice, pd.targetPrice) > percentDiff(pd.lutData.lowPrice, pd.targetPrice)) {
            // targetPrice is closer to lowPrice.
            scaledReserves[i] = parityReserve * pd.lutData.lowPriceI / pd.lutData.precision;
            scaledReserves[j] = parityReserve * pd.lutData.lowPriceJ / pd.lutData.precision;
            // initialize currentPrice:
            pd.currentPrice = pd.lutData.lowPrice;
        } else {
            // targetPrice is closer to highPrice.
            scaledReserves[i] = parityReserve * pd.lutData.highPriceI / pd.lutData.precision;
            scaledReserves[j] = parityReserve * pd.lutData.highPriceJ / pd.lutData.precision;
            // initialize currentPrice:
            pd.currentPrice = pd.lutData.highPrice;
        }

        // calculate max step size:
        // lowPriceJ will always be larger than highPriceJ so a check here is unnecessary.
        pd.maxStepSize = scaledReserves[j] * (pd.lutData.lowPriceJ - pd.lutData.highPriceJ) / pd.lutData.lowPriceJ;

        for (uint256 k; k < 255; k++) {
            scaledReserves[j] = updateReserve(pd, scaledReserves[j]);

            // calculate scaledReserve[i]:
            scaledReserves[i] = calcReserve(scaledReserves, i, lpTokenSupply, abi.encode(18, 18));
            // calculate new price from reserves:
            pd.newPrice = _calcRate(scaledReserves, i, j, lpTokenSupply);

            // if the new current price is either lower or higher than both the previous current price and the target price,
            // (i.e the target price lies between the current price and the previous current price),
            // recalibrate high/low price.
            if (pd.newPrice > pd.currentPrice && pd.newPrice > pd.targetPrice) {
                pd.lutData.highPriceJ = scaledReserves[j] * 1e18 / parityReserve;
                pd.lutData.highPriceI = scaledReserves[i] * 1e18 / parityReserve;
                pd.lutData.highPrice = pd.newPrice;
            } else if (pd.newPrice < pd.currentPrice && pd.newPrice < pd.targetPrice) {
                pd.lutData.lowPriceJ = scaledReserves[j] * 1e18 / parityReserve;
                pd.lutData.lowPriceI = scaledReserves[i] * 1e18 / parityReserve;
                pd.lutData.lowPrice = pd.newPrice;
            }

            // update max step size based on new scaled reserve.
            pd.maxStepSize = scaledReserves[j] * (pd.lutData.lowPriceJ - pd.lutData.highPriceJ) / pd.lutData.lowPriceJ;

            pd.currentPrice = pd.newPrice;

            // check if new price is within PRICE_THRESHOLD:
            if (pd.currentPrice > pd.targetPrice) {
                if (pd.currentPrice - pd.targetPrice <= PRICE_THRESHOLD) {
                    return scaledReserves[j] / (10 ** (18 - decimals[j]));
                }
            } else {
                if (pd.targetPrice - pd.currentPrice <= PRICE_THRESHOLD) {
                    return scaledReserves[j] / (10 ** (18 - decimals[j]));
                }
            }
        }
        revert("Non convergence: calcReserveAtRatioSwap");
    }

    /**
     * @inheritdoc IBeanstalkWellFunction
     * @dev `calcReserveAtRatioLiquidity` fetches the closes approximate ratios from the target price,
     */
    function calcReserveAtRatioLiquidity(
        uint256[] calldata reserves,
        uint256 j,
        uint256[] calldata ratios,
        bytes calldata data
    ) external view returns (uint256 reserve) {
        uint256 i = j == 1 ? 0 : 1;
        // scale reserves and ratios:
        uint256[] memory decimals = decodeWellData(data);
        uint256[] memory scaledReserves = getScaledReserves(reserves, decimals);

        PriceData memory pd;
        uint256[] memory scaledRatios = getScaledReserves(ratios, decimals);
        // calc target price with 6 decimal precision:
        pd.targetPrice = scaledRatios[i] * PRICE_PRECISION / scaledRatios[j];

        // get ratios and price from the closest highest and lowest price from targetPrice:
        pd.lutData = ILookupTable(lookupTable).getRatiosFromPriceLiquidity(pd.targetPrice);

        // update scaledReserve[j] such that calcRate(scaledReserves, i, j) = low/high Price,
        // depending on which is closer to targetPrice.
        if (percentDiff(pd.lutData.highPrice, pd.targetPrice) > percentDiff(pd.lutData.lowPrice, pd.targetPrice)) {
            // targetPrice is closer to lowPrice.
            scaledReserves[j] = scaledReserves[i] * pd.lutData.lowPriceJ / pd.lutData.precision;

            // set current price to lowPrice.
            pd.currentPrice = pd.lutData.lowPrice;
        } else {
            // targetPrice is closer to highPrice.
            scaledReserves[j] = scaledReserves[i] * pd.lutData.highPriceJ / pd.lutData.precision;

            // set current price to highPrice.
            pd.currentPrice = pd.lutData.highPrice;
        }

        // calculate max step size:
        // lowPriceJ will always be larger than highPriceJ so a check here is unnecessary.
        pd.maxStepSize = scaledReserves[j] * (pd.lutData.lowPriceJ - pd.lutData.highPriceJ) / pd.lutData.lowPriceJ;

        for (uint256 k; k < 255; k++) {
            scaledReserves[j] = updateReserve(pd, scaledReserves[j]);
            // calculate new price from reserves:
            pd.newPrice = calcRate(scaledReserves, i, j, abi.encode(18, 18));

            // if the new current price is either lower or higher than both the previous current price and the target price,
            // (i.e the target price lies between the current price and the previous current price),
            // recalibrate high/lowPrice and continue.
            if (pd.newPrice > pd.targetPrice && pd.targetPrice > pd.currentPrice) {
                pd.lutData.highPriceJ = scaledReserves[j] * 1e18 / scaledReserves[i];
                pd.lutData.highPrice = pd.newPrice;
            } else if (pd.newPrice < pd.targetPrice && pd.targetPrice < pd.currentPrice) {
                pd.lutData.lowPriceJ = scaledReserves[j] * 1e18 / scaledReserves[i];
                pd.lutData.lowPrice = pd.newPrice;
            }

            // update max step size based on new scaled reserve.
            pd.maxStepSize = scaledReserves[j] * (pd.lutData.lowPriceJ - pd.lutData.highPriceJ) / pd.lutData.lowPriceJ;

            pd.currentPrice = pd.newPrice;

            // check if new price is within PRICE_THRESHOLD:
            if (pd.currentPrice > pd.targetPrice) {
                if (pd.currentPrice - pd.targetPrice <= PRICE_THRESHOLD) {
                    return scaledReserves[j] / (10 ** (18 - decimals[j]));
                }
            } else {
                if (pd.targetPrice - pd.currentPrice <= PRICE_THRESHOLD) {
                    return scaledReserves[j] / (10 ** (18 - decimals[j]));
                }
            }
        }
        revert("Non convergence: calcReserveAtRatioLiquidity");
    }

    /**
     * @notice decodes the data encoded in the well.
     * @return decimals an array of the decimals of the tokens in the well.
     */
    function decodeWellData(
        bytes memory data
    ) public view virtual returns (uint256[] memory decimals) {
        (uint256 decimal0, uint256 decimal1) = abi.decode(data, (uint256, uint256));

        // if well data returns 0, assume 18 decimals.
        if (decimal0 == 0) {
            decimal0 = 18;
        }
        if (decimal1 == 0) {
            decimal1 = 18;
        }
        if (decimal0 > 18 || decimal1 > 18) revert InvalidTokenDecimals();

        decimals = new uint256[](2);
        decimals[0] = decimal0;
        decimals[1] = decimal1;
    }

    function name() external pure returns (string memory) {
        return "Stable2";
    }

    function symbol() external pure returns (string memory) {
        return "S2";
    }

    function version() external pure returns (string memory) {
        return "1.1.0";
    }

    /**
     * @notice internal calcRate function.
     */
    function _calcRate(
        uint256[] memory reserves,
        uint256 i,
        uint256 j,
        uint256 lpTokenSupply
    ) internal view returns (uint256 rate) {
        // add 1e6 to reserves:
        uint256[] memory _reserves = new uint256[](2);
        _reserves[i] = reserves[i];
        _reserves[j] = reserves[j] + PRICE_PRECISION;

        // calculate rate:
        rate = _reserves[i] - calcReserve(_reserves, i, lpTokenSupply, abi.encode(18, 18));
    }

    /**
     * @inheritdoc IMultiFlowPumpWellFunction
     * @notice Returns the precision of the ratio at which the pump will cap the reserve at.
     * @dev {Stable2.calcRate} returns the rate with PRICE_PRECISION, independent of data or index.
     */
    function ratioPrecision(uint256, bytes calldata) external pure returns (uint256 precision) {
        return PRICE_PRECISION;
    }

    /**
     * @notice scale `reserves` by `precision`.
     * @dev this sets both reserves to 18 decimals.
     */
    function getScaledReserves(
        uint256[] memory reserves,
        uint256[] memory decimals
    ) internal pure returns (uint256[] memory scaledReserves) {
        scaledReserves = new uint256[](2);
        scaledReserves[0] = reserves[0] * 10 ** (18 - decimals[0]);
        scaledReserves[1] = reserves[1] * 10 ** (18 - decimals[1]);
    }

    function _calcReserve(
        uint256 reserve,
        uint256 b,
        uint256 c,
        uint256 lpTokenSupply
    ) private pure returns (uint256) {
        return (reserve * reserve + c) / (reserve * 2 + b - lpTokenSupply);
    }

    function getBandC(
        uint256 Ann,
        uint256 lpTokenSupply,
        uint256 reserves
    ) private pure returns (uint256 c, uint256 b) {
        c = lpTokenSupply * lpTokenSupply / (reserves * N) * lpTokenSupply * A_PRECISION / (Ann * N);
        b = reserves + (lpTokenSupply * A_PRECISION / Ann);
    }

    /**
     * @notice calculates the step size, and returns the updated reserve.
     */
    function updateReserve(PriceData memory pd, uint256 reserve) internal pure returns (uint256) {
        if (pd.targetPrice > pd.currentPrice) {
            // if the targetPrice is greater than the currentPrice,
            // the reserve needs to be decremented to increase currentPrice.
            return reserve
                - pd.maxStepSize * (pd.targetPrice - pd.currentPrice) / (pd.lutData.highPrice - pd.lutData.lowPrice);
        } else {
            // if the targetPrice is less than the currentPrice,
            // the reserve needs to be incremented to decrease currentPrice.
            return reserve
                + pd.maxStepSize * (pd.currentPrice - pd.targetPrice) / (pd.lutData.highPrice - pd.lutData.lowPrice);
        }
    }

    /**
     * @notice Calculate the percentage difference between two numbers.
     * @return The percentage difference as a fixed-point number with 18 decimals.
     * @dev This function calculates the absolute percentage difference:
     *      |(a - b)| / ((a + b) / 2) * 100
     *      The result is scaled by 1e18 for precision.
     */
    function percentDiff(uint256 _a, uint256 _b) internal pure returns (uint256) {
        if (_a == _b) return 0;
        uint256 difference = _a > _b ? _a - _b : _b - _a;
        uint256 average = (_a + _b) / 2;
        // Multiply by 100 * 1e18 to get percentage with 18 decimal places
        return (difference * 100 * 1e18) / average;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IMultiFlowPumpWellFunction} from "src/interfaces/IMultiFlowPumpWellFunction.sol";

/**
 * @title IBeanstalkWellFunction
 * @notice Defines all necessary functions for Beanstalk to support a Well Function in addition to functions defined in the primary interface.
 * It extends `IMultiFlowPumpWellFunction` as Beanstalk requires Wells to use MultiFlowPump in order to have access to manipulation resistant oracles.
 * Beanstalk requires 2 functions to solve for a given reserve value such that the average price between
 * the given reserve and all other reserves equals the average of the input ratios.
 * - `calcReserveAtRatioSwap` assumes the target ratios are reached through executing a swap. Note: `calcReserveAtRatioSwap` is included in {IMultiFlowPumpWellFunction}.
 * - `calcReserveAtRatioLiquidity` assumes the target ratios are reached through adding/removing liquidity.
 */
interface IBeanstalkWellFunction is IMultiFlowPumpWellFunction {
    /**
     * @notice Calculates the `j` reserve such that `π_{i | i != j} (d reserves_j / d reserves_i) = π_{i | i != j}(ratios_j / ratios_i)`.
     * assumes that reserve_j is being added or removed in exchange for LP Tokens.
     * @dev used by Beanstalk to calculate the max deltaB that can be converted in/out of a Well.
     * @param reserves The reserves of the Well
     * @param j The index of the reserve to solve for
     * @param ratios The ratios of reserves to solve for
     * @param data Well function data provided on every call
     * @return reserve The resulting reserve at the jth index
     */
    function calcReserveAtRatioLiquidity(
        uint256[] calldata reserves,
        uint256 j,
        uint256[] calldata ratios,
        bytes calldata data
    ) external view returns (uint256 reserve);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

/**
 * @title PriceReserveMapping
 * @author DeadmanWalking
 * @notice In order to reasonably use `calcReserveAtRatioSwap` and `calcReserveAtRatioLiquidity` on chain,
 * a lookup table contract is used to decrease the amount of iterations needed to converge into an answer.
 */
interface ILookupTable {
    /**
     * @notice the lookup table returns a series of data, given a price point:
     * @param highPrice the closest price to the targetPrice, where targetPrice < highPrice.
     * @param highPriceI reserve i such that `calcRate(reserve, i, j, data)` == highPrice.
     * @param highPriceJ reserve j such that `calcRate(reserve, i, j, data)` == highPrice.
     * @param lowPrice the closest price to the targetPrice, where targetPrice > lowPrice.
     * @param lowPriceI reserve i such that `calcRate(reserve, i, j, data)` == lowPrice.
     * @param lowPriceJ reserve j such that `calcRate(reserve, i, j, data)` == lowPrice.
     * @param precision the initial reserve values. Assumes the inital reserve i == reserve j
     */
    struct PriceData {
        uint256 highPrice;
        uint256 highPriceI;
        uint256 highPriceJ;
        uint256 lowPrice;
        uint256 lowPriceI;
        uint256 lowPriceJ;
        uint256 precision;
    }

    function getRatiosFromPriceLiquidity(
        uint256
    ) external view returns (PriceData memory);
    function getRatiosFromPriceSwap(
        uint256
    ) external view returns (PriceData memory);
    function getAParameter() external view returns (uint256);
}

File 4 of 8 : ProportionalLPToken2.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IWellFunction} from "src/interfaces/IWellFunction.sol";
import {Math} from "oz/utils/math/Math.sol";

/**
 * @title ProportionalLPToken2
 * @notice Defines a proportional relationship between the supply of LP tokens
 * and the amount of each underlying token for a two-token Well.
 * @dev When removing `s` LP tokens with a Well with `S` LP token supply, the user
 * recieves `s * b_i / S` of each underlying token.
 */
abstract contract ProportionalLPToken2 is IWellFunction {
    using Math for uint256;

    function calcLPTokenUnderlying(
        uint256 lpTokenAmount,
        uint256[] calldata reserves,
        uint256 lpTokenSupply,
        bytes calldata
    ) external pure returns (uint256[] memory underlyingAmounts) {
        underlyingAmounts = new uint256[](2);
        underlyingAmounts[0] = lpTokenAmount.mulDiv(reserves[0], lpTokenSupply);
        underlyingAmounts[1] = lpTokenAmount.mulDiv(reserves[1], lpTokenSupply);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;

/// @dev Interface of the ERC20 standard as defined in the EIP.
/// @dev This includes the optional name, symbol, and decimals metadata.
interface IERC20 {
    /// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`).
    event Transfer(address indexed from, address indexed to, uint256 value);

    /// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value`
    /// is the new allowance.
    event Approval(address indexed owner, address indexed spender, uint256 value);

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

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

    /// @notice Moves `amount` tokens from the caller's account to `to`.
    function transfer(address to, uint256 amount) external returns (bool);

    /// @notice Returns the remaining number of tokens that `spender` is allowed
    /// to spend on behalf of `owner`
    function allowance(address owner, address spender) external view returns (uint256);

    /// @notice Sets `amount` as the allowance of `spender` over the caller's tokens.
    /// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
    function approve(address spender, uint256 amount) external returns (bool);

    /// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism.
    /// `amount` is then deducted from the caller's allowance.
    function transferFrom(address from, address to, uint256 amount) external returns (bool);

    /// @notice Returns the name of the token.
    function name() external view returns (string memory);

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

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

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IWellFunction} from "src/interfaces/IWellFunction.sol";

/**
 * @title IMultiFlowPumpWellFunction
 * @dev A Well Function must implement IMultiFlowPumpWellFunction to be supported by
 * the Multi Flow Pump.
 */
interface IMultiFlowPumpWellFunction is IWellFunction {
    /**
     * @notice Calculates the `j` reserve such that `π_{i | i != j} (d reserves_j / d reserves_i) = π_{i | i != j}(ratios_j / ratios_i)`.
     * assumes that reserve_j is being swapped for other reserves in the Well.
     * @dev used by Beanstalk to calculate the deltaB every Season
     * @param reserves The reserves of the Well
     * @param j The index of the reserve to solve for
     * @param ratios The ratios of reserves to solve for
     * @param data Well function data provided on every call
     * @return reserve The resulting reserve at the jth index
     */
    function calcReserveAtRatioSwap(
        uint256[] calldata reserves,
        uint256 j,
        uint256[] calldata ratios,
        bytes calldata data
    ) external view returns (uint256 reserve);

    /**
     * @notice Calculates the rate at which j can be exchanged for i.
     * @param reserves The reserves of the Well
     * @param i The index of the token for which the output is being calculated
     * @param j The index of the token for which 1 token is being exchanged
     * @param data Well function data provided on every call
     * @return rate The rate at which j can be exchanged for i
     * @dev should return with 36 decimal precision
     */
    function calcRate(
        uint256[] calldata reserves,
        uint256 i,
        uint256 j,
        bytes calldata data
    ) external view returns (uint256 rate);

    /**
     * @notice Returns the precision of the ratio at which the pump will cap the reserve at.
     * @param j The index of the reserve to solve for
     * @param data The data passed to the well function
     * @return precision The precision of the ratio at which the pump will cap the reserve at
     */
    function ratioPrecision(uint256 j, bytes calldata data) external view returns (uint256);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

/**
 * @title IWellFunction
 * @notice Defines a relationship between token reserves and LP token supply.
 * @dev Well Functions can contain arbitrary logic, but should be deterministic
 * if expected to be used alongside a Pump. When interacing with a Well or
 * Well Function, always verify that the Well Function is valid.
 */
interface IWellFunction {
    /**
     * @notice Thrown if the user inputs a `j` value is out of bounds.
     */
    error InvalidJArgument();

    /**
     * @notice Calculates the `j`th reserve given a list of `reserves` and `lpTokenSupply`.
     * @param reserves A list of token reserves. The jth reserve will be ignored, but a placeholder must be provided.
     * @param j The index of the reserve to solve for
     * @param lpTokenSupply The supply of LP tokens
     * @param data Extra Well function data provided on every call
     * @return reserve The resulting reserve at the jth index
     * @dev Should round up to ensure that Well reserves are marginally higher to enforce calcLpTokenSupply(...) >= totalSupply()
     */
    function calcReserve(
        uint256[] memory reserves,
        uint256 j,
        uint256 lpTokenSupply,
        bytes calldata data
    ) external view returns (uint256 reserve);

    /**
     * @notice Gets the LP token supply given a list of reserves.
     * @param reserves A list of token reserves
     * @param data Extra Well function data provided on every call
     * @return lpTokenSupply The resulting supply of LP tokens
     * @dev Should round down to ensure so that the Well Token supply is marignally lower to enforce calcLpTokenSupply(...) >= totalSupply()
     */
    function calcLpTokenSupply(
        uint256[] memory reserves,
        bytes calldata data
    ) external view returns (uint256 lpTokenSupply);

    /**
     * @notice Calculates the amount of each reserve token underlying a given amount of LP tokens.
     * @param lpTokenAmount An amount of LP tokens
     * @param reserves A list of token reserves
     * @param lpTokenSupply The current supply of LP tokens
     * @param data Extra Well function data provided on every call
     * @return underlyingAmounts The amount of each reserve token that underlies the LP tokens
     * @dev The constraint totalSupply() <= calcLPTokenSupply(...) must be held in the case where
     * `lpTokenAmount` LP tokens are burned in exchanged for `underlyingAmounts`. If the constraint
     * does not hold, then the Well Function is invalid.
     */
    function calcLPTokenUnderlying(
        uint256 lpTokenAmount,
        uint256[] memory reserves,
        uint256 lpTokenSupply,
        bytes calldata data
    ) external view returns (uint256[] memory underlyingAmounts);

    /**
     * @notice Returns the name of the Well function.
     */
    function name() external view returns (string memory);

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

    /**
     * @notice Returns the version of the Well function.
     */
    function version() external view returns (string memory);
}

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

Settings
{
  "remappings": [
    "prb/math/=lib/prb-math/src/",
    "forge-std/=lib/forge-std/src/",
    "oz/=lib/openzeppelin-contracts/contracts/",
    "ozu/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "src/=src/",
    "mocks/=mocks/",
    "tests/=tests/",
    "utils/=utils/",
    "@openzeppelin/=node_modules/@openzeppelin/",
    "@prb/test/=lib/prb-math/lib/prb-test/src/",
    "ds-test/=lib/prb-math/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "prb-math/=lib/prb-math/src/",
    "prb-test/=lib/prb-math/lib/prb-test/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"lut","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidJArgument","type":"error"},{"inputs":[],"name":"InvalidLUT","type":"error"},{"inputs":[],"name":"InvalidTokenDecimals","type":"error"},{"inputs":[{"internalType":"uint256","name":"lpTokenAmount","type":"uint256"},{"internalType":"uint256[]","name":"reserves","type":"uint256[]"},{"internalType":"uint256","name":"lpTokenSupply","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"calcLPTokenUnderlying","outputs":[{"internalType":"uint256[]","name":"underlyingAmounts","type":"uint256[]"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"reserves","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"calcLpTokenSupply","outputs":[{"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"reserves","type":"uint256[]"},{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"uint256","name":"j","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"calcRate","outputs":[{"internalType":"uint256","name":"rate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"reserves","type":"uint256[]"},{"internalType":"uint256","name":"j","type":"uint256"},{"internalType":"uint256","name":"lpTokenSupply","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"calcReserve","outputs":[{"internalType":"uint256","name":"reserve","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"reserves","type":"uint256[]"},{"internalType":"uint256","name":"j","type":"uint256"},{"internalType":"uint256[]","name":"ratios","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"calcReserveAtRatioLiquidity","outputs":[{"internalType":"uint256","name":"reserve","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"reserves","type":"uint256[]"},{"internalType":"uint256","name":"j","type":"uint256"},{"internalType":"uint256[]","name":"ratios","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"calcReserveAtRatioSwap","outputs":[{"internalType":"uint256","name":"reserve","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"decodeWellData","outputs":[{"internalType":"uint256[]","name":"decimals","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"ratioPrecision","outputs":[{"internalType":"uint256","name":"precision","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"}]

60c060405234801561001057600080fd5b506040516123d43803806123d483398101604081905261002f916100cd565b6001600160a01b03811661005657604051638005fdb760e01b815260040160405180910390fd5b6001600160a01b038116608081905260408051633e2a0a2960e01b81529051633e2a0a29916004808201926020929091908290030181865afa1580156100a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100c491906100fd565b60a05250610116565b6000602082840312156100df57600080fd5b81516001600160a01b03811681146100f657600080fd5b9392505050565b60006020828403121561010f57600080fd5b5051919050565b60805160a05161228b6101496000396000818161024d0152610bb10152600081816106a00152610e07015261228b6000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80637f35e83d116100715780637f35e83d1461014b57806380be8c981461015e57806395d89b4114610171578063a11686b81461018f578063b1d35841146101af578063bf66642a146101c257600080fd5b806306fdde03146100ae57806314c15fc0146100e35780632b227ea91461010457806354fd4d50146101175780635a84467c14610138575b600080fd5b60408051808201909152600781526629ba30b136329960c91b60208201525b6040516100da9190611a97565b60405180910390f35b6100f66100f1366004611c12565b6101d5565b6040519081526020016100da565b6100f6610112366004611c79565b6104e5565b6040805180820190915260058152640312e312e360dc1b60208201526100cd565b6100f6610146366004611d7f565b610551565b6100f6610159366004611c79565b610b87565b6100f661016c366004611e2c565b610d20565b604080518082019091526002815261299960f11b60208201526100cd565b6101a261019d366004611ece565b6113e4565b6040516100da9190611f02565b6100f66101bd366004611f45565b6114ae565b6101a26101d0366004611f90565b6114ba565b6000826000815181106101ea576101ea612016565b6020026020010151600014801561021b57508260018151811061020f5761020f612016565b60200260200101516000145b15610228575060006104df565b6000610233836113e4565b90506000610241858361155f565b905060006002610271817f0000000000000000000000000000000000000000000000000000000000000000612042565b61027b9190612042565b905060008260018151811061029257610292612016565b6020026020010151836000815181106102ad576102ad612016565b60200260200101516102bf9190612059565b905080945060005b60ff811015610486576000808790506002866000815181106102eb576102eb612016565b60200260200101516102fd9190612042565b6103078983612042565b6103119190612082565b905060028660018151811061032857610328612016565b602002602001015161033a9190612042565b6103448983612042565b61034e9190612082565b9050878161035e60026001612059565b6103689190612042565b60648a610375828a6120a4565b61037f9190612042565b6103899190612082565b6103939190612059565b8961039f600285612042565b60646103ab898b612042565b6103b59190612082565b6103bf9190612059565b6103c99190612042565b6103d39190612082565b985080891115610436576103e7818a6120a4565b60020361041357821561040e576103ff60018a6120a4565b985050505050505050506104df565b600192505b600161041f828b6120a4565b116104315750505050505050506104df565b61047b565b61044089826120a4565b60020361045d578215610458576103ff896001612059565b600192505b60016104698a836120a4565b1161047b5750505050505050506104df565b5050506001016102c7565b5060405162461bcd60e51b815260206004820152602260248201527f4e6f6e20636f6e76657267656e63653a2063616c634c70546f6b656e537570706044820152616c7960f01b60648201526084015b60405180910390fd5b92915050565b6000806104f1836113e4565b905060006104ff878361155f565b60408051601260208201819052918101919091529091506000906105379083906060015b6040516020818303038152906040526101d5565b905061054582888884611671565b98975050505050505050565b60008086600114610563576001610566565b60005b60ff16905060006105ac85858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506113e492505050565b905060006105ee8b8b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525086925061155f915050565b90506105f8611a2b565b60006106388a8a8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525088925061155f915050565b9050808b8151811061064c5761064c612016565b6020026020010151620f424082878151811061066a5761066a612016565b602002602001015161067c9190612042565b6106869190612082565b808352604051630a9028c960e41b815260048101919091527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9028c909060240160e060405180830381865afa1580156106ef573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061071391906120b7565b6080830181905260600151825161072a919061176b565b608083015151835161073c919061176b565b11156107b757816080015160c00151826080015160a0015184878151811061076657610766612016565b60200260200101516107789190612042565b6107829190612082565b838c8151811061079457610794612016565b602002602001018181525050816080015160600151826020018181525050610823565b816080015160c001518260800151604001518487815181106107db576107db612016565b60200260200101516107ed9190612042565b6107f79190612082565b838c8151811061080957610809612016565b602090810291909101810191909152608083015151908301525b608082015160a081015160409091015161083d90826120a4565b848d8151811061084f5761084f612016565b60200260200101516108619190612042565b61086b9190612082565b606083015260005b60ff811015610b1e5761089f83858e8151811061089257610892612016565b60200260200101516117e2565b848d815181106108b1576108b1612016565b6020026020010181815250506108fa84878e6012806040516020016108e692919060ff92831681529116602082015260400190565b6040516020818303038152906040526104e5565b604084018190528351108015610914575060208301518351115b156109855783868151811061092b5761092b612016565b6020026020010151848d8151811061094557610945612016565b6020026020010151670de0b6b3a76400006109609190612042565b61096a9190612082565b60808401805160409081019290925290840151905152610a0c565b8251604084015110801561099d575060208301518351105b15610a0c578386815181106109b4576109b4612016565b6020026020010151848d815181106109ce576109ce612016565b6020026020010151670de0b6b3a76400006109e99190612042565b6109f39190612082565b60808401805160a0019190915260408401519051606001525b608083015160a0810151604090910151610a2690826120a4565b858e81518110610a3857610a38612016565b6020026020010151610a4a9190612042565b610a549190612082565b606084015260408301516020840181905283511015610aec5782516020840151600a91610a80916120a4565b11610ae757848c81518110610a9757610a97612016565b60200260200101516012610aab91906120a4565b610ab690600a612225565b848d81518110610ac857610ac8612016565b6020026020010151610ada9190612082565b9650505050505050610b7c565b610b16565b60208301518351600a91610aff916120a4565b11610b1657848c81518110610a9757610a97612016565b600101610873565b5060405162461bcd60e51b815260206004820152602c60248201527f4e6f6e20636f6e76657267656e63653a2063616c63526573657276654174526160448201526b74696f4c697175696469747960a01b60648201526084016104d6565b979650505050505050565b600080610b93836113e4565b90506000610ba1878361155f565b9050600080610c196002610bd5817f0000000000000000000000000000000000000000000000000000000000000000612042565b610bdf9190612042565b888a15610c065785600081518110610bf957610bf9612016565b602002602001015161188f565b85600181518110610bf957610bf9612016565b915091508694506000805b60ff811015610ccf57869150610c3c8784868c611909565b965081871115610ca4576001610c5283896120a4565b11610c9f57858a81518110610c6957610c69612016565b60200260200101516012610c7d91906120a4565b610c8890600a612225565b610c929088612082565b9650505050505050610d18565b610cc7565b6001610cb088846120a4565b11610cc757858a81518110610c6957610c69612016565b600101610c24565b5060405162461bcd60e51b815260206004820152601c60248201527f4e6f6e20636f6e76657267656e63653a2063616c63526573657276650000000060448201526064016104d6565b949350505050565b60008085600114610d32576001610d35565b60005b60ff1690506000610d7b85858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506113e492505050565b90506000610d89898361155f565b9050610d93611a2b565b6000610d9f898561155f565b9050808a81518110610db357610db3612016565b6020026020010151620f4240828781518110610dd157610dd1612016565b6020026020010151610de39190612042565b610ded9190612082565b80835260405163958581e560e01b815260048101919091527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063958581e59060240160e060405180830381865afa158015610e56573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e7a91906120b7565b60808301526040805160126020820181905291810191909152600090610ea4908590606001610523565b90506000610eb3600283612082565b9050610ecb846080015160600151856000015161176b565b6080850151518551610edd919061176b565b1115610f7b5760808085015160c0810151910151610efb9083612042565b610f059190612082565b858881518110610f1757610f17612016565b6020908102919091010152608084015160c081015160a090910151610f3c9083612042565b610f469190612082565b858d81518110610f5857610f58612016565b60200260200101818152505083608001516060015184602001818152505061100c565b608084015160c0810151602090910151610f959083612042565b610f9f9190612082565b858881518110610fb157610fb1612016565b6020908102919091010152608084015160c0810151604090910151610fd69083612042565b610fe09190612082565b858d81518110610ff257610ff2612016565b602090810291909101810191909152608085015151908501525b608084015160a081015160409091015161102690826120a4565b868e8151811061103857611038612016565b602002602001015161104a9190612042565b6110549190612082565b606085015260005b60ff8110156113825761107b85878f8151811061089257610892612016565b868e8151811061108d5761108d612016565b6020026020010181815250506110d68689856012806040516020016110c292919060ff92831681529116602082015260400190565b604051602081830303815290604052610b87565b8689815181106110e8576110e8612016565b60200260200101818152505061110086898f86611671565b60408601819052602086015110801561111d575084516040860151115b156111b85781868e8151811061113557611135612016565b6020026020010151670de0b6b3a76400006111509190612042565b61115a9190612082565b6080860151604001528551829087908a90811061117957611179612016565b6020026020010151670de0b6b3a76400006111949190612042565b61119e9190612082565b60808601805160200191909152604086015190515261126e565b846020015185604001511080156111d3575084516040860151105b1561126e5781868e815181106111eb576111eb612016565b6020026020010151670de0b6b3a76400006112069190612042565b6112109190612082565b608086015160a001528551829087908a90811061122f5761122f612016565b6020026020010151670de0b6b3a764000061124a9190612042565b6112549190612082565b608080870180519091019190915260408601519051606001525b608085015160a081015160409091015161128890826120a4565b878f8151811061129a5761129a612016565b60200260200101516112ac9190612042565b6112b69190612082565b6060860152604085015160208601819052855110156113505784516020860151600a916112e2916120a4565b1161134b57868d815181106112f9576112f9612016565b6020026020010151601261130d91906120a4565b61131890600a612225565b868e8151811061132a5761132a612016565b602002602001015161133c9190612082565b985050505050505050506113db565b61137a565b60208501518551600a91611363916120a4565b1161137a57868d815181106112f9576112f9612016565b60010161105c565b5060405162461bcd60e51b815260206004820152602760248201527f4e6f6e20636f6e76657267656e63653a2063616c635265736572766541745261604482015266074696f537761760cc1b60648201526084016104d6565b95945050505050565b6060600080838060200190518101906113fd9190612231565b915091508160000361140e57601291505b8060000361141a575060125b60128211806114295750601281115b156114475760405163686d360760e01b815260040160405180910390fd5b6040805160028082526060820183529091602083019080368337019050509250818360008151811061147b5761147b612016565b602002602001018181525050808360018151811061149b5761149b612016565b6020026020010181815250505050919050565b620f42405b9392505050565b6040805160028082526060808301845292602083019080368337019050509050611501868660008181106114f0576114f0612016565b8a9260209091020135905086611941565b8160008151811061151457611514612016565b602002602001018181525050611536868660018181106114f0576114f0612016565b8160018151811061154957611549612016565b6020026020010181815250509695505050505050565b60408051600280825260608083018452926020830190803683370190505090508160008151811061159257611592612016565b602002602001015160126115a691906120a4565b6115b190600a612225565b836000815181106115c4576115c4612016565b60200260200101516115d69190612042565b816000815181106115e9576115e9612016565b6020026020010181815250508160018151811061160857611608612016565b6020026020010151601261161c91906120a4565b61162790600a612225565b8360018151811061163a5761163a612016565b602002602001015161164c9190612042565b8160018151811061165f5761165f612016565b60200260200101818152505092915050565b604080516002808252606082018352600092839291906020830190803683370190505090508585815181106116a8576116a8612016565b60200260200101518186815181106116c2576116c2612016565b602002602001018181525050620f42408685815181106116e4576116e4612016565b60200260200101516116f69190612059565b81858151811061170857611708612016565b60200260200101818152505061173d8186856012806040516020016110c292919060ff92831681529116602082015260400190565b81868151811061174f5761174f612016565b602002602001015161176191906120a4565b9695505050505050565b600081830361177c575060006104df565b60008284116117945761178f84846120a4565b61179e565b61179e83856120a4565b9050600060026117ae8587612059565b6117b89190612082565b9050806117c6836064612042565b6117d890670de0b6b3a7640000612042565b6113db9190612082565b60008260200151836000015111156118455760808301516060810151905161180a91906120a4565b6020840151845161181b91906120a4565b846060015161182a9190612042565b6118349190612082565b61183e90836120a4565b90506104df565b60808301516060810151905161185b91906120a4565b8351602085015161186c91906120a4565b846060015161187b9190612042565b6118859190612082565b61183e9083612059565b60008061189d600286612042565b6064856118ab600287612042565b6118b58880612042565b6118bf9190612082565b6118c99190612042565b6118d39190612042565b6118dd9190612082565b9150846118eb606486612042565b6118f59190612082565b6118ff9084612059565b9050935093915050565b60008184611918876002612042565b6119229190612059565b61192c91906120a4565b836119378780612042565b6117d89190612059565b600080806000198587098587029250828110838203039150508060000361197b578382816119715761197161206c565b04925050506114b3565b8084116119c25760405162461bcd60e51b81526020600482015260156024820152744d6174683a206d756c446976206f766572666c6f7760581b60448201526064016104d6565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001611a926040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b905290565b602081526000825180602084015260005b81811015611ac55760208186018101516040868401015201611aa8565b506000604082850101526040601f19601f83011684010191505092915050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715611b2357611b23611ae5565b604052919050565b600082601f830112611b3c57600080fd5b81356001600160401b03811115611b5557611b55611ae5565b8060051b611b6560208201611afb565b91825260208185018101929081019086841115611b8157600080fd5b6020860192505b83831015611761578235825260209283019290910190611b88565b600082601f830112611bb457600080fd5b81356001600160401b03811115611bcd57611bcd611ae5565b611be0601f8201601f1916602001611afb565b818152846020838601011115611bf557600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215611c2557600080fd5b82356001600160401b03811115611c3b57600080fd5b611c4785828601611b2b565b92505060208301356001600160401b03811115611c6357600080fd5b611c6f85828601611ba3565b9150509250929050565b60008060008060808587031215611c8f57600080fd5b84356001600160401b03811115611ca557600080fd5b611cb187828801611b2b565b945050602085013592506040850135915060608501356001600160401b03811115611cdb57600080fd5b611ce787828801611ba3565b91505092959194509250565b60008083601f840112611d0557600080fd5b5081356001600160401b03811115611d1c57600080fd5b6020830191508360208260051b8501011115611d3757600080fd5b9250929050565b60008083601f840112611d5057600080fd5b5081356001600160401b03811115611d6757600080fd5b602083019150836020828501011115611d3757600080fd5b60008060008060008060006080888a031215611d9a57600080fd5b87356001600160401b03811115611db057600080fd5b611dbc8a828b01611cf3565b9098509650506020880135945060408801356001600160401b03811115611de257600080fd5b611dee8a828b01611cf3565b90955093505060608801356001600160401b03811115611e0d57600080fd5b611e198a828b01611d3e565b989b979a50959850939692959293505050565b600080600080600060808688031215611e4457600080fd5b85356001600160401b03811115611e5a57600080fd5b611e6688828901611b2b565b9550506020860135935060408601356001600160401b03811115611e8957600080fd5b611e9588828901611b2b565b93505060608601356001600160401b03811115611eb157600080fd5b611ebd88828901611d3e565b969995985093965092949392505050565b600060208284031215611ee057600080fd5b81356001600160401b03811115611ef657600080fd5b610d1884828501611ba3565b602080825282518282018190526000918401906040840190835b81811015611f3a578351835260209384019390920191600101611f1c565b509095945050505050565b600080600060408486031215611f5a57600080fd5b8335925060208401356001600160401b03811115611f7757600080fd5b611f8386828701611d3e565b9497909650939450505050565b60008060008060008060808789031215611fa957600080fd5b8635955060208701356001600160401b03811115611fc657600080fd5b611fd289828a01611cf3565b9096509450506040870135925060608701356001600160401b03811115611ff857600080fd5b61200489828a01611d3e565b979a9699509497509295939492505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176104df576104df61202c565b808201808211156104df576104df61202c565b634e487b7160e01b600052601260045260246000fd5b60008261209f57634e487b7160e01b600052601260045260246000fd5b500490565b818103818111156104df576104df61202c565b600060e08284031280156120ca57600080fd5b5060405160009060e081016001600160401b03811182821017156120f0576120f0611ae5565b604090815284518252602080860151908301528481015190820152606080850151908201526080808501519082015260a0808501519082015260c09384015193810193909352509092915050565b6001815b60018411156121795780850481111561215d5761215d61202c565b600184161561216b57908102905b60019390931c928002612142565b935093915050565b600082612190575060016104df565b8161219d575060006104df565b81600181146121b357600281146121bd576121d9565b60019150506104df565b60ff8411156121ce576121ce61202c565b50506001821b6104df565b5060208310610133831016604e8410600b84101617156121fc575081810a6104df565b612209600019848461213e565b806000190482111561221d5761221d61202c565b029392505050565b60006114b38383612181565b6000806040838503121561224457600080fd5b50508051602090910151909290915056fea2646970667358221220cfe4388fe2d77062c4ef77da9a3148be16e4200f959eb1c57626d8e2723c7c2a64736f6c634300081a0033000000000000000000000000ba51055dad14d3920e1798d2e8a152d91cadb461

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80637f35e83d116100715780637f35e83d1461014b57806380be8c981461015e57806395d89b4114610171578063a11686b81461018f578063b1d35841146101af578063bf66642a146101c257600080fd5b806306fdde03146100ae57806314c15fc0146100e35780632b227ea91461010457806354fd4d50146101175780635a84467c14610138575b600080fd5b60408051808201909152600781526629ba30b136329960c91b60208201525b6040516100da9190611a97565b60405180910390f35b6100f66100f1366004611c12565b6101d5565b6040519081526020016100da565b6100f6610112366004611c79565b6104e5565b6040805180820190915260058152640312e312e360dc1b60208201526100cd565b6100f6610146366004611d7f565b610551565b6100f6610159366004611c79565b610b87565b6100f661016c366004611e2c565b610d20565b604080518082019091526002815261299960f11b60208201526100cd565b6101a261019d366004611ece565b6113e4565b6040516100da9190611f02565b6100f66101bd366004611f45565b6114ae565b6101a26101d0366004611f90565b6114ba565b6000826000815181106101ea576101ea612016565b6020026020010151600014801561021b57508260018151811061020f5761020f612016565b60200260200101516000145b15610228575060006104df565b6000610233836113e4565b90506000610241858361155f565b905060006002610271817f0000000000000000000000000000000000000000000000000000000000000064612042565b61027b9190612042565b905060008260018151811061029257610292612016565b6020026020010151836000815181106102ad576102ad612016565b60200260200101516102bf9190612059565b905080945060005b60ff811015610486576000808790506002866000815181106102eb576102eb612016565b60200260200101516102fd9190612042565b6103078983612042565b6103119190612082565b905060028660018151811061032857610328612016565b602002602001015161033a9190612042565b6103448983612042565b61034e9190612082565b9050878161035e60026001612059565b6103689190612042565b60648a610375828a6120a4565b61037f9190612042565b6103899190612082565b6103939190612059565b8961039f600285612042565b60646103ab898b612042565b6103b59190612082565b6103bf9190612059565b6103c99190612042565b6103d39190612082565b985080891115610436576103e7818a6120a4565b60020361041357821561040e576103ff60018a6120a4565b985050505050505050506104df565b600192505b600161041f828b6120a4565b116104315750505050505050506104df565b61047b565b61044089826120a4565b60020361045d578215610458576103ff896001612059565b600192505b60016104698a836120a4565b1161047b5750505050505050506104df565b5050506001016102c7565b5060405162461bcd60e51b815260206004820152602260248201527f4e6f6e20636f6e76657267656e63653a2063616c634c70546f6b656e537570706044820152616c7960f01b60648201526084015b60405180910390fd5b92915050565b6000806104f1836113e4565b905060006104ff878361155f565b60408051601260208201819052918101919091529091506000906105379083906060015b6040516020818303038152906040526101d5565b905061054582888884611671565b98975050505050505050565b60008086600114610563576001610566565b60005b60ff16905060006105ac85858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506113e492505050565b905060006105ee8b8b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525086925061155f915050565b90506105f8611a2b565b60006106388a8a8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525088925061155f915050565b9050808b8151811061064c5761064c612016565b6020026020010151620f424082878151811061066a5761066a612016565b602002602001015161067c9190612042565b6106869190612082565b808352604051630a9028c960e41b815260048101919091527f000000000000000000000000ba51055dad14d3920e1798d2e8a152d91cadb4616001600160a01b03169063a9028c909060240160e060405180830381865afa1580156106ef573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061071391906120b7565b6080830181905260600151825161072a919061176b565b608083015151835161073c919061176b565b11156107b757816080015160c00151826080015160a0015184878151811061076657610766612016565b60200260200101516107789190612042565b6107829190612082565b838c8151811061079457610794612016565b602002602001018181525050816080015160600151826020018181525050610823565b816080015160c001518260800151604001518487815181106107db576107db612016565b60200260200101516107ed9190612042565b6107f79190612082565b838c8151811061080957610809612016565b602090810291909101810191909152608083015151908301525b608082015160a081015160409091015161083d90826120a4565b848d8151811061084f5761084f612016565b60200260200101516108619190612042565b61086b9190612082565b606083015260005b60ff811015610b1e5761089f83858e8151811061089257610892612016565b60200260200101516117e2565b848d815181106108b1576108b1612016565b6020026020010181815250506108fa84878e6012806040516020016108e692919060ff92831681529116602082015260400190565b6040516020818303038152906040526104e5565b604084018190528351108015610914575060208301518351115b156109855783868151811061092b5761092b612016565b6020026020010151848d8151811061094557610945612016565b6020026020010151670de0b6b3a76400006109609190612042565b61096a9190612082565b60808401805160409081019290925290840151905152610a0c565b8251604084015110801561099d575060208301518351105b15610a0c578386815181106109b4576109b4612016565b6020026020010151848d815181106109ce576109ce612016565b6020026020010151670de0b6b3a76400006109e99190612042565b6109f39190612082565b60808401805160a0019190915260408401519051606001525b608083015160a0810151604090910151610a2690826120a4565b858e81518110610a3857610a38612016565b6020026020010151610a4a9190612042565b610a549190612082565b606084015260408301516020840181905283511015610aec5782516020840151600a91610a80916120a4565b11610ae757848c81518110610a9757610a97612016565b60200260200101516012610aab91906120a4565b610ab690600a612225565b848d81518110610ac857610ac8612016565b6020026020010151610ada9190612082565b9650505050505050610b7c565b610b16565b60208301518351600a91610aff916120a4565b11610b1657848c81518110610a9757610a97612016565b600101610873565b5060405162461bcd60e51b815260206004820152602c60248201527f4e6f6e20636f6e76657267656e63653a2063616c63526573657276654174526160448201526b74696f4c697175696469747960a01b60648201526084016104d6565b979650505050505050565b600080610b93836113e4565b90506000610ba1878361155f565b9050600080610c196002610bd5817f0000000000000000000000000000000000000000000000000000000000000064612042565b610bdf9190612042565b888a15610c065785600081518110610bf957610bf9612016565b602002602001015161188f565b85600181518110610bf957610bf9612016565b915091508694506000805b60ff811015610ccf57869150610c3c8784868c611909565b965081871115610ca4576001610c5283896120a4565b11610c9f57858a81518110610c6957610c69612016565b60200260200101516012610c7d91906120a4565b610c8890600a612225565b610c929088612082565b9650505050505050610d18565b610cc7565b6001610cb088846120a4565b11610cc757858a81518110610c6957610c69612016565b600101610c24565b5060405162461bcd60e51b815260206004820152601c60248201527f4e6f6e20636f6e76657267656e63653a2063616c63526573657276650000000060448201526064016104d6565b949350505050565b60008085600114610d32576001610d35565b60005b60ff1690506000610d7b85858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506113e492505050565b90506000610d89898361155f565b9050610d93611a2b565b6000610d9f898561155f565b9050808a81518110610db357610db3612016565b6020026020010151620f4240828781518110610dd157610dd1612016565b6020026020010151610de39190612042565b610ded9190612082565b80835260405163958581e560e01b815260048101919091527f000000000000000000000000ba51055dad14d3920e1798d2e8a152d91cadb4616001600160a01b03169063958581e59060240160e060405180830381865afa158015610e56573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e7a91906120b7565b60808301526040805160126020820181905291810191909152600090610ea4908590606001610523565b90506000610eb3600283612082565b9050610ecb846080015160600151856000015161176b565b6080850151518551610edd919061176b565b1115610f7b5760808085015160c0810151910151610efb9083612042565b610f059190612082565b858881518110610f1757610f17612016565b6020908102919091010152608084015160c081015160a090910151610f3c9083612042565b610f469190612082565b858d81518110610f5857610f58612016565b60200260200101818152505083608001516060015184602001818152505061100c565b608084015160c0810151602090910151610f959083612042565b610f9f9190612082565b858881518110610fb157610fb1612016565b6020908102919091010152608084015160c0810151604090910151610fd69083612042565b610fe09190612082565b858d81518110610ff257610ff2612016565b602090810291909101810191909152608085015151908501525b608084015160a081015160409091015161102690826120a4565b868e8151811061103857611038612016565b602002602001015161104a9190612042565b6110549190612082565b606085015260005b60ff8110156113825761107b85878f8151811061089257610892612016565b868e8151811061108d5761108d612016565b6020026020010181815250506110d68689856012806040516020016110c292919060ff92831681529116602082015260400190565b604051602081830303815290604052610b87565b8689815181106110e8576110e8612016565b60200260200101818152505061110086898f86611671565b60408601819052602086015110801561111d575084516040860151115b156111b85781868e8151811061113557611135612016565b6020026020010151670de0b6b3a76400006111509190612042565b61115a9190612082565b6080860151604001528551829087908a90811061117957611179612016565b6020026020010151670de0b6b3a76400006111949190612042565b61119e9190612082565b60808601805160200191909152604086015190515261126e565b846020015185604001511080156111d3575084516040860151105b1561126e5781868e815181106111eb576111eb612016565b6020026020010151670de0b6b3a76400006112069190612042565b6112109190612082565b608086015160a001528551829087908a90811061122f5761122f612016565b6020026020010151670de0b6b3a764000061124a9190612042565b6112549190612082565b608080870180519091019190915260408601519051606001525b608085015160a081015160409091015161128890826120a4565b878f8151811061129a5761129a612016565b60200260200101516112ac9190612042565b6112b69190612082565b6060860152604085015160208601819052855110156113505784516020860151600a916112e2916120a4565b1161134b57868d815181106112f9576112f9612016565b6020026020010151601261130d91906120a4565b61131890600a612225565b868e8151811061132a5761132a612016565b602002602001015161133c9190612082565b985050505050505050506113db565b61137a565b60208501518551600a91611363916120a4565b1161137a57868d815181106112f9576112f9612016565b60010161105c565b5060405162461bcd60e51b815260206004820152602760248201527f4e6f6e20636f6e76657267656e63653a2063616c635265736572766541745261604482015266074696f537761760cc1b60648201526084016104d6565b95945050505050565b6060600080838060200190518101906113fd9190612231565b915091508160000361140e57601291505b8060000361141a575060125b60128211806114295750601281115b156114475760405163686d360760e01b815260040160405180910390fd5b6040805160028082526060820183529091602083019080368337019050509250818360008151811061147b5761147b612016565b602002602001018181525050808360018151811061149b5761149b612016565b6020026020010181815250505050919050565b620f42405b9392505050565b6040805160028082526060808301845292602083019080368337019050509050611501868660008181106114f0576114f0612016565b8a9260209091020135905086611941565b8160008151811061151457611514612016565b602002602001018181525050611536868660018181106114f0576114f0612016565b8160018151811061154957611549612016565b6020026020010181815250509695505050505050565b60408051600280825260608083018452926020830190803683370190505090508160008151811061159257611592612016565b602002602001015160126115a691906120a4565b6115b190600a612225565b836000815181106115c4576115c4612016565b60200260200101516115d69190612042565b816000815181106115e9576115e9612016565b6020026020010181815250508160018151811061160857611608612016565b6020026020010151601261161c91906120a4565b61162790600a612225565b8360018151811061163a5761163a612016565b602002602001015161164c9190612042565b8160018151811061165f5761165f612016565b60200260200101818152505092915050565b604080516002808252606082018352600092839291906020830190803683370190505090508585815181106116a8576116a8612016565b60200260200101518186815181106116c2576116c2612016565b602002602001018181525050620f42408685815181106116e4576116e4612016565b60200260200101516116f69190612059565b81858151811061170857611708612016565b60200260200101818152505061173d8186856012806040516020016110c292919060ff92831681529116602082015260400190565b81868151811061174f5761174f612016565b602002602001015161176191906120a4565b9695505050505050565b600081830361177c575060006104df565b60008284116117945761178f84846120a4565b61179e565b61179e83856120a4565b9050600060026117ae8587612059565b6117b89190612082565b9050806117c6836064612042565b6117d890670de0b6b3a7640000612042565b6113db9190612082565b60008260200151836000015111156118455760808301516060810151905161180a91906120a4565b6020840151845161181b91906120a4565b846060015161182a9190612042565b6118349190612082565b61183e90836120a4565b90506104df565b60808301516060810151905161185b91906120a4565b8351602085015161186c91906120a4565b846060015161187b9190612042565b6118859190612082565b61183e9083612059565b60008061189d600286612042565b6064856118ab600287612042565b6118b58880612042565b6118bf9190612082565b6118c99190612042565b6118d39190612042565b6118dd9190612082565b9150846118eb606486612042565b6118f59190612082565b6118ff9084612059565b9050935093915050565b60008184611918876002612042565b6119229190612059565b61192c91906120a4565b836119378780612042565b6117d89190612059565b600080806000198587098587029250828110838203039150508060000361197b578382816119715761197161206c565b04925050506114b3565b8084116119c25760405162461bcd60e51b81526020600482015260156024820152744d6174683a206d756c446976206f766572666c6f7760581b60448201526064016104d6565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001611a926040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b905290565b602081526000825180602084015260005b81811015611ac55760208186018101516040868401015201611aa8565b506000604082850101526040601f19601f83011684010191505092915050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715611b2357611b23611ae5565b604052919050565b600082601f830112611b3c57600080fd5b81356001600160401b03811115611b5557611b55611ae5565b8060051b611b6560208201611afb565b91825260208185018101929081019086841115611b8157600080fd5b6020860192505b83831015611761578235825260209283019290910190611b88565b600082601f830112611bb457600080fd5b81356001600160401b03811115611bcd57611bcd611ae5565b611be0601f8201601f1916602001611afb565b818152846020838601011115611bf557600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215611c2557600080fd5b82356001600160401b03811115611c3b57600080fd5b611c4785828601611b2b565b92505060208301356001600160401b03811115611c6357600080fd5b611c6f85828601611ba3565b9150509250929050565b60008060008060808587031215611c8f57600080fd5b84356001600160401b03811115611ca557600080fd5b611cb187828801611b2b565b945050602085013592506040850135915060608501356001600160401b03811115611cdb57600080fd5b611ce787828801611ba3565b91505092959194509250565b60008083601f840112611d0557600080fd5b5081356001600160401b03811115611d1c57600080fd5b6020830191508360208260051b8501011115611d3757600080fd5b9250929050565b60008083601f840112611d5057600080fd5b5081356001600160401b03811115611d6757600080fd5b602083019150836020828501011115611d3757600080fd5b60008060008060008060006080888a031215611d9a57600080fd5b87356001600160401b03811115611db057600080fd5b611dbc8a828b01611cf3565b9098509650506020880135945060408801356001600160401b03811115611de257600080fd5b611dee8a828b01611cf3565b90955093505060608801356001600160401b03811115611e0d57600080fd5b611e198a828b01611d3e565b989b979a50959850939692959293505050565b600080600080600060808688031215611e4457600080fd5b85356001600160401b03811115611e5a57600080fd5b611e6688828901611b2b565b9550506020860135935060408601356001600160401b03811115611e8957600080fd5b611e9588828901611b2b565b93505060608601356001600160401b03811115611eb157600080fd5b611ebd88828901611d3e565b969995985093965092949392505050565b600060208284031215611ee057600080fd5b81356001600160401b03811115611ef657600080fd5b610d1884828501611ba3565b602080825282518282018190526000918401906040840190835b81811015611f3a578351835260209384019390920191600101611f1c565b509095945050505050565b600080600060408486031215611f5a57600080fd5b8335925060208401356001600160401b03811115611f7757600080fd5b611f8386828701611d3e565b9497909650939450505050565b60008060008060008060808789031215611fa957600080fd5b8635955060208701356001600160401b03811115611fc657600080fd5b611fd289828a01611cf3565b9096509450506040870135925060608701356001600160401b03811115611ff857600080fd5b61200489828a01611d3e565b979a9699509497509295939492505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176104df576104df61202c565b808201808211156104df576104df61202c565b634e487b7160e01b600052601260045260246000fd5b60008261209f57634e487b7160e01b600052601260045260246000fd5b500490565b818103818111156104df576104df61202c565b600060e08284031280156120ca57600080fd5b5060405160009060e081016001600160401b03811182821017156120f0576120f0611ae5565b604090815284518252602080860151908301528481015190820152606080850151908201526080808501519082015260a0808501519082015260c09384015193810193909352509092915050565b6001815b60018411156121795780850481111561215d5761215d61202c565b600184161561216b57908102905b60019390931c928002612142565b935093915050565b600082612190575060016104df565b8161219d575060006104df565b81600181146121b357600281146121bd576121d9565b60019150506104df565b60ff8411156121ce576121ce61202c565b50506001821b6104df565b5060208310610133831016604e8410600b84101617156121fc575081810a6104df565b612209600019848461213e565b806000190482111561221d5761221d61202c565b029392505050565b60006114b38383612181565b6000806040838503121561224457600080fd5b50508051602090910151909290915056fea2646970667358221220cfe4388fe2d77062c4ef77da9a3148be16e4200f959eb1c57626d8e2723c7c2a64736f6c634300081a0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000BA51055dAD14d3920e1798D2e8A152d91CaDb461

-----Decoded View---------------
Arg [0] : lut (address): 0xBA51055dAD14d3920e1798D2e8A152d91CaDb461

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000BA51055dAD14d3920e1798D2e8A152d91CaDb461


Loading...
Loading
Loading...
Loading
[ 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.