ETH Price: $2,869.20 (-3.05%)

Contract

0xba150052e11591D0648b17A0E608511874921CBC

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

TokenTracker

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Parent Transaction Hash Block From To
2586983422024-09-29 20:56:11482 days ago1727643371  Contract Creation0 ETH

Cross-Chain Transactions
Loading...
Loading

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


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.