ETH Price: $2,578.26 (-2.57%)

Contract

0xF89A1713998593A441cdA571780F0900Dbef20f9
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x61251661176723772023-07-11 19:13:11406 days ago1689102791IN
 Create: GyroECLPMath
0 ETH0.0630808230

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
GyroECLPMath

Compiler Version
v0.7.6+commit.7338295f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 12 : GyroECLPMath.sol
// SPDX-License-Identifier: LicenseRef-Gyro-1.0
// for information on licensing please see the README in the GitHub repository <https://github.com/gyrostable/concentrated-lps>.
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

// import "@balancer-labs/v2-solidity-utils/contracts/math/FixedPoint.sol";
import "../../libraries/GyroFixedPoint.sol";
import "../../libraries/GyroErrors.sol";
import "../../libraries/SignedFixedPoint.sol";
import "../../libraries/GyroPoolMath.sol";
import "./GyroECLPPoolErrors.sol";
import "@balancer-labs/v2-solidity-utils/contracts/math/Math.sol";
import "@balancer-labs/v2-solidity-utils/contracts/helpers/InputHelpers.sol";
import "@openzeppelin/contracts/utils/SafeCast.sol";

// solhint-disable private-vars-leading-underscore

/** @dev ECLP math library. Pretty much a direct translation of the python version (see `tests/`).
 * We use *signed* values here because some of the intermediate results can be negative (e.g. coordinates of points in
 * the untransformed circle, "prices" in the untransformed circle).
 */
library GyroECLPMath {
    uint256 internal constant ONEHALF = 0.5e18;
    int256 internal constant ONE = 1e18; // 18 decimal places
    int256 internal constant ONE_XP = 1e38; // 38 decimal places

    using SignedFixedPoint for int256;
    using GyroFixedPoint for uint256;
    using SafeCast for uint256;
    using SafeCast for int256;

    // Anti-overflow limits: Params and DerivedParams (static, only needs to be checked on pool creation)
    int256 internal constant _ROTATION_VECTOR_NORM_ACCURACY = 1e3; // 1e-15 in normal precision
    int256 internal constant _MAX_STRETCH_FACTOR = 1e26; // 1e8   in normal precision
    int256 internal constant _DERIVED_TAU_NORM_ACCURACY_XP = 1e23; // 1e-15 in extra precision
    int256 internal constant _MAX_INV_INVARIANT_DENOMINATOR_XP = 1e43; // 1e5   in extra precision
    int256 internal constant _DERIVED_DSQ_NORM_ACCURACY_XP = 1e23; // 1e-15 in extra precision

    // Anti-overflow limits: Dynamic values (checked before operations that use them)
    int256 internal constant _MAX_BALANCES = 1e34; // 1e16 in normal precision
    int256 internal constant _MAX_INVARIANT = 3e37; // 3e19 in normal precision

    // Note that all t values (not tp or tpp) could consist of uint's, as could all Params. But it's complicated to
    // convert all the time, so we make them all signed. We also store all intermediate values signed. An exception are
    // the functions that are used by the contract b/c there the values are stored unsigned.
    struct Params {
        // Price bounds (lower and upper). 0 < alpha < beta
        int256 alpha;
        int256 beta;
        // Rotation vector:
        // phi in (-90 degrees, 0] is the implicit rotation vector. It's stored as a point:
        int256 c; // c = cos(-phi) >= 0. rounded to 18 decimals
        int256 s; //  s = sin(-phi) >= 0. rounded to 18 decimals
        // Invariant: c^2 + s^2 == 1, i.e., the point (c, s) is normalized.
        // due to rounding, this may not = 1. The term dSq in DerivedParams corrects for this in extra precision

        // Stretching factor:
        int256 lambda; // lambda >= 1 where lambda == 1 is the circle.
    }

    // terms in this struct are stored in extra precision (38 decimals) with final decimal rounded down
    struct DerivedParams {
        Vector2 tauAlpha;
        Vector2 tauBeta;
        int256 u; // from (A chi)_y = lambda * u + v
        int256 v; // from (A chi)_y = lambda * u + v
        int256 w; // from (A chi)_x = w / lambda + z
        int256 z; // from (A chi)_x = w / lambda + z
        int256 dSq; // error in c^2 + s^2 = dSq, used to correct errors in c, s, tau, u,v,w,z calculations
        //int256 dAlpha; // normalization constant for tau(alpha)
        //int256 dBeta; // normalization constant for tau(beta)
    }

    struct Vector2 {
        int256 x;
        int256 y;
    }

    struct QParams {
        int256 a;
        int256 b;
        int256 c;
    }

    /** @dev Enforces limits and approximate normalization of the rotation vector. */
    function validateParams(Params memory params) internal pure {
        _grequire(0 <= params.s && params.s <= ONE, GyroECLPPoolErrors.ROTATION_VECTOR_WRONG);
        _grequire(0 <= params.c && params.c <= ONE, GyroECLPPoolErrors.ROTATION_VECTOR_WRONG);

        Vector2 memory sc = Vector2(params.s, params.c);
        int256 scnorm2 = scalarProd(sc, sc); // squared norm
        _grequire(
            ONE - _ROTATION_VECTOR_NORM_ACCURACY <= scnorm2 && scnorm2 <= ONE + _ROTATION_VECTOR_NORM_ACCURACY,
            GyroECLPPoolErrors.ROTATION_VECTOR_NOT_NORMALIZED
        );

        _grequire(0 <= params.lambda && params.lambda <= _MAX_STRETCH_FACTOR, GyroECLPPoolErrors.STRETCHING_FACTOR_WRONG);
    }

    /** @dev Enforces limits and approximate normalization of the derived values.
    Does NOT check for internal consistency of 'derived' with 'params'. */
    function validateDerivedParamsLimits(Params memory params, DerivedParams memory derived) external pure {
        int256 norm2;
        norm2 = scalarProdXp(derived.tauAlpha, derived.tauAlpha);
        _grequire(
            ONE_XP - _DERIVED_TAU_NORM_ACCURACY_XP <= norm2 && norm2 <= ONE_XP + _DERIVED_TAU_NORM_ACCURACY_XP,
            GyroECLPPoolErrors.DERIVED_TAU_NOT_NORMALIZED
        );
        norm2 = scalarProdXp(derived.tauBeta, derived.tauBeta);
        _grequire(
            ONE_XP - _DERIVED_TAU_NORM_ACCURACY_XP <= norm2 && norm2 <= ONE_XP + _DERIVED_TAU_NORM_ACCURACY_XP,
            GyroECLPPoolErrors.DERIVED_TAU_NOT_NORMALIZED
        );

        _grequire(derived.u <= ONE_XP, GyroECLPPoolErrors.DERIVED_UVWZ_WRONG);
        _grequire(derived.v <= ONE_XP, GyroECLPPoolErrors.DERIVED_UVWZ_WRONG);
        _grequire(derived.w <= ONE_XP, GyroECLPPoolErrors.DERIVED_UVWZ_WRONG);
        _grequire(derived.z <= ONE_XP, GyroECLPPoolErrors.DERIVED_UVWZ_WRONG);

        _grequire(
            ONE_XP - _DERIVED_DSQ_NORM_ACCURACY_XP <= derived.dSq && derived.dSq <= ONE_XP + _DERIVED_DSQ_NORM_ACCURACY_XP,
            GyroECLPPoolErrors.DERIVED_DSQ_WRONG
        );

        // NB No anti-overflow checks are required given the checks done above and in validateParams().
        int256 mulDenominator = ONE_XP.divXpU(calcAChiAChiInXp(params, derived) - ONE_XP);
        _grequire(mulDenominator <= _MAX_INV_INVARIANT_DENOMINATOR_XP, GyroECLPPoolErrors.INVARIANT_DENOMINATOR_WRONG);
    }

    function scalarProd(Vector2 memory t1, Vector2 memory t2) internal pure returns (int256 ret) {
        ret = t1.x.mulDownMag(t2.x).add(t1.y.mulDownMag(t2.y));
    }

    /// @dev scalar product for extra-precision values
    function scalarProdXp(Vector2 memory t1, Vector2 memory t2) internal pure returns (int256 ret) {
        ret = t1.x.mulXp(t2.x).add(t1.y.mulXp(t2.y));
    }

    // "Methods" for Params. We could put these into a separate library and import them via 'using' to get method call
    // syntax.

    /** @dev Calculate A t where A is given in Section 2.2
     *  This is reversing rotation and scaling of the ellipse (mapping back to circle) */
    function mulA(Params memory params, Vector2 memory tp) internal pure returns (Vector2 memory t) {
        // NB: This function is only used inside calculatePrice(). This is why we can make two simplifications:
        // 1. We don't correct for precision of s, c using d.dSq because that level of precision is not important in this context.
        // 2. We don't need to check for over/underflow b/c these are impossible in that context and given the (checked) assumptions on the various values.
        t.x = params.c.mulDownMagU(tp.x).divDownMagU(params.lambda) - params.s.mulDownMagU(tp.y).divDownMagU(params.lambda);
        t.y = params.s.mulDownMagU(tp.x) + params.c.mulDownMagU(tp.y);
    }

    /** @dev Calculate virtual offset a given invariant r.
     *  See calculation in Section 2.1.2 Computing reserve offsets
     *  Note that, in contrast to virtual reserve offsets in CPMM, these are *subtracted* from the real
     *  reserves, moving the curve to the upper-right. They can be positive or negative, but not both can be negative.
     *  Calculates a = r*(A^{-1}tau(beta))_x rounding up in signed direction
     *  Notice that error in r is scaled by lambda, and so rounding direction is important */
    function virtualOffset0(
        Params memory p,
        DerivedParams memory d,
        Vector2 memory r // overestimate in x component, underestimate in y
    ) internal pure returns (int256 a) {
        // a = r lambda c tau(beta)_x + rs tau(beta)_y
        //       account for 1 factors of dSq (2 s,c factors)
        int256 termXp = d.tauBeta.x.divXpU(d.dSq);
        a = d.tauBeta.x > 0
            ? r.x.mulUpMagU(p.lambda).mulUpMagU(p.c).mulUpXpToNpU(termXp)
            : r.y.mulDownMagU(p.lambda).mulDownMagU(p.c).mulUpXpToNpU(termXp);

        // use fact that tau(beta)_y > 0, so the required rounding direction is clear.
        a = a + r.x.mulUpMagU(p.s).mulUpXpToNpU(d.tauBeta.y.divXpU(d.dSq));
    }

    /** @dev calculate virtual offset b given invariant r.
     *  Calculates b = r*(A^{-1}tau(alpha))_y rounding up in signed direction */
    function virtualOffset1(
        Params memory p,
        DerivedParams memory d,
        Vector2 memory r // overestimate in x component, underestimate in y
    ) internal pure returns (int256 b) {
        // b = -r \lambda s tau(alpha)_x + rc tau(alpha)_y
        //       account for 1 factors of dSq (2 s,c factors)
        int256 termXp = d.tauAlpha.x.divXpU(d.dSq);
        b = (d.tauAlpha.x < 0)
            ? r.x.mulUpMagU(p.lambda).mulUpMagU(p.s).mulUpXpToNpU(-termXp)
            : (-r.y).mulDownMagU(p.lambda).mulDownMagU(p.s).mulUpXpToNpU(termXp);

        // use fact that tau(alpha)_y > 0, so the required rounding direction is clear.
        b = b + r.x.mulUpMagU(p.c).mulUpXpToNpU(d.tauAlpha.y.divXpU(d.dSq));
    }

    /** Maximal value for the real reserves x when the respective other balance is 0 for given invariant
     *  See calculation in Section 2.1.2. Calculation is ordered here for precision, but error in r is magnified by lambda
     *  Rounds down in signed direction */
    function maxBalances0(
        Params memory p,
        DerivedParams memory d,
        Vector2 memory r // overestimate in x-component, underestimate in y-component
    ) internal pure returns (int256 xp) {
        // x^+ = r lambda c (tau(beta)_x - tau(alpha)_x) + rs (tau(beta)_y - tau(alpha)_y)
        //      account for 1 factors of dSq (2 s,c factors)
        int256 termXp1 = (d.tauBeta.x - d.tauAlpha.x).divXpU(d.dSq); // note tauBeta.x > tauAlpha.x, so this is > 0 and rounding direction is clear
        int256 termXp2 = (d.tauBeta.y - d.tauAlpha.y).divXpU(d.dSq); // note this may be negative, but since tauBeta.y, tauAlpha.y >= 0, it is always in [-1, 1].
        xp = r.y.mulDownMagU(p.lambda).mulDownMagU(p.c).mulDownXpToNpU(termXp1);
        xp = xp + (termXp2 > 0 ? r.y.mulDownMagU(p.s) : r.x.mulUpMagU(p.s)).mulDownXpToNpU(termXp2);
    }

    /** Maximal value for the real reserves y when the respective other balance is 0 for given invariant
     *  See calculation in Section 2.1.2. Calculation is ordered here for precision, but erorr in r is magnified by lambda
     *  Rounds down in signed direction */
    function maxBalances1(
        Params memory p,
        DerivedParams memory d,
        Vector2 memory r // overestimate in x-component, underestimate in y-component
    ) internal pure returns (int256 yp) {
        // y^+ = r lambda s (tau(beta)_x - tau(alpha)_x) + rc (tau(alpha)_y - tau(beta)_y)
        //      account for 1 factors of dSq (2 s,c factors)
        int256 termXp1 = (d.tauBeta.x - d.tauAlpha.x).divXpU(d.dSq); // note tauBeta.x > tauAlpha.x
        int256 termXp2 = (d.tauAlpha.y - d.tauBeta.y).divXpU(d.dSq);
        yp = r.y.mulDownMagU(p.lambda).mulDownMagU(p.s).mulDownXpToNpU(termXp1);
        yp = yp + (termXp2 > 0 ? r.y.mulDownMagU(p.c) : r.x.mulUpMagU(p.c)).mulDownXpToNpU(termXp2);
    }

    /** @dev Compute the invariant 'r' corresponding to the given values. The invariant can't be negative, but
     *  we use a signed value to store it because all the other calculations are happening with signed ints, too.
     *  Computes r according to Prop 13 in 2.2.1 Initialization from Real Reserves
     *  orders operations to achieve best precision
     *  Returns an underestimate and a bound on error size.
     *  Enforces anti-overflow limits on balances and the computed invariant in the process. */
    function calculateInvariantWithError(
        uint256[] memory balances,
        Params memory params,
        DerivedParams memory derived
    ) public pure returns (int256, int256) {
        (int256 x, int256 y) = (balances[0].toInt256(), balances[1].toInt256());
        _grequire(x.add(y) <= _MAX_BALANCES, GyroECLPPoolErrors.MAX_ASSETS_EXCEEDED);

        int256 AtAChi = calcAtAChi(x, y, params, derived);
        (int256 sqrt, int256 err) = calcInvariantSqrt(x, y, params, derived);
        // calculate the error in the square root term, separates cases based on sqrt >= 1/2
        // somedayTODO: can this be improved for cases of large balances (when xp error magnifies to np)
        // Note: the minimum non-zero value of sqrt is 1e-9 since the minimum argument is 1e-18
        if (sqrt > 0) {
            // err + 1 to account for O(eps_np) term ignored before
            err = (err + 1).divUpMagU(2 * sqrt);
        } else {
            // in the false case here, the extra precision error does not magnify, and so the error inside the sqrt is O(1e-18)
            // somedayTODO: The true case will almost surely never happen (can it be removed)
            err = err > 0 ? GyroPoolMath._sqrt(err.toUint256(), 5).toInt256() : 1e9;
        }
        // calculate the error in the numerator, scale the error by 20 to be sure all possible terms accounted for
        err = ((params.lambda.mulUpMagU(x + y) / ONE_XP) + err + 1) * 20;

        // A chi \cdot A chi > 1, so round it up to round denominator up
        // denominator uses extra precision, so we do * 1/denominator so we are sure the calculation doesn't overflow
        int256 mulDenominator = ONE_XP.divXpU(calcAChiAChiInXp(params, derived) - ONE_XP);
        // NOTE: Anti-overflow limits on mulDenominator are checked on contract creation.

        // as alternative, could do, but could overflow: invariant = (AtAChi.add(sqrt) - err).divXp(denominator);
        int256 invariant = (AtAChi + sqrt - err).mulDownXpToNpU(mulDenominator);
        // error scales if denominator is small
        // NB: This error calculation computes the error in the expression "numerator / denominator", but in this code
        // we actually use the formula "numerator * (1 / denominator)" to compute the invariant. This affects this line
        // and the one below.
        err = err.mulUpXpToNpU(mulDenominator);
        // account for relative error due to error in the denominator
        // error in denominator is O(epsilon) if lambda<1e11, scale up by 10 to be sure we catch it, and add O(eps)
        // error in denominator is lambda^2 * 2e-37 and scales relative to the result / denominator
        // Scale by a constant to account for errors in the scaling factor itself and limited compounding.
        // calculating lambda^2 w/o decimals so that the calculation will never overflow, the lost precision isn't important
        err = err + ((invariant.mulUpXpToNpU(mulDenominator) * ((params.lambda * params.lambda) / 1e36)) * 40) / ONE_XP + 1;

        _grequire(invariant.add(err) <= _MAX_INVARIANT, GyroECLPPoolErrors.MAX_INVARIANT_EXCEEDED);

        return (invariant, err);
    }

    function calculateInvariant(
        uint256[] memory balances,
        Params memory params,
        DerivedParams memory derived
    ) external pure returns (uint256 uinvariant) {
        (int256 invariant, ) = calculateInvariantWithError(balances, params, derived);
        uinvariant = invariant.toUint256();
    }

    /// @dev calculate At \cdot A chi, ignores rounding direction. We will later compensate for the rounding error.
    function calcAtAChi(
        int256 x,
        int256 y,
        Params memory p,
        DerivedParams memory d
    ) internal pure returns (int256 val) {
        // to save gas, pre-compute dSq^2 as it will be used 3 times
        int256 dSq2 = d.dSq.mulXpU(d.dSq);

        // (cx - sy) * (w/lambda + z) / lambda
        //      account for 2 factors of dSq (4 s,c factors)
        int256 termXp = (d.w.divDownMagU(p.lambda) + d.z).divDownMagU(p.lambda).divXpU(dSq2);
        val = (x.mulDownMagU(p.c) - y.mulDownMagU(p.s)).mulDownXpToNpU(termXp);

        // (x lambda s + y lambda c) * u, note u > 0
        int256 termNp = x.mulDownMagU(p.lambda).mulDownMagU(p.s) + y.mulDownMagU(p.lambda).mulDownMagU(p.c);
        val = val + termNp.mulDownXpToNpU(d.u.divXpU(dSq2));

        // (sx+cy) * v, note v > 0
        termNp = x.mulDownMagU(p.s) + y.mulDownMagU(p.c);
        val = val + termNp.mulDownXpToNpU(d.v.divXpU(dSq2));
    }

    /// @dev calculates A chi \cdot A chi in extra precision
    /// Note: this can be >1 (and involves factor of lambda^2). We can compute it in extra precision w/o overflowing b/c it will be
    /// at most 38 + 16 digits (38 from decimals, 2*8 from lambda^2 if lambda=1e8)
    /// Since we will only divide by this later, we will not need to worry about overflow in that operation if done in the right way
    /// TODO: is rounding direction ok?
    function calcAChiAChiInXp(Params memory p, DerivedParams memory d) internal pure returns (int256 val) {
        // to save gas, pre-compute dSq^3 as it will be used 4 times
        int256 dSq3 = d.dSq.mulXpU(d.dSq).mulXpU(d.dSq);

        // (A chi)_y^2 = lambda^2 u^2 + lambda 2 u v + v^2
        //      account for 3 factors of dSq (6 s,c factors)
        // SOMEDAY: In these calcs, a calculated value is multiplied by lambda and lambda^2, resp, which implies some
        // error amplification. It's fine b/c we're doing it in extra precision here, but would still be nice if it
        // could be avoided, perhaps by splitting up the numbers into a high and low part.
        val = p.lambda.mulUpMagU((2 * d.u).mulXpU(d.v).divXpU(dSq3));
        // for lambda^2 u^2 factor in rounding error in u since lambda could be big
        // Note: lambda^2 is multiplied at the end to be sure the calculation doesn't overflow, but this can lose some precision
        val = val + ((d.u + 1).mulXpU(d.u + 1).divXpU(dSq3)).mulUpMagU(p.lambda).mulUpMagU(p.lambda);
        // the next line converts from extre precision to normal precision post-computation while rounding up
        val = val + (d.v).mulXpU(d.v).divXpU(dSq3);

        // (A chi)_x^2 = (w/lambda + z)^2
        //      account for 3 factors of dSq (6 s,c factors)
        int256 termXp = d.w.divUpMagU(p.lambda) + d.z;
        val = val + termXp.mulXpU(termXp).divXpU(dSq3);
    }

    /// @dev calculate -(At)_x ^2 (A chi)_y ^2 + (At)_x ^2, rounding down in signed direction
    function calcMinAtxAChiySqPlusAtxSq(
        int256 x,
        int256 y,
        Params memory p,
        DerivedParams memory d
    ) internal pure returns (int256 val) {
        ////////////////////////////////////////////////////////////////////////////////////
        // (At)_x^2 (A chi)_y^2 = (x^2 c^2 - xy2sc + y^2 s^2) (u^2 + 2uv/lambda + v^2/lambda^2)
        //      account for 4 factors of dSq (8 s,c factors)
        //
        // (At)_x^2 = (x^2 c^2 - xy2sc + y^2 s^2)/lambda^2
        //      account for 1 factor of dSq (2 s,c factors)
        ////////////////////////////////////////////////////////////////////////////////////
        int256 termNp = x.mulUpMagU(x).mulUpMagU(p.c).mulUpMagU(p.c) + y.mulUpMagU(y).mulUpMagU(p.s).mulUpMagU(p.s);
        termNp = termNp - x.mulDownMagU(y).mulDownMagU(p.c * 2).mulDownMagU(p.s);

        int256 termXp = d.u.mulXpU(d.u) + (2 * d.u).mulXpU(d.v).divDownMagU(p.lambda) + d.v.mulXpU(d.v).divDownMagU(p.lambda).divDownMagU(p.lambda);
        termXp = termXp.divXpU(d.dSq.mulXpU(d.dSq).mulXpU(d.dSq).mulXpU(d.dSq));
        val = (-termNp).mulDownXpToNpU(termXp);

        // now calculate (At)_x^2 accounting for possible rounding error to round down
        // need to do 1/dSq in a way so that there is no overflow for large balances
        val = val + (termNp - 9).divDownMagU(p.lambda).divDownMagU(p.lambda).mulDownXpToNpU(SignedFixedPoint.ONE_XP.divXpU(d.dSq));
    }

    /// @dev calculate 2(At)_x * (At)_y * (A chi)_x * (A chi)_y, ignores rounding direction
    //  Note: this ignores rounding direction and is corrected for later
    function calc2AtxAtyAChixAChiy(
        int256 x,
        int256 y,
        Params memory p,
        DerivedParams memory d
    ) internal pure returns (int256 val) {
        ////////////////////////////////////////////////////////////////////////////////////
        // = ((x^2 - y^2)sc + yx(c^2-s^2)) * 2 * (zu + (wu + zv)/lambda + wv/lambda^2)
        //      account for 4 factors of dSq (8 s,c factors)
        ////////////////////////////////////////////////////////////////////////////////////
        int256 termNp = (x.mulDownMagU(x) - y.mulUpMagU(y)).mulDownMagU(2 * p.c).mulDownMagU(p.s);
        int256 xy = y.mulDownMagU(2 * x);
        termNp = termNp + xy.mulDownMagU(p.c).mulDownMagU(p.c) - xy.mulDownMagU(p.s).mulDownMagU(p.s);

        int256 termXp = d.z.mulXpU(d.u) + d.w.mulXpU(d.v).divDownMagU(p.lambda).divDownMagU(p.lambda);
        termXp = termXp + (d.w.mulXpU(d.u) + d.z.mulXpU(d.v)).divDownMagU(p.lambda);
        termXp = termXp.divXpU(d.dSq.mulXpU(d.dSq).mulXpU(d.dSq).mulXpU(d.dSq));

        val = termNp.mulDownXpToNpU(termXp);
    }

    /// @dev calculate -(At)_y ^2 (A chi)_x ^2 + (At)_y ^2, rounding down in signed direction
    function calcMinAtyAChixSqPlusAtySq(
        int256 x,
        int256 y,
        Params memory p,
        DerivedParams memory d
    ) internal pure returns (int256 val) {
        ////////////////////////////////////////////////////////////////////////////////////
        // (At)_y^2 (A chi)_x^2 = (x^2 s^2 + xy2sc + y^2 c^2) * (z^2 + 2zw/lambda + w^2/lambda^2)
        //      account for 4 factors of dSq (8 s,c factors)
        // (At)_y^2 = (x^2 s^2 + xy2sc + y^2 c^2)
        //      account for 1 factor of dSq (2 s,c factors)
        ////////////////////////////////////////////////////////////////////////////////////
        int256 termNp = x.mulUpMagU(x).mulUpMagU(p.s).mulUpMagU(p.s) + y.mulUpMagU(y).mulUpMagU(p.c).mulUpMagU(p.c);
        termNp = termNp + x.mulUpMagU(y).mulUpMagU(p.s * 2).mulUpMagU(p.c);

        int256 termXp = d.z.mulXpU(d.z) + d.w.mulXpU(d.w).divDownMagU(p.lambda).divDownMagU(p.lambda);
        termXp = termXp + (2 * d.z).mulXpU(d.w).divDownMagU(p.lambda);
        termXp = termXp.divXpU(d.dSq.mulXpU(d.dSq).mulXpU(d.dSq).mulXpU(d.dSq));
        val = (-termNp).mulDownXpToNpU(termXp);

        // now calculate (At)_y^2 accounting for possible rounding error to round down
        // need to do 1/dSq in a way so that there is no overflow for large balances
        val = val + (termNp - 9).mulDownXpToNpU(SignedFixedPoint.ONE_XP.divXpU(d.dSq));
    }

    /// @dev Rounds down. Also returns an estimate for the error of the term under the sqrt (!) and without the regular
    /// normal-precision error of O(1e-18).
    function calcInvariantSqrt(
        int256 x,
        int256 y,
        Params memory p,
        DerivedParams memory d
    ) internal pure returns (int256 val, int256 err) {
        val = calcMinAtxAChiySqPlusAtxSq(x, y, p, d) + calc2AtxAtyAChixAChiy(x, y, p, d);
        val = val + calcMinAtyAChixSqPlusAtySq(x, y, p, d);
        // error inside the square root is O((x^2 + y^2) * eps_xp) + O(eps_np), where eps_xp=1e-38, eps_np=1e-18
        // note that in terms of rounding down, error corrects for calc2AtxAtyAChixAChiy()
        // however, we also use this error to correct the invariant for an overestimate in swaps, it is all the same order though
        // Note the O(eps_np) term will be dealt with later, so not included yet
        // Note that the extra precision term doesn't propagate unless balances are > 100b
        err = (x.mulUpMagU(x) + y.mulUpMagU(y)) / 1e38;
        // we will account for the error later after the square root
        // mathematically, terms in square root > 0, so treat as 0 if it is < 0 b/c of rounding error
        val = val > 0 ? GyroPoolMath._sqrt(val.toUint256(), 5).toInt256() : 0;
    }

    /** @dev Spot price of token 0 in units of token 1.
     *  See Prop. 12 in 2.1.6 Computing Prices */
    function calcSpotPrice0in1(
        uint256[] memory balances,
        Params memory params,
        DerivedParams memory derived,
        int256 invariant
    ) external pure returns (uint256 px) {
        // shift by virtual offsets to get v(t)
        Vector2 memory r = Vector2(invariant, invariant); // ignore r rounding for spot price, precision will be lost in TWAP anyway
        Vector2 memory ab = Vector2(virtualOffset0(params, derived, r), virtualOffset1(params, derived, r));
        Vector2 memory vec = Vector2(balances[0].toInt256() - ab.x, balances[1].toInt256() - ab.y);

        // transform to circle to get Av(t)
        vec = mulA(params, vec);
        // compute prices on circle
        Vector2 memory pc = Vector2(vec.x.divDownMagU(vec.y), ONE);

        // Convert prices back to ellipse
        // NB: These operations check for overflow because the price pc[0] might be large when vex.y is small.
        // SOMEDAY I think this probably can't actually happen due to our bounds on the different values. In this case we could do this unchecked as well.
        int256 pgx = scalarProd(pc, mulA(params, Vector2(ONE, 0)));
        px = pgx.divDownMag(scalarProd(pc, mulA(params, Vector2(0, ONE)))).toUint256();
    }

    /** @dev Check that post-swap balances obey maximal asset bounds
     *  newBalance = post-swap balance of one asset
     *  assetIndex gives the index of the provided asset (0 = X, 1 = Y) */
    function checkAssetBounds(
        Params memory params,
        DerivedParams memory derived,
        Vector2 memory invariant,
        int256 newBal,
        uint8 assetIndex
    ) internal pure {
        if (assetIndex == 0) {
            int256 xPlus = maxBalances0(params, derived, invariant);
            if (!(newBal <= _MAX_BALANCES && newBal <= xPlus)) _grequire(false, GyroECLPPoolErrors.ASSET_BOUNDS_EXCEEDED);
            return;
        }
        {
            int256 yPlus = maxBalances1(params, derived, invariant);
            if (!(newBal <= _MAX_BALANCES && newBal <= yPlus)) _grequire(false, GyroECLPPoolErrors.ASSET_BOUNDS_EXCEEDED);
        }
    }

    function calcOutGivenIn(
        uint256[] memory balances,
        uint256 amountIn,
        bool tokenInIsToken0,
        Params memory params,
        DerivedParams memory derived,
        Vector2 memory invariant
    ) external pure returns (uint256 amountOut) {
        function(int256, Params memory, DerivedParams memory, Vector2 memory) pure returns (int256) calcGiven;
        uint8 ixIn;
        uint8 ixOut;
        if (tokenInIsToken0) {
            ixIn = 0;
            ixOut = 1;
            calcGiven = calcYGivenX;
        } else {
            ixIn = 1;
            ixOut = 0;
            calcGiven = calcXGivenY;
        }

        int256 balInNew = balances[ixIn].add(amountIn).toInt256(); // checked because amountIn is given by the user.
        checkAssetBounds(params, derived, invariant, balInNew, ixIn);
        int256 balOutNew = calcGiven(balInNew, params, derived, invariant);
        // Make sub checked as an extra check against numerical error; but this really should never happen
        amountOut = balances[ixOut].sub(balOutNew.toUint256());
        // The above line guarantees that amountOut <= balances[ixOut].
    }

    function calcInGivenOut(
        uint256[] memory balances,
        uint256 amountOut,
        bool tokenInIsToken0,
        Params memory params,
        DerivedParams memory derived,
        Vector2 memory invariant
    ) external pure returns (uint256 amountIn) {
        function(int256, Params memory, DerivedParams memory, Vector2 memory) pure returns (int256) calcGiven;
        uint8 ixIn;
        uint8 ixOut;
        if (tokenInIsToken0) {
            ixIn = 0;
            ixOut = 1;
            calcGiven = calcXGivenY; // this reverses compared to calcOutGivenIn
        } else {
            ixIn = 1;
            ixOut = 0;
            calcGiven = calcYGivenX; // this reverses compared to calcOutGivenIn
        }

        if (!(amountOut <= balances[ixOut])) _grequire(false, GyroECLPPoolErrors.ASSET_BOUNDS_EXCEEDED);
        int256 balOutNew = (balances[ixOut] - amountOut).toInt256();
        int256 balInNew = calcGiven(balOutNew, params, derived, invariant);
        // The checks in the following two lines should really always succeed; we keep them as extra safety against numerical error.
        checkAssetBounds(params, derived, invariant, balInNew, ixIn);
        amountIn = balInNew.toUint256().sub(balances[ixIn]);
    }

    /** @dev Variables are named for calculating y given x
     *  to calculate x given y, change x->y, s->c, c->s, a_>b, b->a, tauBeta.x -> -tauAlpha.x, tauBeta.y -> tauAlpha.y
     *  calculates an overestimate of calculated reserve post-swap */
    function solveQuadraticSwap(
        int256 lambda,
        int256 x,
        int256 s,
        int256 c,
        Vector2 memory r, // overestimate in x component, underestimate in y
        Vector2 memory ab,
        Vector2 memory tauBeta,
        int256 dSq
    ) internal pure returns (int256) {
        // x component will round up, y will round down, use extra precision
        Vector2 memory lamBar;
        lamBar.x = SignedFixedPoint.ONE_XP - SignedFixedPoint.ONE_XP.divDownMagU(lambda).divDownMagU(lambda);
        // Note: The following cannot become negative even with errors because we require lambda >= 1 and
        // divUpMag returns the exact result if the quotient is representable in 18 decimals.
        lamBar.y = SignedFixedPoint.ONE_XP - SignedFixedPoint.ONE_XP.divUpMagU(lambda).divUpMagU(lambda);
        // using qparams struct to avoid "stack too deep"
        QParams memory q;
        // shift by the virtual offsets
        // note that we want an overestimate of offset here so that -x'*lambar*s*c is overestimated in signed direction
        // account for 1 factor of dSq (2 s,c factors)
        int256 xp = x - ab.x;
        if (xp > 0) {
            q.b = (-xp).mulDownMagU(s).mulDownMagU(c).mulUpXpToNpU(lamBar.y.divXpU(dSq));
        } else {
            q.b = (-xp).mulUpMagU(s).mulUpMagU(c).mulUpXpToNpU(lamBar.x.divXpU(dSq) + 1);
        }

        // x component will round up, y will round down, use extra precision
        // account for 1 factor of dSq (2 s,c factors)
        Vector2 memory sTerm;
        // we wil take sTerm = 1 - sTerm below, using multiple lines to avoid "stack too deep"
        sTerm.x = lamBar.y.mulDownMagU(s).mulDownMagU(s).divXpU(dSq);
        sTerm.y = lamBar.x.mulUpMagU(s);
        sTerm.y = sTerm.y.mulUpMagU(s).divXpU(dSq + 1) + 1; // account for rounding error in dSq, divXp
        sTerm = Vector2(SignedFixedPoint.ONE_XP - sTerm.x, SignedFixedPoint.ONE_XP - sTerm.y);
        // ^^ NB: The components of sTerm are non-negative: We only need to worry about sTerm.y. This is non-negative b/c, because of bounds on lambda lamBar <= 1 - 1e-16, and division by dSq ensures we have enough precision so that rounding errors are never magnitude 1e-16.

        // now compute the argument of the square root
        q.c = -calcXpXpDivLambdaLambda(x, r, lambda, s, c, tauBeta, dSq);
        q.c = q.c + r.y.mulDownMagU(r.y).mulDownXpToNpU(sTerm.y);
        // the square root is always being subtracted, so round it down to overestimate the end balance
        // mathematically, terms in square root > 0, so treat as 0 if it is < 0 b/c of rounding error
        q.c = q.c > 0 ? GyroPoolMath._sqrt(q.c.toUint256(), 5).toInt256() : 0;

        // calculate the result in q.a
        if (q.b - q.c > 0) {
            q.a = (q.b - q.c).mulUpXpToNpU(SignedFixedPoint.ONE_XP.divXpU(sTerm.y) + 1);
        } else {
            q.a = (q.b - q.c).mulUpXpToNpU(SignedFixedPoint.ONE_XP.divXpU(sTerm.x));
        }

        // lastly, add the offset, note that we want an overestimate of offset here
        return q.a + ab.y;
    }

    /** @dev Calculates x'x'/λ^2 where x' = x - b = x - r (A^{-1}tau(beta))_x
     *  calculates an overestimate
     *  to calculate y'y', change x->y, s->c, c->s, tauBeta.x -> -tauAlpha.x, tauBeta.y -> tauAlpha.y  */
    function calcXpXpDivLambdaLambda(
        int256 x,
        Vector2 memory r, // overestimate in x component, underestimate in y
        int256 lambda,
        int256 s,
        int256 c,
        Vector2 memory tauBeta,
        int256 dSq
    ) internal pure returns (int256) {
        //////////////////////////////////////////////////////////////////////////////////
        // x'x'/lambda^2 = r^2 c^2 tau(beta)_x^2
        //      + ( r^2 2s c tau(beta)_x tau(beta)_y - rx 2c tau(beta)_x ) / lambda
        //      + ( r^2 s^2 tau(beta)_y^2 - rx 2s tau(beta)_y + x^2 ) / lambda^2
        //////////////////////////////////////////////////////////////////////////////////
        // to save gas, pre-compute dSq^2 as it will be used 3 times, and r.x^2 as it will be used 2-3 times
        // sqVars = (dSq^2, r.x^2)
        Vector2 memory sqVars = Vector2(dSq.mulXpU(dSq), r.x.mulUpMagU(r.x));

        QParams memory q; // for working terms
        // q.a = r^2 s 2c tau(beta)_x tau(beta)_y
        //      account for 2 factors of dSq (4 s,c factors)
        int256 termXp = tauBeta.x.mulXpU(tauBeta.y).divXpU(sqVars.x);
        if (termXp > 0) {
            q.a = sqVars.y.mulUpMagU(2 * s);
            q.a = q.a.mulUpMagU(c).mulUpXpToNpU(termXp + 7); // +7 account for rounding in termXp
        } else {
            q.a = r.y.mulDownMagU(r.y).mulDownMagU(2 * s);
            q.a = q.a.mulDownMagU(c).mulUpXpToNpU(termXp);
        }

        // -rx 2c tau(beta)_x
        //      account for 1 factor of dSq (2 s,c factors)
        if (tauBeta.x < 0) {
            // +3 account for rounding in extra precision terms
            q.b = r.x.mulUpMagU(x).mulUpMagU(2 * c).mulUpXpToNpU(-tauBeta.x.divXpU(dSq) + 3);
        } else {
            q.b = (-r.y).mulDownMagU(x).mulDownMagU(2 * c).mulUpXpToNpU(tauBeta.x.divXpU(dSq));
        }
        // q.a later needs to be divided by lambda
        q.a = q.a + q.b;

        // q.b = r^2 s^2 tau(beta)_y^2
        //      account for 2 factors of dSq (4 s,c factors)
        termXp = tauBeta.y.mulXpU(tauBeta.y).divXpU(sqVars.x) + 7; // +7 account for rounding in termXp
        q.b = sqVars.y.mulUpMagU(s);
        q.b = q.b.mulUpMagU(s).mulUpXpToNpU(termXp);

        // q.c = -rx 2s tau(beta)_y, recall that tauBeta.y > 0 so round lower in magnitude
        //      account for 1 factor of dSq (2 s,c factors)
        q.c = (-r.y).mulDownMagU(x).mulDownMagU(2 * s).mulUpXpToNpU(tauBeta.y.divXpU(dSq));

        // (q.b + q.c + x^2) / lambda
        q.b = q.b + q.c + x.mulUpMagU(x);
        q.b = q.b > 0 ? q.b.divUpMagU(lambda) : q.b.divDownMagU(lambda);

        // remaining calculation is (q.a + q.b) / lambda
        q.a = q.a + q.b;
        q.a = q.a > 0 ? q.a.divUpMagU(lambda) : q.a.divDownMagU(lambda);

        // + r^2 c^2 tau(beta)_x^2
        //      account for 2 factors of dSq (4 s,c factors)
        termXp = tauBeta.x.mulXpU(tauBeta.x).divXpU(sqVars.x) + 7; // +7 account for rounding in termXp
        int256 val = sqVars.y.mulUpMagU(c).mulUpMagU(c);
        return (val.mulUpXpToNpU(termXp)) + q.a;
    }

    /** @dev compute y such that (x, y) satisfy the invariant at the given parameters.
     *  Note that we calculate an overestimate of y
     *   See Prop 14 in section 2.2.2 Trade Execution */
    function calcYGivenX(
        int256 x,
        Params memory params,
        DerivedParams memory d,
        Vector2 memory r // overestimate in x component, underestimate in y
    ) internal pure returns (int256 y) {
        // want to overestimate the virtual offsets except in a particular setting that will be corrected for later
        // note that the error correction in the invariant should more than make up for uncaught rounding directions (in 38 decimals) in virtual offsets
        Vector2 memory ab = Vector2(virtualOffset0(params, d, r), virtualOffset1(params, d, r));
        y = solveQuadraticSwap(params.lambda, x, params.s, params.c, r, ab, d.tauBeta, d.dSq);
    }

    function calcXGivenY(
        int256 y,
        Params memory params,
        DerivedParams memory d,
        Vector2 memory r // overestimate in x component, underestimate in y
    ) internal pure returns (int256 x) {
        // want to overestimate the virtual offsets except in a particular setting that will be corrected for later
        // note that the error correction in the invariant should more than make up for uncaught rounding directions (in 38 decimals) in virtual offsets
        Vector2 memory ba = Vector2(virtualOffset1(params, d, r), virtualOffset0(params, d, r));
        // change x->y, s->c, c->s, b->a, a->b, tauBeta.x -> -tauAlpha.x, tauBeta.y -> tauAlpha.y vs calcYGivenX
        x = solveQuadraticSwap(params.lambda, y, params.c, params.s, r, ba, Vector2(-d.tauAlpha.x, d.tauAlpha.y), d.dSq);
    }
}

File 2 of 12 : GyroFixedPoint.sol
// SPDX-License-Identifier: LicenseRef-Gyro-1.0
// for information on licensing please see the README in the GitHub repository <https://github.com/gyrostable/concentrated-lps>.

pragma solidity ^0.7.0;

import "@balancer-labs/v2-solidity-utils/contracts/math/LogExpMath.sol";
import "@balancer-labs/v2-solidity-utils/contracts/helpers/BalancerErrors.sol";
import "@balancer-labs/v2-solidity-utils/contracts/math/Math.sol";

/* solhint-disable private-vars-leading-underscore */

// Gyroscope: Copied from Balancer's FixedPoint library. We added a few additional functions and made _require()s more
// gas-efficient.
// We renamed this to `GyroFixedPoint` to avoid name clashes with functions used in other Balancer libraries we use.

library GyroFixedPoint {
    uint256 internal constant ONE = 1e18; // 18 decimal places
    uint256 internal constant MIDDECIMAL = 1e9; // splits the fixed point decimals into two equal parts.

    uint256 internal constant MAX_POW_RELATIVE_ERROR = 10000; // 10^(-14)

    // Minimum base for the power function when the exponent is 'free' (larger than ONE).
    uint256 internal constant MIN_POW_BASE_FREE_EXPONENT = 0.7e18;

    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        // Fixed Point addition is the same as regular checked addition

        uint256 c = a + b;
        if (!(c >= a)) {
            _require(false, Errors.ADD_OVERFLOW);
        }
        return c;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        // Fixed Point addition is the same as regular checked addition

        if (!(b <= a)) {
            _require(false, Errors.SUB_OVERFLOW);
        }
        uint256 c = a - b;
        return c;
    }

    function mulDown(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 product = a * b;
        if (!(a == 0 || product / a == b)) {
            _require(false, Errors.MUL_OVERFLOW);
        }

        return product / ONE;
    }

    /// @dev "U" denotes version of the math function that does not check for overflows in order to save gas
    function mulDownU(uint256 a, uint256 b) internal pure returns (uint256) {
        return (a * b) / ONE;
    }

    function mulUp(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 product = a * b;
        if (!(a == 0 || product / a == b)) {
            _require(false, Errors.MUL_OVERFLOW);
        }

        if (product == 0) {
            return 0;
        }

        // The traditional divUp formula is:
        // divUp(x, y) := (x + y - 1) / y
        // To avoid intermediate overflow in the addition, we distribute the division and get:
        // divUp(x, y) := (x - 1) / y + 1
        // Note that this requires x != 0, which we already tested for.

        return ((product - 1) / ONE) + 1;
    }

    function mulUpU(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 product = a * b;

        if (product == 0) {
            return 0;
        }
        // The traditional divUp formula is:
        // divUp(x, y) := (x + y - 1) / y
        // To avoid intermediate overflow in the addition, we distribute the division and get:
        // divUp(x, y) := (x - 1) / y + 1
        // Note that this requires x != 0, which we already tested for.

        return ((product - 1) / ONE) + 1;
    }

    function divDown(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            _require(false, Errors.ZERO_DIVISION);
        }

        if (a == 0) {
            return 0;
        }

        uint256 aInflated = a * ONE;
        if (!(aInflated / a == ONE)) {
            _require(false, Errors.DIV_INTERNAL); // mul overflow
        }

        return aInflated / b;
    }

    function divDownU(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            _require(false, Errors.ZERO_DIVISION);
        }

        return (a * ONE) / b;
    }

    function divUp(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            _require(false, Errors.ZERO_DIVISION);
        }

        if (a == 0) {
            return 0;
        }

        uint256 aInflated = a * ONE;
        if (!(aInflated / a == ONE)) {
            _require(aInflated / a == ONE, Errors.DIV_INTERNAL); // mul overflow
        }

        // The traditional divUp formula is:
        // divUp(x, y) := (x + y - 1) / y
        // To avoid intermediate overflow in the addition, we distribute the division and get:
        // divUp(x, y) := (x - 1) / y + 1
        // Note that this requires x != 0, which we already tested for.

        return ((aInflated - 1) / b) + 1;
    }

    function divUpU(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            _require(false, Errors.ZERO_DIVISION);
        }

        if (a == 0) {
            return 0;
        }
        return ((a * ONE - 1) / b) + 1;
    }

    /**
     * @dev Like mulDown(), but it also works in some situations where mulDown(a, b) would overflow because a * b is too
     * large. We achieve this by splitting up `a` into its integer and its fractional part. `a` should be the bigger of
     * the two numbers to achieve the best overflow guarantees.
     * This won't overflow if both of
     *   - a * b ≤ 1.15e95 (raw values, i.e., a * b ≤ 1.15e59 with respect to the fixed-point values that they describe)
     *   - b ≤ 1.15e59 (raw values, i.e., a ≤ 1.15e41 with respect to the values that a describes)
     * hold. That's better than mulDown(), where we would need a * b ≤ 1.15e77 approximately.
     */
    function mulDownLargeSmall(uint256 a, uint256 b) internal pure returns (uint256) {
        return add(Math.mul(a / ONE, b), mulDown(a % ONE, b));
    }

    function mulDownLargeSmallU(uint256 a, uint256 b) internal pure returns (uint256) {
        return (a / ONE) * b + mulDownU(a % ONE, b);
    }

    /**
     * @dev Like divDown(), but it also works when `a` would overflow in `divDown`. This is safe if both of
     * - a ≤ 1.15e68 (raw, i.e., a ≤ 1.15e50 with respect to the value that is represented)
     * - b ≥ 1e9 (raw, i.e., b ≥ 1e-9 with respect to the value represented)
     * hold. For `divDown` it's 1.15e59 and 1.15e41, respectively.
     * Introduces some rounding error that is relevant iff b is small.
     */
    function divDownLarge(uint256 a, uint256 b) internal pure returns (uint256) {
        return divDownLarge(a, b, MIDDECIMAL, MIDDECIMAL);
    }

    function divDownLargeU(uint256 a, uint256 b) internal pure returns (uint256) {
        return divDownLargeU(a, b, MIDDECIMAL, MIDDECIMAL);
    }

    /**
     * @dev Like divDown(), but it also works when `a` would overflow in `divDown`. d and e must be chosen such that
     * d * e = 1e18 (raw numbers, or d * e = 1e-18 with respect to the numbers they represent in fixed point). Note that
     * this requires d, e ≤ 1e18 (raw, or d, e ≤ 1 with respect to the numbers represented).
     * This operation is safe if both of
     * - a * d ≤ 1.15e77 (raw, i.e., a * d ≤ 1.15e41 with respect to the value that is represented)
     * - b ≥ e (with respect to raw or represented numbers)
     * hold.
     * Introduces some rounding error that is relevant iff b is small and is proportional to e.
     */
    function divDownLarge(
        uint256 a,
        uint256 b,
        uint256 d,
        uint256 e
    ) internal pure returns (uint256) {
        return Math.divDown(Math.mul(a, d), Math.divUp(b, e));
    }

    /// @dev e is assumed to be non-zero, and so division by zero is not checked for it
    function divDownLargeU(
        uint256 a,
        uint256 b,
        uint256 d,
        uint256 e
    ) internal pure returns (uint256) {
        // (a * d) / (b / e)

        if (b == 0) {
            // In this case only, the denominator of the outer division is zero, and we revert
            _require(false, Errors.ZERO_DIVISION);
        }

        uint256 denom = 1 + (b - 1) / e;

        return (a * d) / denom;
    }

    /**
     * @dev Returns x^y, assuming both are fixed point numbers, rounding down. The result is guaranteed to not be above
     * the true value (that is, the error function expected - actual is always positive).
     */
    function powDown(uint256 x, uint256 y) internal pure returns (uint256) {
        uint256 raw = LogExpMath.pow(x, y);
        uint256 maxError = add(mulUp(raw, MAX_POW_RELATIVE_ERROR), 1);

        if (raw < maxError) {
            return 0;
        }
        return sub(raw, maxError);
    }

    /**
     * @dev Returns x^y, assuming both are fixed point numbers, rounding up. The result is guaranteed to not be below
     * the true value (that is, the error function expected - actual is always negative).
     */
    function powUp(uint256 x, uint256 y) internal pure returns (uint256) {
        uint256 raw = LogExpMath.pow(x, y);
        uint256 maxError = add(mulUp(raw, MAX_POW_RELATIVE_ERROR), 1);

        return add(raw, maxError);
    }

    /**
     * @dev Returns the complement of a value (1 - x), capped to 0 if x is larger than 1.
     *
     * Useful when computing the complement for values with some level of relative error, as it strips this error and
     * prevents intermediate negative values.
     */
    function complement(uint256 x) internal pure returns (uint256) {
        return (x < ONE) ? (ONE - x) : 0;
    }
}

File 3 of 12 : GyroErrors.sol
// SPDX-License-Identifier: LicenseRef-Gyro-1.0
// for information on licensing please see the README in the GitHub repository <https://github.com/gyrostable/concentrated-lps>.

pragma solidity ^0.7.0;

/**
 * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are
 * supported.
 * Uses the default 'BAL' prefix for the error code
 */
function _grequire(bool condition, uint256 errorCode) pure {
    if (!condition) _grevert(errorCode);
}

/**
 * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are
 * supported.
 */
function _grequire(
    bool condition,
    uint256 errorCode,
    bytes3 prefix
) pure {
    if (!condition) _grevert(errorCode, prefix);
}

/**
 * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.
 * Uses the default 'BAL' prefix for the error code
 */
function _grevert(uint256 errorCode) pure {
    _grevert(errorCode, 0x475952); // This is the raw byte representation of "GYR"
}

/**
 * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.
 */
function _grevert(uint256 errorCode, bytes3 prefix) pure {
    uint256 prefixUint = uint256(uint24(prefix));
    // We're going to dynamically create a revert string based on the error code, with the following format:
    // 'BAL#{errorCode}'
    // where the code is left-padded with zeroes to three digits (so they range from 000 to 999).
    //
    // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a
    // number (8 to 16 bits) than the individual string characters.
    //
    // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a
    // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a
    // safe place to rely on it without worrying about how its usage might affect e.g. memory contents.
    assembly {
        // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999
        // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for
        // the '0' character.

        let units := add(mod(errorCode, 10), 0x30)

        errorCode := div(errorCode, 10)
        let tenths := add(mod(errorCode, 10), 0x30)

        errorCode := div(errorCode, 10)
        let hundreds := add(mod(errorCode, 10), 0x30)

        // With the individual characters, we can now construct the full string.
        // We first append the '#' character (0x23) to the prefix. In the case of 'BAL', it results in 0x42414c23 ('BAL#')
        // Then, we shift this by 24 (to provide space for the 3 bytes of the error code), and add the
        // characters to it, each shifted by a multiple of 8.
        // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits
        // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte
        // array).
        let formattedPrefix := shl(24, add(0x23, shl(8, prefixUint)))

        let revertReason := shl(200, add(formattedPrefix, add(add(units, shl(8, tenths)), shl(16, hundreds))))

        // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded
        // message will have the following layout:
        // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ]

        // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We
        // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten.
        mstore(0x0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
        // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away).
        mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020)
        // The string length is fixed: 7 characters.
        mstore(0x24, 7)
        // Finally, the string itself is stored.
        mstore(0x44, revertReason)

        // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of
        // the encoded message is therefore 4 + 32 + 32 + 32 = 100.
        revert(0, 100)
    }
}

library GyroErrors {
    uint256 internal constant ZERO_ADDRESS = 105;
}

File 4 of 12 : SignedFixedPoint.sol
// SPDX-License-Identifier: LicenseRef-Gyro-1.0
// for information on licensing please see the README in the GitHub repository <https://github.com/gyrostable/concentrated-lps>.

pragma solidity 0.7.6;

// import "@balancer-labs/v2-solidity-utils/contracts/math/FixedPoint.sol";
import "./GyroFixedPoint.sol";
import "@balancer-labs/v2-solidity-utils/contracts/helpers/BalancerErrors.sol";

/* solhint-disable private-vars-leading-underscore */

/// @dev Signed fixed point operations based on Balancer's FixedPoint library.
/// Note: The `{mul,div}{UpMag,DownMag}()` functions do *not* round up or down, respectively,
/// in a signed fashion (like ceil and floor operations), but *in absolute value* (or *magnitude*), i.e.,
/// towards 0. This is useful in some applications.
library SignedFixedPoint {
    int256 internal constant ONE = 1e18; // 18 decimal places
    // setting extra precision at 38 decimals, which is the most we can get w/o overflowing on normal multiplication
    // this allows 20 extra digits to absorb error when multiplying by large numbers
    int256 internal constant ONE_XP = 1e38; // 38 decimal places

    function add(int256 a, int256 b) internal pure returns (int256) {
        // Fixed Point addition is the same as regular checked addition

        int256 c = a + b;
        if (!(b >= 0 ? c >= a : c < a)) _require(false, Errors.ADD_OVERFLOW);
        return c;
    }

    function addMag(int256 a, int256 b) internal pure returns (int256 c) {
        // add b in the same signed direction as a, i.e. increase the magnitude of a by b
        c = a > 0 ? add(a, b) : sub(a, b);
    }

    function sub(int256 a, int256 b) internal pure returns (int256) {
        // Fixed Point subtraction is the same as regular checked subtraction

        int256 c = a - b;
        if (!(b <= 0 ? c >= a : c < a)) _require(false, Errors.SUB_OVERFLOW);
        return c;
    }

    /// @dev This rounds towards 0, i.e., down *in absolute value*!
    function mulDownMag(int256 a, int256 b) internal pure returns (int256) {
        int256 product = a * b;
        if (!(a == 0 || product / a == b)) _require(false, Errors.MUL_OVERFLOW);

        return product / ONE;
    }

    /// @dev this implements mulDownMag w/o checking for over/under-flows, which saves significantly on gas if these aren't needed
    function mulDownMagU(int256 a, int256 b) internal pure returns (int256) {
        return (a * b) / ONE;
    }

    /// @dev This rounds away from 0, i.e., up *in absolute value*!
    function mulUpMag(int256 a, int256 b) internal pure returns (int256) {
        int256 product = a * b;
        if (!(a == 0 || product / a == b)) _require(false, Errors.MUL_OVERFLOW);

        // If product > 0, the result should be ceil(p/ONE) = floor((p-1)/ONE) + 1, where floor() is implicit. If
        // product < 0, the result should be floor(p/ONE) = ceil((p+1)/ONE) - 1, where ceil() is implicit.
        // Addition for signed numbers: Case selection so we round away from 0, not always up.
        if (product > 0) return ((product - 1) / ONE) + 1;
        else if (product < 0) return ((product + 1) / ONE) - 1;
        // product == 0
        return 0;
    }

    /// @dev this implements mulUpMag w/o checking for over/under-flows, which saves significantly on gas if these aren't needed
    function mulUpMagU(int256 a, int256 b) internal pure returns (int256) {
        int256 product = a * b;

        // If product > 0, the result should be ceil(p/ONE) = floor((p-1)/ONE) + 1, where floor() is implicit. If
        // product < 0, the result should be floor(p/ONE) = ceil((p+1)/ONE) - 1, where ceil() is implicit.
        // Addition for signed numbers: Case selection so we round away from 0, not always up.
        if (product > 0) return ((product - 1) / ONE) + 1;
        else if (product < 0) return ((product + 1) / ONE) - 1;
        // product == 0
        return 0;
    }

    /// @dev Rounds towards 0, i.e., down in absolute value.
    function divDownMag(int256 a, int256 b) internal pure returns (int256) {
        if (b == 0) _require(false, Errors.ZERO_DIVISION);

        if (a == 0) {
            return 0;
        }

        int256 aInflated = a * ONE;
        if (aInflated / a != ONE) _require(false, Errors.DIV_INTERNAL);

        return aInflated / b;
    }

    /// @dev this implements divDownMag w/o checking for over/under-flows, which saves significantly on gas if these aren't needed
    function divDownMagU(int256 a, int256 b) internal pure returns (int256) {
        if (b == 0) _require(false, Errors.ZERO_DIVISION);
        return (a * ONE) / b;
    }

    /// @dev Rounds away from 0, i.e., up in absolute value.
    function divUpMag(int256 a, int256 b) internal pure returns (int256) {
        if (b == 0) _require(false, Errors.ZERO_DIVISION);

        if (a == 0) {
            return 0;
        }

        if (b < 0) {
            // Required so the below is correct.
            b = -b;
            a = -a;
        }

        int256 aInflated = a * ONE;
        if (aInflated / a != ONE) _require(false, Errors.DIV_INTERNAL);

        if (aInflated > 0) return ((aInflated - 1) / b) + 1;
        return ((aInflated + 1) / b) - 1;
    }

    /// @dev this implements divUpMag w/o checking for over/under-flows, which saves significantly on gas if these aren't needed
    function divUpMagU(int256 a, int256 b) internal pure returns (int256) {
        if (b == 0) _require(false, Errors.ZERO_DIVISION);

        if (a == 0) {
            return 0;
        }

        // SOMEDAY check if we can shave off some gas by logically refactoring this vs the below case distinction into one (on a * b or so).
        if (b < 0) {
            // Ensure b > 0 so the below is correct.
            b = -b;
            a = -a;
        }

        if (a > 0) return ((a * ONE - 1) / b) + 1;
        return ((a * ONE + 1) / b) - 1;
    }

    /// @dev multiplies two extra precision numbers (with 38 decimals)
    /// rounds down in magnitude but this shouldn't matter
    /// multiplication can overflow if a,b are > 2 in magnitude
    function mulXp(int256 a, int256 b) internal pure returns (int256) {
        int256 product = a * b;
        if (!(a == 0 || product / a == b)) _require(false, Errors.MUL_OVERFLOW);

        return product / ONE_XP;
    }

    /// @dev multiplies two extra precision numbers (with 38 decimals)
    /// rounds down in magnitude but this shouldn't matter
    /// multiplication can overflow if a,b are > 2 in magnitude
    /// this implements mulXp w/o checking for over/under-flows, which saves significantly on gas if these aren't needed
    function mulXpU(int256 a, int256 b) internal pure returns (int256) {
        return (a * b) / ONE_XP;
    }

    /// @dev divides two extra precision numbers (with 38 decimals)
    /// rounds down in magnitude but this shouldn't matter
    /// can overflow if a > 2 or b << 1 in magnitude
    function divXp(int256 a, int256 b) internal pure returns (int256) {
        if (b == 0) _require(false, Errors.ZERO_DIVISION);

        if (a == 0) {
            return 0;
        }

        int256 aInflated = a * ONE_XP;
        if (aInflated / a != ONE_XP) _require(false, Errors.DIV_INTERNAL);

        return aInflated / b;
    }

    /// @dev divides two extra precision numbers (with 38 decimals)
    /// rounds down in magnitude but this shouldn't matter
    /// can overflow if a > 2 or b << 1 in magnitude
    /// this implements divXp w/o checking for over/under-flows, which saves significantly on gas if these aren't needed
    function divXpU(int256 a, int256 b) internal pure returns (int256) {
        if (b == 0) _require(false, Errors.ZERO_DIVISION);

        return (a * ONE_XP) / b;
    }

    /// @dev multiplies normal precision a with extra precision b (with 38 decimals)
    /// Rounds down in signed direction
    /// returns normal precision of the product
    function mulDownXpToNp(int256 a, int256 b) internal pure returns (int256) {
        int256 b1 = b / 1e19;
        int256 prod1 = a * b1;
        if (!(a == 0 || prod1 / a == b1)) _require(false, Errors.MUL_OVERFLOW);
        int256 b2 = b % 1e19;
        int256 prod2 = a * b2;
        if (!(a == 0 || prod2 / a == b2)) _require(false, Errors.MUL_OVERFLOW);
        return prod1 >= 0 && prod2 >= 0 ? (prod1 + prod2 / 1e19) / 1e19 : (prod1 + prod2 / 1e19 + 1) / 1e19 - 1;
    }

    /// @dev multiplies normal precision a with extra precision b (with 38 decimals)
    /// Rounds down in signed direction
    /// returns normal precision of the product
    /// this implements mulDownXpToNp w/o checking for over/under-flows, which saves significantly on gas if these aren't needed
    function mulDownXpToNpU(int256 a, int256 b) internal pure returns (int256) {
        int256 b1 = b / 1e19;
        int256 b2 = b % 1e19;
        // SOMEDAY check if we eliminate these vars and save some gas (by only checking the sign of prod1, say)
        int256 prod1 = a * b1;
        int256 prod2 = a * b2;
        return prod1 >= 0 && prod2 >= 0 ? (prod1 + prod2 / 1e19) / 1e19 : (prod1 + prod2 / 1e19 + 1) / 1e19 - 1;
    }

    /// @dev multiplies normal precision a with extra precision b (with 38 decimals)
    /// Rounds up in signed direction
    /// returns normal precision of the product
    function mulUpXpToNp(int256 a, int256 b) internal pure returns (int256) {
        int256 b1 = b / 1e19;
        int256 prod1 = a * b1;
        if (!(a == 0 || prod1 / a == b1)) _require(false, Errors.MUL_OVERFLOW);
        int256 b2 = b % 1e19;
        int256 prod2 = a * b2;
        if (!(a == 0 || prod2 / a == b2)) _require(false, Errors.MUL_OVERFLOW);
        return prod1 <= 0 && prod2 <= 0 ? (prod1 + prod2 / 1e19) / 1e19 : (prod1 + prod2 / 1e19 - 1) / 1e19 + 1;
    }

    /// @dev multiplies normal precision a with extra precision b (with 38 decimals)
    /// Rounds up in signed direction
    /// returns normal precision of the product
    /// this implements mulUpXpToNp w/o checking for over/under-flows, which saves significantly on gas if these aren't needed
    function mulUpXpToNpU(int256 a, int256 b) internal pure returns (int256) {
        int256 b1 = b / 1e19;
        int256 b2 = b % 1e19;
        // SOMEDAY check if we eliminate these vars and save some gas (by only checking the sign of prod1, say)
        int256 prod1 = a * b1;
        int256 prod2 = a * b2;
        return prod1 <= 0 && prod2 <= 0 ? (prod1 + prod2 / 1e19) / 1e19 : (prod1 + prod2 / 1e19 - 1) / 1e19 + 1;
    }

    // not implementing the pow functions right now b/c it's annoying and slightly ill-defined, and we don't use them.

    /**
     * @dev Returns the complement of a value (1 - x), capped to 0 if x is larger than 1.
     *
     * Useful when computing the complement for values with some level of relative error, as it strips this error and
     * prevents intermediate negative values.
     */
    function complement(int256 x) internal pure returns (int256) {
        if (x >= ONE || x <= 0) return 0;
        return ONE - x;
    }
}

File 5 of 12 : GyroPoolMath.sol
// SPDX-License-Identifier: LicenseRef-Gyro-1.0
// for information on licensing please see the README in the GitHub repository <https://github.com/gyrostable/concentrated-lps>.

pragma solidity 0.7.6;

// import "@balancer-labs/v2-solidity-utils/contracts/math/FixedPoint.sol";
import "./GyroFixedPoint.sol";
import "@balancer-labs/v2-solidity-utils/contracts/math/Math.sol";
import "@balancer-labs/v2-solidity-utils/contracts/helpers/InputHelpers.sol";

library GyroPoolMath {
    using GyroFixedPoint for uint256;

    uint256 private constant SQRT_1E_NEG_1 = 316227766016837933;
    uint256 private constant SQRT_1E_NEG_3 = 31622776601683793;
    uint256 private constant SQRT_1E_NEG_5 = 3162277660168379;
    uint256 private constant SQRT_1E_NEG_7 = 316227766016837;
    uint256 private constant SQRT_1E_NEG_9 = 31622776601683;
    uint256 private constant SQRT_1E_NEG_11 = 3162277660168;
    uint256 private constant SQRT_1E_NEG_13 = 316227766016;
    uint256 private constant SQRT_1E_NEG_15 = 31622776601;
    uint256 private constant SQRT_1E_NEG_17 = 3162277660;

    // Note: this function is identical to that in WeightedMath.sol audited by Balancer
    function _calcAllTokensInGivenExactBptOut(
        uint256[] memory balances,
        uint256 bptOut,
        uint256 totalBPT
    ) internal pure returns (uint256[] memory amountsIn) {
        /************************************************************************************
        // tokensInForExactBptOut                                                          //
        //                              /   bptOut   \                                     //
        // amountsIn[i] = balances[i] * | ------------ |                                   //
        //                              \  totalBPT  /                                     //
        ************************************************************************************/
        // We adjust the order of operations to minimize error amplification, assuming that
        // balances[i], totalBPT > 1 (which is usually the case).
        // Tokens in, so we round up overall.

        amountsIn = new uint256[](balances.length);
        for (uint256 i = 0; i < balances.length; i++) {
            amountsIn[i] = balances[i].mulUp(bptOut).divUp(totalBPT);
        }

        return amountsIn;
    }

    // Note: this function is identical to that in WeightedMath.sol audited by Balancer
    function _calcTokensOutGivenExactBptIn(
        uint256[] memory balances,
        uint256 bptIn,
        uint256 totalBPT
    ) internal pure returns (uint256[] memory amountsOut) {
        /**********************************************************************************************
        // exactBPTInForTokensOut                                                                    //
        // (per token)                                                                               //
        //                                /        bptIn         \                                   //
        // amountsOut[i] = balances[i] * | ---------------------  |                                  //
        //                                \       totalBPT       /                                   //
        **********************************************************************************************/
        // We adjust the order of operations to minimize error amplification, assuming that
        // balances[i], totalBPT > 1 (which is usually the case).
        // Since we're computing an amount out, we round down overall. This means rounding down on both the
        // multiplication and division.

        amountsOut = new uint256[](balances.length);
        for (uint256 i = 0; i < balances.length; i++) {
            amountsOut[i] = balances[i].mulDown(bptIn).divDown(totalBPT);
        }

        return amountsOut;
    }

    /** @dev Calculates protocol fees due to Gyro and Balancer
     *   Note: we do this differently than normal Balancer pools by paying fees in BPT tokens
     *   b/c this is much more gas efficient than doing many transfers of underlying assets
     *   This function gets protocol fee parameters from GyroConfig
     */
    function _calcProtocolFees(
        uint256 previousInvariant,
        uint256 currentInvariant,
        uint256 currentBptSupply,
        uint256 protocolSwapFeePerc,
        uint256 protocolFeeGyroPortion
    ) internal pure returns (uint256, uint256) {
        /*********************************************************************************
        /*  Protocol fee collection should decrease the invariant L by
        *        Delta L = protocolSwapFeePerc * (currentInvariant - previousInvariant)
        *   To take these fees in BPT LP shares, the protocol mints Delta S new LP shares where
        *        Delta S = S * Delta L / ( currentInvariant - Delta L )
        *   where S = current BPT supply
        *   The protocol then splits the fees (in BPT) considering protocolFeeGyroPortion
        *   See also the write-up, Proposition 7.
        *********************************************************************************/

        if (currentInvariant <= previousInvariant) {
            // This shouldn't happen outside of rounding errors, but have this safeguard nonetheless to prevent the Pool
            // from entering a locked state in which joins and exits revert while computing accumulated swap fees.
            // NB: This condition is also used by the pools to indicate that _lastInvariant is invalid and should be ignored.
            return (0, 0);
        }

        // Calculate due protocol fees in BPT terms
        // We round down to prevent issues in the Pool's accounting, even if it means paying slightly less in protocol
        // fees to the Vault.
        // For the numerator, we need to round down delta L. Also for the denominator b/c subtracted
        // Ordering multiplications for best fixed point precision considering that S and currentInvariant-previousInvariant could be large
        uint256 numerator = (currentBptSupply.mulDown(currentInvariant.sub(previousInvariant))).mulDown(protocolSwapFeePerc);
        uint256 diffInvariant = protocolSwapFeePerc.mulDown(currentInvariant.sub(previousInvariant));
        uint256 denominator = currentInvariant.sub(diffInvariant);
        uint256 deltaS = numerator.divDown(denominator);

        // Split fees between Gyro and Balancer
        uint256 gyroFees = protocolFeeGyroPortion.mulDown(deltaS);
        uint256 balancerFees = deltaS.sub(gyroFees);

        return (gyroFees, balancerFees);
    }

    /** @dev Implements square root algorithm using Newton's method and a first-guess optimisation **/
    function _sqrt(uint256 input, uint256 tolerance) internal pure returns (uint256) {
        if (input == 0) {
            return 0;
        }

        uint256 guess = _makeInitialGuess(input);

        // 7 iterations
        guess = (guess + ((input * GyroFixedPoint.ONE) / guess)) / 2;
        guess = (guess + ((input * GyroFixedPoint.ONE) / guess)) / 2;
        guess = (guess + ((input * GyroFixedPoint.ONE) / guess)) / 2;
        guess = (guess + ((input * GyroFixedPoint.ONE) / guess)) / 2;
        guess = (guess + ((input * GyroFixedPoint.ONE) / guess)) / 2;
        guess = (guess + ((input * GyroFixedPoint.ONE) / guess)) / 2;
        guess = (guess + ((input * GyroFixedPoint.ONE) / guess)) / 2;

        // Check in some epsilon range
        // Check square is more or less correct
        uint256 guessSquared = guess.mulDown(guess);
        require(guessSquared <= input.add(guess.mulUp(tolerance)) && guessSquared >= input.sub(guess.mulUp(tolerance)), "_sqrt FAILED");

        return guess;
    }

    // function _makeInitialGuess10(uint256 input) internal pure returns (uint256) {
    //     uint256 orderUpperBound = 72;
    //     uint256 orderLowerBound = 0;
    //     uint256 orderMiddle;

    //     orderMiddle = (orderUpperBound + orderLowerBound) / 2;

    //     while (orderUpperBound - orderLowerBound != 1) {
    //         if (10**orderMiddle > input) {
    //             orderUpperBound = orderMiddle;
    //         } else {
    //             orderLowerBound = orderMiddle;
    //         }
    //     }

    //     return 10**(orderUpperBound / 2);
    // }

    function _makeInitialGuess(uint256 input) internal pure returns (uint256) {
        if (input >= GyroFixedPoint.ONE) {
            return (1 << (_intLog2Halved(input / GyroFixedPoint.ONE))) * GyroFixedPoint.ONE;
        } else {
            if (input <= 10) {
                return SQRT_1E_NEG_17;
            }
            if (input <= 1e2) {
                return 1e10;
            }
            if (input <= 1e3) {
                return SQRT_1E_NEG_15;
            }
            if (input <= 1e4) {
                return 1e11;
            }
            if (input <= 1e5) {
                return SQRT_1E_NEG_13;
            }
            if (input <= 1e6) {
                return 1e12;
            }
            if (input <= 1e7) {
                return SQRT_1E_NEG_11;
            }
            if (input <= 1e8) {
                return 1e13;
            }
            if (input <= 1e9) {
                return SQRT_1E_NEG_9;
            }
            if (input <= 1e10) {
                return 1e14;
            }
            if (input <= 1e11) {
                return SQRT_1E_NEG_7;
            }
            if (input <= 1e12) {
                return 1e15;
            }
            if (input <= 1e13) {
                return SQRT_1E_NEG_5;
            }
            if (input <= 1e14) {
                return 1e16;
            }
            if (input <= 1e15) {
                return SQRT_1E_NEG_3;
            }
            if (input <= 1e16) {
                return 1e17;
            }
            if (input <= 1e17) {
                return SQRT_1E_NEG_1;
            }
            return input;
        }
    }

    function _intLog2Halved(uint256 x) public pure returns (uint256 n) {
        if (x >= 1 << 128) {
            x >>= 128;
            n += 64;
        }
        if (x >= 1 << 64) {
            x >>= 64;
            n += 32;
        }
        if (x >= 1 << 32) {
            x >>= 32;
            n += 16;
        }
        if (x >= 1 << 16) {
            x >>= 16;
            n += 8;
        }
        if (x >= 1 << 8) {
            x >>= 8;
            n += 4;
        }
        if (x >= 1 << 4) {
            x >>= 4;
            n += 2;
        }
        if (x >= 1 << 2) {
            x >>= 2;
            n += 1;
        }
    }

    /** @dev If liquidity update is proportional so that price stays the same ("balanced liquidity update"), then this
     *  returns the invariant after that change. This is more efficient than calling `calculateInvariant()` on the updated balances.
     *  `isIncreaseLiq` denotes the sign of the update. See the writeup, Corollary 3 in Section 3.1.3. */
    function liquidityInvariantUpdate(
        uint256 uinvariant,
        uint256 changeBptSupply,
        uint256 currentBptSupply,
        bool isIncreaseLiq
    ) internal pure returns (uint256 unewInvariant) {
        //  change in invariant
        if (isIncreaseLiq) {
            // round new invariant up so that protocol fees not triggered
            uint256 dL = uinvariant.mulUp(changeBptSupply).divUp(currentBptSupply);
            unewInvariant = uinvariant.add(dL);
        } else {
            // round new invariant up (and so round dL down) so that protocol fees not triggered
            uint256 dL = uinvariant.mulDown(changeBptSupply).divDown(currentBptSupply);
            unewInvariant = uinvariant.sub(dL);
        }
    }

    /** @dev If `deltaBalances` are such that, when changing `balances` by it, the price stays the same ("balanced
     * liquidity update"), then this returns the invariant after that change. This is more efficient than calling
     * `calculateInvariant()` on the updated balances. `isIncreaseLiq` denotes the sign of the update.
     * See the writeup, Corollary 3 in Section 3.1.3.
     *
     * DEPRECATED and will go out of use and be removed once pending changes to the ECLP are merged. Use the other liquidityInvariantUpdate() function instead!
     */
    function liquidityInvariantUpdate(
        uint256[] memory balances,
        uint256 uinvariant,
        uint256[] memory deltaBalances,
        bool isIncreaseLiq
    ) internal pure returns (uint256 unewInvariant) {
        uint256 largestBalanceIndex;
        uint256 largestBalance;
        for (uint256 i = 0; i < balances.length; i++) {
            if (balances[i] > largestBalance) {
                largestBalance = balances[i];
                largestBalanceIndex = i;
            }
        }

        uint256 deltaInvariant = uinvariant.mulDown(deltaBalances[largestBalanceIndex]).divDown(balances[largestBalanceIndex]);
        unewInvariant = isIncreaseLiq ? uinvariant.add(deltaInvariant) : uinvariant.sub(deltaInvariant);
    }
}

File 6 of 12 : GyroECLPPoolErrors.sol
// SPDX-License-Identifier: LicenseRef-Gyro-1.0
// for information on licensing please see the README in the GitHub repository <https://github.com/gyrostable/concentrated-lps>.

pragma solidity 0.7.6;

// solhint-disable

library GyroECLPPoolErrors {
    // Input
    uint256 internal constant ADDRESS_IS_ZERO_ADDRESS = 120;
    uint256 internal constant TOKEN_IN_IS_NOT_TOKEN_0 = 121;

    // Math
    uint256 internal constant PRICE_BOUNDS_WRONG = 354;
    uint256 internal constant ROTATION_VECTOR_WRONG = 355;
    uint256 internal constant ROTATION_VECTOR_NOT_NORMALIZED = 356;
    uint256 internal constant ASSET_BOUNDS_EXCEEDED = 357;
    uint256 internal constant DERIVED_TAU_NOT_NORMALIZED = 358;
    uint256 internal constant DERIVED_ZETA_WRONG = 359;
    uint256 internal constant STRETCHING_FACTOR_WRONG = 360;
    uint256 internal constant DERIVED_UVWZ_WRONG = 361;
    uint256 internal constant INVARIANT_DENOMINATOR_WRONG = 362;
    uint256 internal constant MAX_ASSETS_EXCEEDED = 363;
    uint256 internal constant MAX_INVARIANT_EXCEEDED = 363;
    uint256 internal constant DERIVED_DSQ_WRONG = 364;
}

File 7 of 12 : Math.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "../helpers/BalancerErrors.sol";

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow checks.
 * Adapted from OpenZeppelin's SafeMath library
 */
library Math {
    /**
     * @dev Returns the addition of two unsigned integers of 256 bits, reverting on overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        _require(c >= a, Errors.ADD_OVERFLOW);
        return c;
    }

    /**
     * @dev Returns the addition of two signed integers, reverting on overflow.
     */
    function add(int256 a, int256 b) internal pure returns (int256) {
        int256 c = a + b;
        _require((b >= 0 && c >= a) || (b < 0 && c < a), Errors.ADD_OVERFLOW);
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers of 256 bits, reverting on overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        _require(b <= a, Errors.SUB_OVERFLOW);
        uint256 c = a - b;
        return c;
    }

    /**
     * @dev Returns the subtraction of two signed integers, reverting on overflow.
     */
    function sub(int256 a, int256 b) internal pure returns (int256) {
        int256 c = a - b;
        _require((b >= 0 && c <= a) || (b < 0 && c > a), Errors.SUB_OVERFLOW);
        return c;
    }

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

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

    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a * b;
        _require(a == 0 || c / a == b, Errors.MUL_OVERFLOW);
        return c;
    }

    function div(
        uint256 a,
        uint256 b,
        bool roundUp
    ) internal pure returns (uint256) {
        return roundUp ? divUp(a, b) : divDown(a, b);
    }

    function divDown(uint256 a, uint256 b) internal pure returns (uint256) {
        _require(b != 0, Errors.ZERO_DIVISION);
        return a / b;
    }

    function divUp(uint256 a, uint256 b) internal pure returns (uint256) {
        _require(b != 0, Errors.ZERO_DIVISION);

        if (a == 0) {
            return 0;
        } else {
            return 1 + (a - 1) / b;
        }
    }
}

File 8 of 12 : InputHelpers.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;

import "../openzeppelin/IERC20.sol";

import "./BalancerErrors.sol";

library InputHelpers {
    function ensureInputLengthMatch(uint256 a, uint256 b) internal pure {
        _require(a == b, Errors.INPUT_LENGTH_MISMATCH);
    }

    function ensureInputLengthMatch(
        uint256 a,
        uint256 b,
        uint256 c
    ) internal pure {
        _require(a == b && b == c, Errors.INPUT_LENGTH_MISMATCH);
    }

    function ensureArrayIsSorted(IERC20[] memory array) internal pure {
        address[] memory addressArray;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            addressArray := array
        }
        ensureArrayIsSorted(addressArray);
    }

    function ensureArrayIsSorted(address[] memory array) internal pure {
        if (array.length < 2) {
            return;
        }

        address previous = array[0];
        for (uint256 i = 1; i < array.length; ++i) {
            address current = array[i];
            _require(previous < current, Errors.UNSORTED_ARRAY);
            previous = current;
        }
    }
}

File 9 of 12 : SafeCast.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;


/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= -2**127 && value < 2**127, "SafeCast: value doesn\'t fit in 128 bits");
        return int128(value);
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= -2**63 && value < 2**63, "SafeCast: value doesn\'t fit in 64 bits");
        return int64(value);
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= -2**31 && value < 2**31, "SafeCast: value doesn\'t fit in 32 bits");
        return int32(value);
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= -2**15 && value < 2**15, "SafeCast: value doesn\'t fit in 16 bits");
        return int16(value);
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= -2**7 && value < 2**7, "SafeCast: value doesn\'t fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        require(value < 2**255, "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 10 of 12 : LogExpMath.sol
// SPDX-License-Identifier: MIT
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
// Software.

// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

pragma solidity ^0.7.0;

import "../helpers/BalancerErrors.sol";

/* solhint-disable */

/**
 * @dev Exponentiation and logarithm functions for 18 decimal fixed point numbers (both base and exponent/argument).
 *
 * Exponentiation and logarithm with arbitrary bases (x^y and log_x(y)) are implemented by conversion to natural
 * exponentiation and logarithm (where the base is Euler's number).
 *
 * @author Fernando Martinelli - @fernandomartinelli
 * @author Sergio Yuhjtman - @sergioyuhjtman
 * @author Daniel Fernandez - @dmf7z
 */
library LogExpMath {
    // All fixed point multiplications and divisions are inlined. This means we need to divide by ONE when multiplying
    // two numbers, and multiply by ONE when dividing them.

    // All arguments and return values are 18 decimal fixed point numbers.
    int256 constant ONE_18 = 1e18;

    // Internally, intermediate values are computed with higher precision as 20 decimal fixed point numbers, and in the
    // case of ln36, 36 decimals.
    int256 constant ONE_20 = 1e20;
    int256 constant ONE_36 = 1e36;

    // The domain of natural exponentiation is bound by the word size and number of decimals used.
    //
    // Because internally the result will be stored using 20 decimals, the largest possible result is
    // (2^255 - 1) / 10^20, which makes the largest exponent ln((2^255 - 1) / 10^20) = 130.700829182905140221.
    // The smallest possible result is 10^(-18), which makes largest negative argument
    // ln(10^(-18)) = -41.446531673892822312.
    // We use 130.0 and -41.0 to have some safety margin.
    int256 constant MAX_NATURAL_EXPONENT = 130e18;
    int256 constant MIN_NATURAL_EXPONENT = -41e18;

    // Bounds for ln_36's argument. Both ln(0.9) and ln(1.1) can be represented with 36 decimal places in a fixed point
    // 256 bit integer.
    int256 constant LN_36_LOWER_BOUND = ONE_18 - 1e17;
    int256 constant LN_36_UPPER_BOUND = ONE_18 + 1e17;

    uint256 constant MILD_EXPONENT_BOUND = 2**254 / uint256(ONE_20);

    // 18 decimal constants
    int256 constant x0 = 128000000000000000000; // 2ˆ7
    int256 constant a0 = 38877084059945950922200000000000000000000000000000000000; // eˆ(x0) (no decimals)
    int256 constant x1 = 64000000000000000000; // 2ˆ6
    int256 constant a1 = 6235149080811616882910000000; // eˆ(x1) (no decimals)

    // 20 decimal constants
    int256 constant x2 = 3200000000000000000000; // 2ˆ5
    int256 constant a2 = 7896296018268069516100000000000000; // eˆ(x2)
    int256 constant x3 = 1600000000000000000000; // 2ˆ4
    int256 constant a3 = 888611052050787263676000000; // eˆ(x3)
    int256 constant x4 = 800000000000000000000; // 2ˆ3
    int256 constant a4 = 298095798704172827474000; // eˆ(x4)
    int256 constant x5 = 400000000000000000000; // 2ˆ2
    int256 constant a5 = 5459815003314423907810; // eˆ(x5)
    int256 constant x6 = 200000000000000000000; // 2ˆ1
    int256 constant a6 = 738905609893065022723; // eˆ(x6)
    int256 constant x7 = 100000000000000000000; // 2ˆ0
    int256 constant a7 = 271828182845904523536; // eˆ(x7)
    int256 constant x8 = 50000000000000000000; // 2ˆ-1
    int256 constant a8 = 164872127070012814685; // eˆ(x8)
    int256 constant x9 = 25000000000000000000; // 2ˆ-2
    int256 constant a9 = 128402541668774148407; // eˆ(x9)
    int256 constant x10 = 12500000000000000000; // 2ˆ-3
    int256 constant a10 = 113314845306682631683; // eˆ(x10)
    int256 constant x11 = 6250000000000000000; // 2ˆ-4
    int256 constant a11 = 106449445891785942956; // eˆ(x11)

    /**
     * @dev Exponentiation (x^y) with unsigned 18 decimal fixed point base and exponent.
     *
     * Reverts if ln(x) * y is smaller than `MIN_NATURAL_EXPONENT`, or larger than `MAX_NATURAL_EXPONENT`.
     */
    function pow(uint256 x, uint256 y) internal pure returns (uint256) {
        if (y == 0) {
            // We solve the 0^0 indetermination by making it equal one.
            return uint256(ONE_18);
        }

        if (x == 0) {
            return 0;
        }

        // Instead of computing x^y directly, we instead rely on the properties of logarithms and exponentiation to
        // arrive at that result. In particular, exp(ln(x)) = x, and ln(x^y) = y * ln(x). This means
        // x^y = exp(y * ln(x)).

        // The ln function takes a signed value, so we need to make sure x fits in the signed 256 bit range.
        _require(x < 2**255, Errors.X_OUT_OF_BOUNDS);
        int256 x_int256 = int256(x);

        // We will compute y * ln(x) in a single step. Depending on the value of x, we can either use ln or ln_36. In
        // both cases, we leave the division by ONE_18 (due to fixed point multiplication) to the end.

        // This prevents y * ln(x) from overflowing, and at the same time guarantees y fits in the signed 256 bit range.
        _require(y < MILD_EXPONENT_BOUND, Errors.Y_OUT_OF_BOUNDS);
        int256 y_int256 = int256(y);

        int256 logx_times_y;
        if (LN_36_LOWER_BOUND < x_int256 && x_int256 < LN_36_UPPER_BOUND) {
            int256 ln_36_x = _ln_36(x_int256);

            // ln_36_x has 36 decimal places, so multiplying by y_int256 isn't as straightforward, since we can't just
            // bring y_int256 to 36 decimal places, as it might overflow. Instead, we perform two 18 decimal
            // multiplications and add the results: one with the first 18 decimals of ln_36_x, and one with the
            // (downscaled) last 18 decimals.
            logx_times_y = ((ln_36_x / ONE_18) * y_int256 + ((ln_36_x % ONE_18) * y_int256) / ONE_18);
        } else {
            logx_times_y = _ln(x_int256) * y_int256;
        }
        logx_times_y /= ONE_18;

        // Finally, we compute exp(y * ln(x)) to arrive at x^y
        _require(
            MIN_NATURAL_EXPONENT <= logx_times_y && logx_times_y <= MAX_NATURAL_EXPONENT,
            Errors.PRODUCT_OUT_OF_BOUNDS
        );

        return uint256(exp(logx_times_y));
    }

    /**
     * @dev Natural exponentiation (e^x) with signed 18 decimal fixed point exponent.
     *
     * Reverts if `x` is smaller than MIN_NATURAL_EXPONENT, or larger than `MAX_NATURAL_EXPONENT`.
     */
    function exp(int256 x) internal pure returns (int256) {
        _require(x >= MIN_NATURAL_EXPONENT && x <= MAX_NATURAL_EXPONENT, Errors.INVALID_EXPONENT);

        if (x < 0) {
            // We only handle positive exponents: e^(-x) is computed as 1 / e^x. We can safely make x positive since it
            // fits in the signed 256 bit range (as it is larger than MIN_NATURAL_EXPONENT).
            // Fixed point division requires multiplying by ONE_18.
            return ((ONE_18 * ONE_18) / exp(-x));
        }

        // First, we use the fact that e^(x+y) = e^x * e^y to decompose x into a sum of powers of two, which we call x_n,
        // where x_n == 2^(7 - n), and e^x_n = a_n has been precomputed. We choose the first x_n, x0, to equal 2^7
        // because all larger powers are larger than MAX_NATURAL_EXPONENT, and therefore not present in the
        // decomposition.
        // At the end of this process we will have the product of all e^x_n = a_n that apply, and the remainder of this
        // decomposition, which will be lower than the smallest x_n.
        // exp(x) = k_0 * a_0 * k_1 * a_1 * ... + k_n * a_n * exp(remainder), where each k_n equals either 0 or 1.
        // We mutate x by subtracting x_n, making it the remainder of the decomposition.

        // The first two a_n (e^(2^7) and e^(2^6)) are too large if stored as 18 decimal numbers, and could cause
        // intermediate overflows. Instead we store them as plain integers, with 0 decimals.
        // Additionally, x0 + x1 is larger than MAX_NATURAL_EXPONENT, which means they will not both be present in the
        // decomposition.

        // For each x_n, we test if that term is present in the decomposition (if x is larger than it), and if so deduct
        // it and compute the accumulated product.

        int256 firstAN;
        if (x >= x0) {
            x -= x0;
            firstAN = a0;
        } else if (x >= x1) {
            x -= x1;
            firstAN = a1;
        } else {
            firstAN = 1; // One with no decimal places
        }

        // We now transform x into a 20 decimal fixed point number, to have enhanced precision when computing the
        // smaller terms.
        x *= 100;

        // `product` is the accumulated product of all a_n (except a0 and a1), which starts at 20 decimal fixed point
        // one. Recall that fixed point multiplication requires dividing by ONE_20.
        int256 product = ONE_20;

        if (x >= x2) {
            x -= x2;
            product = (product * a2) / ONE_20;
        }
        if (x >= x3) {
            x -= x3;
            product = (product * a3) / ONE_20;
        }
        if (x >= x4) {
            x -= x4;
            product = (product * a4) / ONE_20;
        }
        if (x >= x5) {
            x -= x5;
            product = (product * a5) / ONE_20;
        }
        if (x >= x6) {
            x -= x6;
            product = (product * a6) / ONE_20;
        }
        if (x >= x7) {
            x -= x7;
            product = (product * a7) / ONE_20;
        }
        if (x >= x8) {
            x -= x8;
            product = (product * a8) / ONE_20;
        }
        if (x >= x9) {
            x -= x9;
            product = (product * a9) / ONE_20;
        }

        // x10 and x11 are unnecessary here since we have high enough precision already.

        // Now we need to compute e^x, where x is small (in particular, it is smaller than x9). We use the Taylor series
        // expansion for e^x: 1 + x + (x^2 / 2!) + (x^3 / 3!) + ... + (x^n / n!).

        int256 seriesSum = ONE_20; // The initial one in the sum, with 20 decimal places.
        int256 term; // Each term in the sum, where the nth term is (x^n / n!).

        // The first term is simply x.
        term = x;
        seriesSum += term;

        // Each term (x^n / n!) equals the previous one times x, divided by n. Since x is a fixed point number,
        // multiplying by it requires dividing by ONE_20, but dividing by the non-fixed point n values does not.

        term = ((term * x) / ONE_20) / 2;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 3;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 4;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 5;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 6;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 7;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 8;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 9;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 10;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 11;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 12;
        seriesSum += term;

        // 12 Taylor terms are sufficient for 18 decimal precision.

        // We now have the first a_n (with no decimals), and the product of all other a_n present, and the Taylor
        // approximation of the exponentiation of the remainder (both with 20 decimals). All that remains is to multiply
        // all three (one 20 decimal fixed point multiplication, dividing by ONE_20, and one integer multiplication),
        // and then drop two digits to return an 18 decimal value.

        return (((product * seriesSum) / ONE_20) * firstAN) / 100;
    }

    /**
     * @dev Logarithm (log(arg, base), with signed 18 decimal fixed point base and argument.
     */
    function log(int256 arg, int256 base) internal pure returns (int256) {
        // This performs a simple base change: log(arg, base) = ln(arg) / ln(base).

        // Both logBase and logArg are computed as 36 decimal fixed point numbers, either by using ln_36, or by
        // upscaling.

        int256 logBase;
        if (LN_36_LOWER_BOUND < base && base < LN_36_UPPER_BOUND) {
            logBase = _ln_36(base);
        } else {
            logBase = _ln(base) * ONE_18;
        }

        int256 logArg;
        if (LN_36_LOWER_BOUND < arg && arg < LN_36_UPPER_BOUND) {
            logArg = _ln_36(arg);
        } else {
            logArg = _ln(arg) * ONE_18;
        }

        // When dividing, we multiply by ONE_18 to arrive at a result with 18 decimal places
        return (logArg * ONE_18) / logBase;
    }

    /**
     * @dev Natural logarithm (ln(a)) with signed 18 decimal fixed point argument.
     */
    function ln(int256 a) internal pure returns (int256) {
        // The real natural logarithm is not defined for negative numbers or zero.
        _require(a > 0, Errors.OUT_OF_BOUNDS);
        if (LN_36_LOWER_BOUND < a && a < LN_36_UPPER_BOUND) {
            return _ln_36(a) / ONE_18;
        } else {
            return _ln(a);
        }
    }

    /**
     * @dev Internal natural logarithm (ln(a)) with signed 18 decimal fixed point argument.
     */
    function _ln(int256 a) private pure returns (int256) {
        if (a < ONE_18) {
            // Since ln(a^k) = k * ln(a), we can compute ln(a) as ln(a) = ln((1/a)^(-1)) = - ln((1/a)). If a is less
            // than one, 1/a will be greater than one, and this if statement will not be entered in the recursive call.
            // Fixed point division requires multiplying by ONE_18.
            return (-_ln((ONE_18 * ONE_18) / a));
        }

        // First, we use the fact that ln^(a * b) = ln(a) + ln(b) to decompose ln(a) into a sum of powers of two, which
        // we call x_n, where x_n == 2^(7 - n), which are the natural logarithm of precomputed quantities a_n (that is,
        // ln(a_n) = x_n). We choose the first x_n, x0, to equal 2^7 because the exponential of all larger powers cannot
        // be represented as 18 fixed point decimal numbers in 256 bits, and are therefore larger than a.
        // At the end of this process we will have the sum of all x_n = ln(a_n) that apply, and the remainder of this
        // decomposition, which will be lower than the smallest a_n.
        // ln(a) = k_0 * x_0 + k_1 * x_1 + ... + k_n * x_n + ln(remainder), where each k_n equals either 0 or 1.
        // We mutate a by subtracting a_n, making it the remainder of the decomposition.

        // For reasons related to how `exp` works, the first two a_n (e^(2^7) and e^(2^6)) are not stored as fixed point
        // numbers with 18 decimals, but instead as plain integers with 0 decimals, so we need to multiply them by
        // ONE_18 to convert them to fixed point.
        // For each a_n, we test if that term is present in the decomposition (if a is larger than it), and if so divide
        // by it and compute the accumulated sum.

        int256 sum = 0;
        if (a >= a0 * ONE_18) {
            a /= a0; // Integer, not fixed point division
            sum += x0;
        }

        if (a >= a1 * ONE_18) {
            a /= a1; // Integer, not fixed point division
            sum += x1;
        }

        // All other a_n and x_n are stored as 20 digit fixed point numbers, so we convert the sum and a to this format.
        sum *= 100;
        a *= 100;

        // Because further a_n are  20 digit fixed point numbers, we multiply by ONE_20 when dividing by them.

        if (a >= a2) {
            a = (a * ONE_20) / a2;
            sum += x2;
        }

        if (a >= a3) {
            a = (a * ONE_20) / a3;
            sum += x3;
        }

        if (a >= a4) {
            a = (a * ONE_20) / a4;
            sum += x4;
        }

        if (a >= a5) {
            a = (a * ONE_20) / a5;
            sum += x5;
        }

        if (a >= a6) {
            a = (a * ONE_20) / a6;
            sum += x6;
        }

        if (a >= a7) {
            a = (a * ONE_20) / a7;
            sum += x7;
        }

        if (a >= a8) {
            a = (a * ONE_20) / a8;
            sum += x8;
        }

        if (a >= a9) {
            a = (a * ONE_20) / a9;
            sum += x9;
        }

        if (a >= a10) {
            a = (a * ONE_20) / a10;
            sum += x10;
        }

        if (a >= a11) {
            a = (a * ONE_20) / a11;
            sum += x11;
        }

        // a is now a small number (smaller than a_11, which roughly equals 1.06). This means we can use a Taylor series
        // that converges rapidly for values of `a` close to one - the same one used in ln_36.
        // Let z = (a - 1) / (a + 1).
        // ln(a) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))

        // Recall that 20 digit fixed point division requires multiplying by ONE_20, and multiplication requires
        // division by ONE_20.
        int256 z = ((a - ONE_20) * ONE_20) / (a + ONE_20);
        int256 z_squared = (z * z) / ONE_20;

        // num is the numerator of the series: the z^(2 * n + 1) term
        int256 num = z;

        // seriesSum holds the accumulated sum of each term in the series, starting with the initial z
        int256 seriesSum = num;

        // In each step, the numerator is multiplied by z^2
        num = (num * z_squared) / ONE_20;
        seriesSum += num / 3;

        num = (num * z_squared) / ONE_20;
        seriesSum += num / 5;

        num = (num * z_squared) / ONE_20;
        seriesSum += num / 7;

        num = (num * z_squared) / ONE_20;
        seriesSum += num / 9;

        num = (num * z_squared) / ONE_20;
        seriesSum += num / 11;

        // 6 Taylor terms are sufficient for 36 decimal precision.

        // Finally, we multiply by 2 (non fixed point) to compute ln(remainder)
        seriesSum *= 2;

        // We now have the sum of all x_n present, and the Taylor approximation of the logarithm of the remainder (both
        // with 20 decimals). All that remains is to sum these two, and then drop two digits to return a 18 decimal
        // value.

        return (sum + seriesSum) / 100;
    }

    /**
     * @dev Intrnal high precision (36 decimal places) natural logarithm (ln(x)) with signed 18 decimal fixed point argument,
     * for x close to one.
     *
     * Should only be used if x is between LN_36_LOWER_BOUND and LN_36_UPPER_BOUND.
     */
    function _ln_36(int256 x) private pure returns (int256) {
        // Since ln(1) = 0, a value of x close to one will yield a very small result, which makes using 36 digits
        // worthwhile.

        // First, we transform x to a 36 digit fixed point value.
        x *= ONE_18;

        // We will use the following Taylor expansion, which converges very rapidly. Let z = (x - 1) / (x + 1).
        // ln(x) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))

        // Recall that 36 digit fixed point division requires multiplying by ONE_36, and multiplication requires
        // division by ONE_36.
        int256 z = ((x - ONE_36) * ONE_36) / (x + ONE_36);
        int256 z_squared = (z * z) / ONE_36;

        // num is the numerator of the series: the z^(2 * n + 1) term
        int256 num = z;

        // seriesSum holds the accumulated sum of each term in the series, starting with the initial z
        int256 seriesSum = num;

        // In each step, the numerator is multiplied by z^2
        num = (num * z_squared) / ONE_36;
        seriesSum += num / 3;

        num = (num * z_squared) / ONE_36;
        seriesSum += num / 5;

        num = (num * z_squared) / ONE_36;
        seriesSum += num / 7;

        num = (num * z_squared) / ONE_36;
        seriesSum += num / 9;

        num = (num * z_squared) / ONE_36;
        seriesSum += num / 11;

        num = (num * z_squared) / ONE_36;
        seriesSum += num / 13;

        num = (num * z_squared) / ONE_36;
        seriesSum += num / 15;

        // 8 Taylor terms are sufficient for 36 decimal precision.

        // All that remains is multiplying by 2 (non fixed point).
        return seriesSum * 2;
    }
}

File 11 of 12 : BalancerErrors.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.7.0;

// solhint-disable

/**
 * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are
 * supported.
 */
function _require(bool condition, uint256 errorCode) pure {
    if (!condition) _revert(errorCode);
}

/**
 * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.
 */
function _revert(uint256 errorCode) pure {
    // We're going to dynamically create a revert string based on the error code, with the following format:
    // 'BAL#{errorCode}'
    // where the code is left-padded with zeroes to three digits (so they range from 000 to 999).
    //
    // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a
    // number (8 to 16 bits) than the individual string characters.
    //
    // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a
    // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a
    // safe place to rely on it without worrying about how its usage might affect e.g. memory contents.
    assembly {
        // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999
        // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for
        // the '0' character.

        let units := add(mod(errorCode, 10), 0x30)

        errorCode := div(errorCode, 10)
        let tenths := add(mod(errorCode, 10), 0x30)

        errorCode := div(errorCode, 10)
        let hundreds := add(mod(errorCode, 10), 0x30)

        // With the individual characters, we can now construct the full string. The "BAL#" part is a known constant
        // (0x42414c23): we simply shift this by 24 (to provide space for the 3 bytes of the error code), and add the
        // characters to it, each shifted by a multiple of 8.
        // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits
        // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte
        // array).

        let revertReason := shl(200, add(0x42414c23000000, add(add(units, shl(8, tenths)), shl(16, hundreds))))

        // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded
        // message will have the following layout:
        // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ]

        // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We
        // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten.
        mstore(0x0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
        // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away).
        mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020)
        // The string length is fixed: 7 characters.
        mstore(0x24, 7)
        // Finally, the string itself is stored.
        mstore(0x44, revertReason)

        // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of
        // the encoded message is therefore 4 + 32 + 32 + 32 = 100.
        revert(0, 100)
    }
}

library Errors {
    // Math
    uint256 internal constant ADD_OVERFLOW = 0;
    uint256 internal constant SUB_OVERFLOW = 1;
    uint256 internal constant SUB_UNDERFLOW = 2;
    uint256 internal constant MUL_OVERFLOW = 3;
    uint256 internal constant ZERO_DIVISION = 4;
    uint256 internal constant DIV_INTERNAL = 5;
    uint256 internal constant X_OUT_OF_BOUNDS = 6;
    uint256 internal constant Y_OUT_OF_BOUNDS = 7;
    uint256 internal constant PRODUCT_OUT_OF_BOUNDS = 8;
    uint256 internal constant INVALID_EXPONENT = 9;

    // Input
    uint256 internal constant OUT_OF_BOUNDS = 100;
    uint256 internal constant UNSORTED_ARRAY = 101;
    uint256 internal constant UNSORTED_TOKENS = 102;
    uint256 internal constant INPUT_LENGTH_MISMATCH = 103;
    uint256 internal constant ZERO_TOKEN = 104;

    // Shared pools
    uint256 internal constant MIN_TOKENS = 200;
    uint256 internal constant MAX_TOKENS = 201;
    uint256 internal constant MAX_SWAP_FEE_PERCENTAGE = 202;
    uint256 internal constant MIN_SWAP_FEE_PERCENTAGE = 203;
    uint256 internal constant MINIMUM_BPT = 204;
    uint256 internal constant CALLER_NOT_VAULT = 205;
    uint256 internal constant UNINITIALIZED = 206;
    uint256 internal constant BPT_IN_MAX_AMOUNT = 207;
    uint256 internal constant BPT_OUT_MIN_AMOUNT = 208;
    uint256 internal constant EXPIRED_PERMIT = 209;
    uint256 internal constant NOT_TWO_TOKENS = 210;

    // Pools
    uint256 internal constant MIN_AMP = 300;
    uint256 internal constant MAX_AMP = 301;
    uint256 internal constant MIN_WEIGHT = 302;
    uint256 internal constant MAX_STABLE_TOKENS = 303;
    uint256 internal constant MAX_IN_RATIO = 304;
    uint256 internal constant MAX_OUT_RATIO = 305;
    uint256 internal constant MIN_BPT_IN_FOR_TOKEN_OUT = 306;
    uint256 internal constant MAX_OUT_BPT_FOR_TOKEN_IN = 307;
    uint256 internal constant NORMALIZED_WEIGHT_INVARIANT = 308;
    uint256 internal constant INVALID_TOKEN = 309;
    uint256 internal constant UNHANDLED_JOIN_KIND = 310;
    uint256 internal constant ZERO_INVARIANT = 311;
    uint256 internal constant ORACLE_INVALID_SECONDS_QUERY = 312;
    uint256 internal constant ORACLE_NOT_INITIALIZED = 313;
    uint256 internal constant ORACLE_QUERY_TOO_OLD = 314;
    uint256 internal constant ORACLE_INVALID_INDEX = 315;
    uint256 internal constant ORACLE_BAD_SECS = 316;
    uint256 internal constant AMP_END_TIME_TOO_CLOSE = 317;
    uint256 internal constant AMP_ONGOING_UPDATE = 318;
    uint256 internal constant AMP_RATE_TOO_HIGH = 319;
    uint256 internal constant AMP_NO_ONGOING_UPDATE = 320;
    uint256 internal constant STABLE_INVARIANT_DIDNT_CONVERGE = 321;
    uint256 internal constant STABLE_GET_BALANCE_DIDNT_CONVERGE = 322;
    uint256 internal constant RELAYER_NOT_CONTRACT = 323;
    uint256 internal constant BASE_POOL_RELAYER_NOT_CALLED = 324;
    uint256 internal constant REBALANCING_RELAYER_REENTERED = 325;
    uint256 internal constant GRADUAL_UPDATE_TIME_TRAVEL = 326;
    uint256 internal constant SWAPS_DISABLED = 327;
    uint256 internal constant CALLER_IS_NOT_LBP_OWNER = 328;
    uint256 internal constant PRICE_RATE_OVERFLOW = 329;
    uint256 internal constant INVALID_JOIN_EXIT_KIND_WHILE_SWAPS_DISABLED = 330;
    uint256 internal constant WEIGHT_CHANGE_TOO_FAST = 331;
    uint256 internal constant LOWER_GREATER_THAN_UPPER_TARGET = 332;
    uint256 internal constant UPPER_TARGET_TOO_HIGH = 333;
    uint256 internal constant UNHANDLED_BY_LINEAR_POOL = 334;
    uint256 internal constant OUT_OF_TARGET_RANGE = 335;
    uint256 internal constant UNHANDLED_EXIT_KIND = 336;
    uint256 internal constant UNAUTHORIZED_EXIT = 337;
    uint256 internal constant MAX_MANAGEMENT_SWAP_FEE_PERCENTAGE = 338;
    uint256 internal constant UNHANDLED_BY_INVESTMENT_POOL = 339;
    uint256 internal constant UNHANDLED_BY_PHANTOM_POOL = 340;
    uint256 internal constant TOKEN_DOES_NOT_HAVE_RATE_PROVIDER = 341;
    uint256 internal constant INVALID_INITIALIZATION = 342;

    // Lib
    uint256 internal constant REENTRANCY = 400;
    uint256 internal constant SENDER_NOT_ALLOWED = 401;
    uint256 internal constant PAUSED = 402;
    uint256 internal constant PAUSE_WINDOW_EXPIRED = 403;
    uint256 internal constant MAX_PAUSE_WINDOW_DURATION = 404;
    uint256 internal constant MAX_BUFFER_PERIOD_DURATION = 405;
    uint256 internal constant INSUFFICIENT_BALANCE = 406;
    uint256 internal constant INSUFFICIENT_ALLOWANCE = 407;
    uint256 internal constant ERC20_TRANSFER_FROM_ZERO_ADDRESS = 408;
    uint256 internal constant ERC20_TRANSFER_TO_ZERO_ADDRESS = 409;
    uint256 internal constant ERC20_MINT_TO_ZERO_ADDRESS = 410;
    uint256 internal constant ERC20_BURN_FROM_ZERO_ADDRESS = 411;
    uint256 internal constant ERC20_APPROVE_FROM_ZERO_ADDRESS = 412;
    uint256 internal constant ERC20_APPROVE_TO_ZERO_ADDRESS = 413;
    uint256 internal constant ERC20_TRANSFER_EXCEEDS_ALLOWANCE = 414;
    uint256 internal constant ERC20_DECREASED_ALLOWANCE_BELOW_ZERO = 415;
    uint256 internal constant ERC20_TRANSFER_EXCEEDS_BALANCE = 416;
    uint256 internal constant ERC20_BURN_EXCEEDS_ALLOWANCE = 417;
    uint256 internal constant SAFE_ERC20_CALL_FAILED = 418;
    uint256 internal constant ADDRESS_INSUFFICIENT_BALANCE = 419;
    uint256 internal constant ADDRESS_CANNOT_SEND_VALUE = 420;
    uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_INT256 = 421;
    uint256 internal constant GRANT_SENDER_NOT_ADMIN = 422;
    uint256 internal constant REVOKE_SENDER_NOT_ADMIN = 423;
    uint256 internal constant RENOUNCE_SENDER_NOT_ALLOWED = 424;
    uint256 internal constant BUFFER_PERIOD_EXPIRED = 425;
    uint256 internal constant CALLER_IS_NOT_OWNER = 426;
    uint256 internal constant NEW_OWNER_IS_ZERO = 427;
    uint256 internal constant CODE_DEPLOYMENT_FAILED = 428;
    uint256 internal constant CALL_TO_NON_CONTRACT = 429;
    uint256 internal constant LOW_LEVEL_CALL_FAILED = 430;

    // Vault
    uint256 internal constant INVALID_POOL_ID = 500;
    uint256 internal constant CALLER_NOT_POOL = 501;
    uint256 internal constant SENDER_NOT_ASSET_MANAGER = 502;
    uint256 internal constant USER_DOESNT_ALLOW_RELAYER = 503;
    uint256 internal constant INVALID_SIGNATURE = 504;
    uint256 internal constant EXIT_BELOW_MIN = 505;
    uint256 internal constant JOIN_ABOVE_MAX = 506;
    uint256 internal constant SWAP_LIMIT = 507;
    uint256 internal constant SWAP_DEADLINE = 508;
    uint256 internal constant CANNOT_SWAP_SAME_TOKEN = 509;
    uint256 internal constant UNKNOWN_AMOUNT_IN_FIRST_SWAP = 510;
    uint256 internal constant MALCONSTRUCTED_MULTIHOP_SWAP = 511;
    uint256 internal constant INTERNAL_BALANCE_OVERFLOW = 512;
    uint256 internal constant INSUFFICIENT_INTERNAL_BALANCE = 513;
    uint256 internal constant INVALID_ETH_INTERNAL_BALANCE = 514;
    uint256 internal constant INVALID_POST_LOAN_BALANCE = 515;
    uint256 internal constant INSUFFICIENT_ETH = 516;
    uint256 internal constant UNALLOCATED_ETH = 517;
    uint256 internal constant ETH_TRANSFER = 518;
    uint256 internal constant CANNOT_USE_ETH_SENTINEL = 519;
    uint256 internal constant TOKENS_MISMATCH = 520;
    uint256 internal constant TOKEN_NOT_REGISTERED = 521;
    uint256 internal constant TOKEN_ALREADY_REGISTERED = 522;
    uint256 internal constant TOKENS_ALREADY_SET = 523;
    uint256 internal constant TOKENS_LENGTH_MUST_BE_2 = 524;
    uint256 internal constant NONZERO_TOKEN_BALANCE = 525;
    uint256 internal constant BALANCE_TOTAL_OVERFLOW = 526;
    uint256 internal constant POOL_NO_TOKENS = 527;
    uint256 internal constant INSUFFICIENT_FLASH_LOAN_BALANCE = 528;

    // Fees
    uint256 internal constant SWAP_FEE_PERCENTAGE_TOO_HIGH = 600;
    uint256 internal constant FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH = 601;
    uint256 internal constant INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT = 602;
}

File 12 of 12 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"uint256[]","name":"balances","type":"uint256[]"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"bool","name":"tokenInIsToken0","type":"bool"},{"components":[{"internalType":"int256","name":"alpha","type":"int256"},{"internalType":"int256","name":"beta","type":"int256"},{"internalType":"int256","name":"c","type":"int256"},{"internalType":"int256","name":"s","type":"int256"},{"internalType":"int256","name":"lambda","type":"int256"}],"internalType":"struct GyroECLPMath.Params","name":"params","type":"tuple"},{"components":[{"components":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"internalType":"struct GyroECLPMath.Vector2","name":"tauAlpha","type":"tuple"},{"components":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"internalType":"struct GyroECLPMath.Vector2","name":"tauBeta","type":"tuple"},{"internalType":"int256","name":"u","type":"int256"},{"internalType":"int256","name":"v","type":"int256"},{"internalType":"int256","name":"w","type":"int256"},{"internalType":"int256","name":"z","type":"int256"},{"internalType":"int256","name":"dSq","type":"int256"}],"internalType":"struct GyroECLPMath.DerivedParams","name":"derived","type":"tuple"},{"components":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"internalType":"struct GyroECLPMath.Vector2","name":"invariant","type":"tuple"}],"name":"calcInGivenOut","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"balances","type":"uint256[]"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"bool","name":"tokenInIsToken0","type":"bool"},{"components":[{"internalType":"int256","name":"alpha","type":"int256"},{"internalType":"int256","name":"beta","type":"int256"},{"internalType":"int256","name":"c","type":"int256"},{"internalType":"int256","name":"s","type":"int256"},{"internalType":"int256","name":"lambda","type":"int256"}],"internalType":"struct GyroECLPMath.Params","name":"params","type":"tuple"},{"components":[{"components":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"internalType":"struct GyroECLPMath.Vector2","name":"tauAlpha","type":"tuple"},{"components":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"internalType":"struct GyroECLPMath.Vector2","name":"tauBeta","type":"tuple"},{"internalType":"int256","name":"u","type":"int256"},{"internalType":"int256","name":"v","type":"int256"},{"internalType":"int256","name":"w","type":"int256"},{"internalType":"int256","name":"z","type":"int256"},{"internalType":"int256","name":"dSq","type":"int256"}],"internalType":"struct GyroECLPMath.DerivedParams","name":"derived","type":"tuple"},{"components":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"internalType":"struct GyroECLPMath.Vector2","name":"invariant","type":"tuple"}],"name":"calcOutGivenIn","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"balances","type":"uint256[]"},{"components":[{"internalType":"int256","name":"alpha","type":"int256"},{"internalType":"int256","name":"beta","type":"int256"},{"internalType":"int256","name":"c","type":"int256"},{"internalType":"int256","name":"s","type":"int256"},{"internalType":"int256","name":"lambda","type":"int256"}],"internalType":"struct GyroECLPMath.Params","name":"params","type":"tuple"},{"components":[{"components":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"internalType":"struct GyroECLPMath.Vector2","name":"tauAlpha","type":"tuple"},{"components":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"internalType":"struct GyroECLPMath.Vector2","name":"tauBeta","type":"tuple"},{"internalType":"int256","name":"u","type":"int256"},{"internalType":"int256","name":"v","type":"int256"},{"internalType":"int256","name":"w","type":"int256"},{"internalType":"int256","name":"z","type":"int256"},{"internalType":"int256","name":"dSq","type":"int256"}],"internalType":"struct GyroECLPMath.DerivedParams","name":"derived","type":"tuple"},{"internalType":"int256","name":"invariant","type":"int256"}],"name":"calcSpotPrice0in1","outputs":[{"internalType":"uint256","name":"px","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"balances","type":"uint256[]"},{"components":[{"internalType":"int256","name":"alpha","type":"int256"},{"internalType":"int256","name":"beta","type":"int256"},{"internalType":"int256","name":"c","type":"int256"},{"internalType":"int256","name":"s","type":"int256"},{"internalType":"int256","name":"lambda","type":"int256"}],"internalType":"struct GyroECLPMath.Params","name":"params","type":"tuple"},{"components":[{"components":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"internalType":"struct GyroECLPMath.Vector2","name":"tauAlpha","type":"tuple"},{"components":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"internalType":"struct GyroECLPMath.Vector2","name":"tauBeta","type":"tuple"},{"internalType":"int256","name":"u","type":"int256"},{"internalType":"int256","name":"v","type":"int256"},{"internalType":"int256","name":"w","type":"int256"},{"internalType":"int256","name":"z","type":"int256"},{"internalType":"int256","name":"dSq","type":"int256"}],"internalType":"struct GyroECLPMath.DerivedParams","name":"derived","type":"tuple"}],"name":"calculateInvariant","outputs":[{"internalType":"uint256","name":"uinvariant","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"balances","type":"uint256[]"},{"components":[{"internalType":"int256","name":"alpha","type":"int256"},{"internalType":"int256","name":"beta","type":"int256"},{"internalType":"int256","name":"c","type":"int256"},{"internalType":"int256","name":"s","type":"int256"},{"internalType":"int256","name":"lambda","type":"int256"}],"internalType":"struct GyroECLPMath.Params","name":"params","type":"tuple"},{"components":[{"components":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"internalType":"struct GyroECLPMath.Vector2","name":"tauAlpha","type":"tuple"},{"components":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"internalType":"struct GyroECLPMath.Vector2","name":"tauBeta","type":"tuple"},{"internalType":"int256","name":"u","type":"int256"},{"internalType":"int256","name":"v","type":"int256"},{"internalType":"int256","name":"w","type":"int256"},{"internalType":"int256","name":"z","type":"int256"},{"internalType":"int256","name":"dSq","type":"int256"}],"internalType":"struct GyroECLPMath.DerivedParams","name":"derived","type":"tuple"}],"name":"calculateInvariantWithError","outputs":[{"internalType":"int256","name":"","type":"int256"},{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"int256","name":"alpha","type":"int256"},{"internalType":"int256","name":"beta","type":"int256"},{"internalType":"int256","name":"c","type":"int256"},{"internalType":"int256","name":"s","type":"int256"},{"internalType":"int256","name":"lambda","type":"int256"}],"internalType":"struct GyroECLPMath.Params","name":"params","type":"tuple"},{"components":[{"components":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"internalType":"struct GyroECLPMath.Vector2","name":"tauAlpha","type":"tuple"},{"components":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"internalType":"struct GyroECLPMath.Vector2","name":"tauBeta","type":"tuple"},{"internalType":"int256","name":"u","type":"int256"},{"internalType":"int256","name":"v","type":"int256"},{"internalType":"int256","name":"w","type":"int256"},{"internalType":"int256","name":"z","type":"int256"},{"internalType":"int256","name":"dSq","type":"int256"}],"internalType":"struct GyroECLPMath.DerivedParams","name":"derived","type":"tuple"}],"name":"validateDerivedParamsLimits","outputs":[],"stateMutability":"pure","type":"function"}]

612516610026600b82828239805160001a60731461001957fe5b30600052607381538281f3fe730000000000000000000000000000000000000000301460806040526004361061006c5760003560e01c806361ff4236146100715780636e70c2ef1461009a57806378ace857146100af578063c7fe78d0146100d0578063d83ca862146100e3578063ec6339ec146100f6575b600080fd5b61008461007f3660046123da565b610109565b60405161009191906124af565b60405180910390f35b6100ad6100a836600461246c565b6101cc565b005b6100c26100bd366004612313565b61038f565b6040516100919291906124a1565b6100846100de366004612372565b610557565b6100846100f1366004612313565b6106a6565b6100846101043660046123da565b6106c9565b600061210c6000808715610128575061078f9150600090506001610135565b506107eb91506001905060005b60006101686101638b8d8660ff168151811061014d57fe5b602002602001015161086090919063ffffffff16565b610881565b905061017788888884876108cd565b6000610189828a8a8a8963ffffffff16565b90506101bc61019782610962565b8d8560ff16815181106101a657fe5b60200260200101516109b990919063ffffffff16565b9c9b505050505050505050505050565b80516000906101db90806109d5565b90506102186f4b3b4ca85a86af4d06c240f509800000821280159061021057506f4b3b4ca85a86d9a70c52038af68000008213155b610166610a0f565b602082015161022790806109d5565b90506102636f4b3b4ca85a86af4d06c240f509800000821280159061021057506f4b3b4ca85a86d9a70c52038af6800000821315610166610a0f565b6102856b012ced32a16a1b11e826288960261b83604001511315610169610a0f565b6102a76b012ced32a16a1b11e826288960261b83606001511315610169610a0f565b6102c96b012ced32a16a1b11e826288960261b83608001511315610169610a0f565b6102eb6b012ced32a16a1b11e826288960261b8360a001511315610169610a0f565b60c082015161032f906f4b3b4ca85a86af4d06c240f50980000013801590610327575060c08301516f4b3b4ca85a86d9a70c52038af680000012155b61016c610a0f565b60006103666b012ced32a16a1b11e826288960261b61034e8686610a21565b6b012ced32a16a1b11e826288960261b919003610aff565b90506103897172cb5bd86321e38cb6ce6682e8000000000082131561016a610a0f565b50505050565b6000806000806103b2876000815181106103a557fe5b6020026020010151610881565b6103c2886001815181106103a557fe5b90925090506103ee6e01ed09bead87c0378d8e64000000006103e48484610b34565b131561016b610a0f565b60006103fc83838989610b5d565b905060008061040d85858b8b610c96565b9150915060008213156104315761042a6001820160028402610d1c565b905061045c565b6000811361044357633b9aca00610459565b61045961016361045283610962565b6005610d9d565b90505b806b012ced32a16a1b11e826288960261b6104868688018c60800151610f3390919063ffffffff16565b8161048d57fe5b0501600101601402905060006104b66b012ced32a16a1b11e826288960261b61034e8c8c610a21565b905060006104c985850184900383610f85565b90506104d58383610fe5565b60808c01519093506b012ced32a16a1b11e826288960261b906ec097ce7bc90715b34b9f10000000009080020561050c8385610fe5565b026028028161051757fe5b05830160010192506105466f1691ca32818ed48b02dca3e0000000006103e48584610b3490919063ffffffff16565b9b919a509098505050505050505050565b60008060405180604001604052808481526020018481525090506000604051806040016040528061058988888661102c565b81526020016105998888866110e7565b81525090506000604051806040016040528083600001516105c08b6000815181106103a557fe5b03815260200183602001516105db8b6001815181106103a557fe5b03905290506105ea878261118e565b9050600060405180604001604052806106148460200151856000015161120c90919063ffffffff16565b8152602001670de0b6b3a764000081525090506000610659826106548b6040518060400160405280670de0b6b3a76400008152602001600081525061118e565b611232565b905061069861069361068c846106548d604051806040016040528060008152602001670de0b6b3a764000081525061118e565b839061125f565b610962565b9a9950505050505050505050565b6000806106b485858561038f565b5090506106c081610962565b95945050505050565b600061210c60008087156106e857506107eb91506000905060016106f5565b5061078f91506001905060005b898160ff168151811061070457fe5b6020026020010151891115610720576107206000610165610a0f565b60006107438a8c8460ff168151811061073557fe5b602002602001015103610881565b90506000610757828a8a8a8963ffffffff16565b905061076689898984886108cd565b6101bc8c8560ff168151811061077857fe5b602002602001015161078983610962565b906109b9565b60008060405180604001604052806107a887878761102c565b81526020016107b88787876110e7565b81525090506107e18560800151878760600151886040015187868a602001518b60c001516112b9565b9695505050505050565b60008060405180604001604052806108048787876110e7565b815260200161081487878761102c565b81525090506107e185608001518787604001518860600151878660405180604001604052808c600001516000015160000381526020018c60000151602001518152508b60c001516112b9565b60008282018381101561087857610878600080611521565b90505b92915050565b6000600160ff1b82106108c55760405162461bcd60e51b81526004018080602001828103825260288152602001806124b96028913960400191505060405180910390fd5b50805b919050565b60ff811661091a5760006108e286868661152f565b90506e01ed09bead87c0378d8e640000000083131580156109035750808313155b610914576109146000610165610a0f565b5061095b565b60006109278686866115e5565b90506e01ed09bead87c0378d8e640000000083131580156109485750808313155b610959576109596000610165610a0f565b505b5050505050565b6000808212156108c5576040805162461bcd60e51b815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f736974697665604482015290519081900360640190fd5b6000828211156109cf576109cf60006001611521565b50900390565b6000610a086109f58360200151856020015161168a90919063ffffffff16565b83518551610a029161168a565b90610b34565b9392505050565b81610a1d57610a1d816116ca565b5050565b60c08101516000908190610a3f90610a3981806116dd565b906116dd565b9050610a77610a6c82610a66866060015187604001516002026116dd90919063ffffffff16565b90610aff565b608086015190610f33565b60808501516040850151919350610aa591610a9f90829082908690610a6690600101806116dd565b90610f33565b6060840151920191610abd908290610a6690806116dd565b8201915060008360a00151610ae386608001518660800151610d1c90919063ffffffff16565b019050610af482610a6683806116dd565b909201949350505050565b600081610b1257610b1260006004611521565b816b012ced32a16a1b11e826288960261b840281610b2c57fe5b059392505050565b600082820181831215610b4957838112610b4e565b838112155b61087857610878600080611521565b60c08101516000908190610b7190806116dd565b90506000610ba882610a6687608001518760a00151610ba18a608001518a6080015161120c90919063ffffffff16565b019061120c565b9050610bdc81610bc58760600151896116f790919063ffffffff16565b6040880151610bd5908b906116f7565b0390610f85565b92506000610c058660400151610bff88608001518a6116f790919063ffffffff16565b906116f7565b610c248760600151610bff89608001518c6116f790919063ffffffff16565b019050610c48610c41848760400151610aff90919063ffffffff16565b8290610f85565b6040870151940193610c5b9088906116f7565b6060870151610c6b908a906116f7565b019050610c88610c41848760600151610aff90919063ffffffff16565b909301979650505050505050565b600080610ca58686868661170a565b610cb187878787611837565b019150610cc086868686611969565b909101906b012ced32a16a1b11e826288960261b610cde8680610f33565b610ce88880610f33565b0181610cf057fe5b05905060008213610d02576000610d11565b610d1161016361045284610962565b915094509492505050565b600081610d2f57610d2f60006004611521565b82610d3c5750600061087b565b6000821215610d52578160000391508260000392505b6000831315610d7c57816001670de0b6b3a764000085020381610d7157fe5b05600101905061087b565b600182670de0b6b3a7640000850260010181610d9457fe5b05039392505050565b600082610dac5750600061087b565b6000610db784611a7f565b9050600281670de0b6b3a7640000860281610dce57fe5b04820181610dd857fe5b049050600281670de0b6b3a7640000860281610df057fe5b04820181610dfa57fe5b049050600281670de0b6b3a7640000860281610e1257fe5b04820181610e1c57fe5b049050600281670de0b6b3a7640000860281610e3457fe5b04820181610e3e57fe5b049050600281670de0b6b3a7640000860281610e5657fe5b04820181610e6057fe5b049050600281670de0b6b3a7640000860281610e7857fe5b04820181610e8257fe5b049050600281670de0b6b3a7640000860281610e9a57fe5b04820181610ea457fe5b0490506000610eb38280611c51565b9050610ec9610ec28386611c8d565b8690610860565b8111158015610eeb5750610ee7610ee08386611c8d565b86906109b9565b8110155b610f2b576040805162461bcd60e51b815260206004820152600c60248201526b17dcdc5c9d0811905253115160a21b604482015290519081900360640190fd5b509392505050565b600082820281811315610f5a57670de0b6b3a764000060001982010560010191505061087b565b6000811215610f7b576001670de0b6b3a7640000828201050391505061087b565b5060009392505050565b6000678ac7230489e80000808305908307848202858202848212801590610fad575060008112155b610fca576001678ac7230489e80000808305840182010503610fda565b678ac7230489e800008082058301055b979650505050505050565b6000678ac7230489e8000080830590830784820285820284821380159061100d575060008113155b610fca57678ac7230489e8000060001981830584010105600101610fda565b60c0820151602083015151600091829161104591610aff565b9050600084602001516000015113611089576110848161107e8760400151610bff896080015188602001516116f790919063ffffffff16565b90610fe5565b6110a8565b6040850151608086015184516110a892849261107e92610a9f91610f33565b91506110dc6110cc8560c00151866020015160200151610aff90919063ffffffff16565b6060870151855161107e91610f33565b909101949350505050565b60c082015182515160009182916110fd91610aff565b845151909150600013611139576111348161107e8760600151610bff896080015188602001516000036116f790919063ffffffff16565b611163565b6111638160000361107e8760600151610a9f89608001518860000151610f3390919063ffffffff16565b60c08501518551602001519193506110dc9161117e91610aff565b6040870151855161107e91610f33565b61119661210e565b6111bf83608001516111b9846020015186606001516116f790919063ffffffff16565b9061120c565b6080840151835160408601516111da92916111b991906116f7565b038152602082015160408401516111f0916116f7565b82516060850151611200916116f7565b01602082015292915050565b60008161121f5761121f60006004611521565b81670de0b6b3a7640000840281610b2c57fe5b6000610a0861125283602001518560200151611ce190919063ffffffff16565b83518551610a0291611ce1565b6000816112725761127260006004611521565b8261127f5750600061087b565b670de0b6b3a76400008381029084828161129557fe5b05146112a7576112a760006005611521565b8281816112b057fe5b05949350505050565b60006112c361210e565b6112e08a6111b96b012ced32a16a1b11e826288960261b8261120c565b6b012ced32a16a1b11e826288960261b908103825261130b908b906113059082610d1c565b90610d1c565b6b012ced32a16a1b11e826288960261b036020820152611329612128565b85518a03600081131561136557602083015161135b906113499087610aff565b61107e8b610bff60008690038f6116f7565b6020830152611390565b825161138a906113759087610aff565b60010161107e8b610a9f60008690038f610f33565b60208301525b61139861210e565b6113b786610a668d610bff8f89602001516116f790919063ffffffff16565b815283516113c5908c610f33565b602082018190526113df906001880190610a66908e610f33565b60010160208083019182526040805180820190915292516b012ced32a16a1b11e826288960261b90810384529151909103908201526114238c8a8f8e8e8c8c611d1a565b6000036040840152602080820151908a015161144a919061144490806116f7565b90610f85565b6040840180519190910190819052600012611466576000611479565b6114796101636104528560400151610962565b604084018190526020840151600091900313156114d4576114cd6114b982602001516b012ced32a16a1b11e826288960261b610aff90919063ffffffff16565b604085015160208601510390600101610fe5565b8352611508565b8051611505906114f4906b012ced32a16a1b11e826288960261b90610aff565b604085015160208601510390610fe5565b83525b5050602086015190510191505098975050505050505050565b81610a1d57610a1d81611fc2565b60c0820151825151602084015151600092839261154d920390610aff565b9050600061157a8560c0015186600001516020015187602001516020015103610aff90919063ffffffff16565b90506115a3826114448860400151610bff8a6080015189602001516116f790919063ffffffff16565b92506115d981600083136115c657606088015186516115c191610f33565b611444565b60608801516020870151611444916116f7565b90920195945050505050565b60c08201518251516020840151516000928392611603920390610aff565b905060006116308560c0015186602001516020015187600001516020015103610aff90919063ffffffff16565b9050611659826114448860600151610bff8a6080015189602001516116f790919063ffffffff16565b92506115d9816000831361167757604088015186516115c191610f33565b60408801516020870151611444916116f7565b60008282028315806116a45750828482816116a157fe5b05145b6116b4576116b460006003611521565b6b012ced32a16a1b11e826288960261b816112b0565b6116da816223aca960e91b612015565b50565b60006b012ced32a16a1b11e826288960261b838302610b2c565b6000670de0b6b3a7640000838302610b2c565b6000806117448460600151610bff8660400151600202611733898a610f3390919063ffffffff16565b61173d8b806116f7565b03906116f7565b9050600061175586600289026116f7565b606086015190915061176b90610bff83826116f7565b604086015161177e90610bff84826116f7565b830103915060006117b086608001516111b988608001516111b989606001518a608001516116dd90919063ffffffff16565b604086015160a08701516117c3916116dd565b0190506117fc86608001516117e987606001518860a001516116dd90919063ffffffff16565b60408801516080890151610ba1916116dd565b60c086015191019061181f9061181890610a39818181806116dd565b8290610aff565b905061182b8382610f85565b98975050505050505050565b6060820151600090819061185190610a9f81818980610f33565b604085015161186690610a9f81818b80610f33565b01905061188f8460600151610bff8660400151600202610bff898b6116f790919063ffffffff16565b6080850151606085015191909203916000916118b491906111b99082908290806116dd565b6118da86608001516111b9876060015188604001516002026116dd90919063ffffffff16565b60408601516118e990806116dd565b60c0870151910191909101915061190a9061181890610a39818181806116dd565b905061191a600083900382610f85565b925061195c6119458560c001516b012ced32a16a1b11e826288960261b610aff90919063ffffffff16565b6080870151611444906111b960081987018261120c565b9092019695505050505050565b6040820151600090819061198390610a9f81818980610f33565b606085015161199890610a9f81818b80610f33565b0190506119c18460400151610a9f8660600151600202610a9f898b610f3390919063ffffffff16565b6080858101519085015192909101916000916119e5916111b99082908290806116dd565b60a08501516119f490806116dd565b019050611a1d85608001516111b986608001518760a001516002026116dd90919063ffffffff16565b60c0850151910190611a399061181890610a39818181806116dd565b9050611a49600083900382610f85565b925061195c611a748560c001516b012ced32a16a1b11e826288960261b610aff90919063ffffffff16565b600819840190610f85565b6000670de0b6b3a76400008210611aac57670de0b6b3a7640000611aa4818404612076565b1b90506108c8565b600a8211611abf575063bc7c871c6108c8565b60648211611ad357506402540be4006108c8565b6103e88211611ae8575064075cdd47196108c8565b6127108211611afd575064174876e8006108c8565b620186a08211611b1357506449a0a4c7006108c8565b620f42408211611b29575064e8d4a510006108c8565b629896808211611b4057506502e0466fc6086108c8565b6305f5e1008211611b5857506509184e72a0006108c8565b633b9aca008211611b705750651cc2c05dbc536108c8565b6402540be4008211611b895750655af3107a40006108c8565b64174876e8008211611ba3575066011f9b83a95b456108c8565b64e8d4a510008211611bbd575066038d7ea4c680006108c8565b6509184e72a0008211611bd85750660b3c13249d90bb6108c8565b655af3107a40008211611bf35750662386f26fc100006108c8565b66038d7ea4c680008211611c0f5750667058bf6e27a7516108c8565b662386f26fc100008211611c2c575067016345785d8a00006108c8565b67016345785d8a00008211611c4a5750670463777a4d8c892d6108c8565b50806108c8565b6000828202831580611c6b575082848281611c6857fe5b04145b611c7b57611c7b60006003611521565b670de0b6b3a764000090049392505050565b6000828202831580611ca7575082848281611ca457fe5b04145b611cb757611cb760006003611521565b80611cc657600091505061087b565b670de0b6b3a764000060001982010460010191505092915050565b6000828202831580611cfb575082848281611cf857fe5b05145b611d0b57611d0b60006003611521565b670de0b6b3a7640000816112b0565b6000806040518060400160405280611d3b85866116dd90919063ffffffff16565b81528951602090910190611d4f9080610f33565b90529050611d5b612128565b815160208601518651600092611d76929091610a66916116dd565b90506000811315611db0576020830151611d939060028a02610f33565b808352611da990600783019061107e908a610f33565b8252611dde565b60208a0151611dc89060028a0290610bff90806116f7565b808352611ddb90829061107e908a6116f7565b82525b855160001315611e26578551611e1c90611df89087610aff565b60000360030161107e89600202610a9f8f8f60000151610f3390919063ffffffff16565b6020830152611e5d565b8551611e5790611e369087610aff565b61107e89600202610bff8f8f602001516000036116f790919063ffffffff16565b60208301525b6020808301518351018352835190870151611e7d9190610a6690806116dd565b6007019050611e99888460200151610f3390919063ffffffff16565b60208301819052611eb090829061107e908b610f33565b602080840191909152860151611eeb90611eca9087610aff565b61107e8a600202610bff8f8f602001516000036116f790919063ffffffff16565b6040830152611efa8b80610f33565b60408301516020840180519091019190910190819052600012611f2b576020820151611f26908a61120c565b611f3a565b6020820151611f3a908a610d1c565b60208301819052825101808352600012611f5f578151611f5a908a61120c565b611f6b565b8151611f6b908a610d1c565b825282518651611f809190610a6690806116dd565b60070190506000611fa288610a9f8a8760200151610f3390919063ffffffff16565b8351909150611fb18284610fe5565b019c9b505050505050505050505050565b62461bcd60e51b6000908152602060045260076024526642414c23000030600a808404818106603090810160081b95839006959095019082900491820690940160101b939093010160c81b604452606490fd5b62461bcd60e51b600090815260206004526007602452600a808404818106603090810160081b958390069590950190829004918206850160101b01602363ffffff0060e086901c160160181b0190930160c81b60445260e882901c90606490fd5b6000600160801b821061208e5760809190911c906040015b6801000000000000000082106120a95760409190911c906020015b64010000000082106120c05760209190911c906010015b6201000082106120d55760109190911c906008015b61010082106120e95760089190911c906004015b601082106120fc5760049190911c906002015b600482106108c857600101919050565bfe5b604051806040016040528060008152602001600081525090565b60405180606001604052806000815260200160008152602001600081525090565b600082601f830112612159578081fd5b8135602067ffffffffffffffff8083111561217057fe5b8183026040518382820101818110848211171561218957fe5b604052848152838101925086840182880185018910156121a7578687fd5b8692505b858310156121c95780358452928401926001929092019184016121ab565b50979650505050505050565b600061012082840312156121e7578081fd5b60405160e0810181811067ffffffffffffffff8211171561220457fe5b60405290508061221484846122cb565b815261222384604085016122cb565b60208201526080830135604082015260a0830135606082015260c0830135608082015260e083013560a082015261010083013560c08201525092915050565b600060a08284031215612273578081fd5b60405160a0810181811067ffffffffffffffff8211171561229057fe5b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b6000604082840312156122dc578081fd5b6040516040810181811067ffffffffffffffff821117156122f957fe5b604052823581526020928301359281019290925250919050565b60008060006101e08486031215612328578283fd5b833567ffffffffffffffff81111561233e578384fd5b61234a86828701612149565b93505061235a8560208601612262565b91506123698560c086016121d5565b90509250925092565b6000806000806102008587031215612388578081fd5b843567ffffffffffffffff81111561239e578182fd5b6123aa87828801612149565b9450506123ba8660208701612262565b92506123c98660c087016121d5565b939692955092936101e00135925050565b60008060008060008061026087890312156123f3578182fd5b863567ffffffffffffffff811115612409578283fd5b61241589828a01612149565b9650506020870135945060408701358015158114612431578283fd5b93506124408860608901612262565b92506124508861010089016121d5565b91506124608861022089016122cb565b90509295509295509295565b6000806101c0838503121561247f578182fd5b6124898484612262565b91506124988460a085016121d5565b90509250929050565b918252602082015260400190565b9081526020019056fe53616665436173743a2076616c756520646f65736e27742066697420696e20616e20696e74323536a264697066735822122060e9f1202b9aee0c6a5281c816d3a203a9ef7946d166ddb79d6fcea15c8a2d0e64736f6c63430007060033

Deployed Bytecode

0x73f89a1713998593a441cda571780f0900dbef20f9301460806040526004361061006c5760003560e01c806361ff4236146100715780636e70c2ef1461009a57806378ace857146100af578063c7fe78d0146100d0578063d83ca862146100e3578063ec6339ec146100f6575b600080fd5b61008461007f3660046123da565b610109565b60405161009191906124af565b60405180910390f35b6100ad6100a836600461246c565b6101cc565b005b6100c26100bd366004612313565b61038f565b6040516100919291906124a1565b6100846100de366004612372565b610557565b6100846100f1366004612313565b6106a6565b6100846101043660046123da565b6106c9565b600061210c6000808715610128575061078f9150600090506001610135565b506107eb91506001905060005b60006101686101638b8d8660ff168151811061014d57fe5b602002602001015161086090919063ffffffff16565b610881565b905061017788888884876108cd565b6000610189828a8a8a8963ffffffff16565b90506101bc61019782610962565b8d8560ff16815181106101a657fe5b60200260200101516109b990919063ffffffff16565b9c9b505050505050505050505050565b80516000906101db90806109d5565b90506102186f4b3b4ca85a86af4d06c240f509800000821280159061021057506f4b3b4ca85a86d9a70c52038af68000008213155b610166610a0f565b602082015161022790806109d5565b90506102636f4b3b4ca85a86af4d06c240f509800000821280159061021057506f4b3b4ca85a86d9a70c52038af6800000821315610166610a0f565b6102856b012ced32a16a1b11e826288960261b83604001511315610169610a0f565b6102a76b012ced32a16a1b11e826288960261b83606001511315610169610a0f565b6102c96b012ced32a16a1b11e826288960261b83608001511315610169610a0f565b6102eb6b012ced32a16a1b11e826288960261b8360a001511315610169610a0f565b60c082015161032f906f4b3b4ca85a86af4d06c240f50980000013801590610327575060c08301516f4b3b4ca85a86d9a70c52038af680000012155b61016c610a0f565b60006103666b012ced32a16a1b11e826288960261b61034e8686610a21565b6b012ced32a16a1b11e826288960261b919003610aff565b90506103897172cb5bd86321e38cb6ce6682e8000000000082131561016a610a0f565b50505050565b6000806000806103b2876000815181106103a557fe5b6020026020010151610881565b6103c2886001815181106103a557fe5b90925090506103ee6e01ed09bead87c0378d8e64000000006103e48484610b34565b131561016b610a0f565b60006103fc83838989610b5d565b905060008061040d85858b8b610c96565b9150915060008213156104315761042a6001820160028402610d1c565b905061045c565b6000811361044357633b9aca00610459565b61045961016361045283610962565b6005610d9d565b90505b806b012ced32a16a1b11e826288960261b6104868688018c60800151610f3390919063ffffffff16565b8161048d57fe5b0501600101601402905060006104b66b012ced32a16a1b11e826288960261b61034e8c8c610a21565b905060006104c985850184900383610f85565b90506104d58383610fe5565b60808c01519093506b012ced32a16a1b11e826288960261b906ec097ce7bc90715b34b9f10000000009080020561050c8385610fe5565b026028028161051757fe5b05830160010192506105466f1691ca32818ed48b02dca3e0000000006103e48584610b3490919063ffffffff16565b9b919a509098505050505050505050565b60008060405180604001604052808481526020018481525090506000604051806040016040528061058988888661102c565b81526020016105998888866110e7565b81525090506000604051806040016040528083600001516105c08b6000815181106103a557fe5b03815260200183602001516105db8b6001815181106103a557fe5b03905290506105ea878261118e565b9050600060405180604001604052806106148460200151856000015161120c90919063ffffffff16565b8152602001670de0b6b3a764000081525090506000610659826106548b6040518060400160405280670de0b6b3a76400008152602001600081525061118e565b611232565b905061069861069361068c846106548d604051806040016040528060008152602001670de0b6b3a764000081525061118e565b839061125f565b610962565b9a9950505050505050505050565b6000806106b485858561038f565b5090506106c081610962565b95945050505050565b600061210c60008087156106e857506107eb91506000905060016106f5565b5061078f91506001905060005b898160ff168151811061070457fe5b6020026020010151891115610720576107206000610165610a0f565b60006107438a8c8460ff168151811061073557fe5b602002602001015103610881565b90506000610757828a8a8a8963ffffffff16565b905061076689898984886108cd565b6101bc8c8560ff168151811061077857fe5b602002602001015161078983610962565b906109b9565b60008060405180604001604052806107a887878761102c565b81526020016107b88787876110e7565b81525090506107e18560800151878760600151886040015187868a602001518b60c001516112b9565b9695505050505050565b60008060405180604001604052806108048787876110e7565b815260200161081487878761102c565b81525090506107e185608001518787604001518860600151878660405180604001604052808c600001516000015160000381526020018c60000151602001518152508b60c001516112b9565b60008282018381101561087857610878600080611521565b90505b92915050565b6000600160ff1b82106108c55760405162461bcd60e51b81526004018080602001828103825260288152602001806124b96028913960400191505060405180910390fd5b50805b919050565b60ff811661091a5760006108e286868661152f565b90506e01ed09bead87c0378d8e640000000083131580156109035750808313155b610914576109146000610165610a0f565b5061095b565b60006109278686866115e5565b90506e01ed09bead87c0378d8e640000000083131580156109485750808313155b610959576109596000610165610a0f565b505b5050505050565b6000808212156108c5576040805162461bcd60e51b815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f736974697665604482015290519081900360640190fd5b6000828211156109cf576109cf60006001611521565b50900390565b6000610a086109f58360200151856020015161168a90919063ffffffff16565b83518551610a029161168a565b90610b34565b9392505050565b81610a1d57610a1d816116ca565b5050565b60c08101516000908190610a3f90610a3981806116dd565b906116dd565b9050610a77610a6c82610a66866060015187604001516002026116dd90919063ffffffff16565b90610aff565b608086015190610f33565b60808501516040850151919350610aa591610a9f90829082908690610a6690600101806116dd565b90610f33565b6060840151920191610abd908290610a6690806116dd565b8201915060008360a00151610ae386608001518660800151610d1c90919063ffffffff16565b019050610af482610a6683806116dd565b909201949350505050565b600081610b1257610b1260006004611521565b816b012ced32a16a1b11e826288960261b840281610b2c57fe5b059392505050565b600082820181831215610b4957838112610b4e565b838112155b61087857610878600080611521565b60c08101516000908190610b7190806116dd565b90506000610ba882610a6687608001518760a00151610ba18a608001518a6080015161120c90919063ffffffff16565b019061120c565b9050610bdc81610bc58760600151896116f790919063ffffffff16565b6040880151610bd5908b906116f7565b0390610f85565b92506000610c058660400151610bff88608001518a6116f790919063ffffffff16565b906116f7565b610c248760600151610bff89608001518c6116f790919063ffffffff16565b019050610c48610c41848760400151610aff90919063ffffffff16565b8290610f85565b6040870151940193610c5b9088906116f7565b6060870151610c6b908a906116f7565b019050610c88610c41848760600151610aff90919063ffffffff16565b909301979650505050505050565b600080610ca58686868661170a565b610cb187878787611837565b019150610cc086868686611969565b909101906b012ced32a16a1b11e826288960261b610cde8680610f33565b610ce88880610f33565b0181610cf057fe5b05905060008213610d02576000610d11565b610d1161016361045284610962565b915094509492505050565b600081610d2f57610d2f60006004611521565b82610d3c5750600061087b565b6000821215610d52578160000391508260000392505b6000831315610d7c57816001670de0b6b3a764000085020381610d7157fe5b05600101905061087b565b600182670de0b6b3a7640000850260010181610d9457fe5b05039392505050565b600082610dac5750600061087b565b6000610db784611a7f565b9050600281670de0b6b3a7640000860281610dce57fe5b04820181610dd857fe5b049050600281670de0b6b3a7640000860281610df057fe5b04820181610dfa57fe5b049050600281670de0b6b3a7640000860281610e1257fe5b04820181610e1c57fe5b049050600281670de0b6b3a7640000860281610e3457fe5b04820181610e3e57fe5b049050600281670de0b6b3a7640000860281610e5657fe5b04820181610e6057fe5b049050600281670de0b6b3a7640000860281610e7857fe5b04820181610e8257fe5b049050600281670de0b6b3a7640000860281610e9a57fe5b04820181610ea457fe5b0490506000610eb38280611c51565b9050610ec9610ec28386611c8d565b8690610860565b8111158015610eeb5750610ee7610ee08386611c8d565b86906109b9565b8110155b610f2b576040805162461bcd60e51b815260206004820152600c60248201526b17dcdc5c9d0811905253115160a21b604482015290519081900360640190fd5b509392505050565b600082820281811315610f5a57670de0b6b3a764000060001982010560010191505061087b565b6000811215610f7b576001670de0b6b3a7640000828201050391505061087b565b5060009392505050565b6000678ac7230489e80000808305908307848202858202848212801590610fad575060008112155b610fca576001678ac7230489e80000808305840182010503610fda565b678ac7230489e800008082058301055b979650505050505050565b6000678ac7230489e8000080830590830784820285820284821380159061100d575060008113155b610fca57678ac7230489e8000060001981830584010105600101610fda565b60c0820151602083015151600091829161104591610aff565b9050600084602001516000015113611089576110848161107e8760400151610bff896080015188602001516116f790919063ffffffff16565b90610fe5565b6110a8565b6040850151608086015184516110a892849261107e92610a9f91610f33565b91506110dc6110cc8560c00151866020015160200151610aff90919063ffffffff16565b6060870151855161107e91610f33565b909101949350505050565b60c082015182515160009182916110fd91610aff565b845151909150600013611139576111348161107e8760600151610bff896080015188602001516000036116f790919063ffffffff16565b611163565b6111638160000361107e8760600151610a9f89608001518860000151610f3390919063ffffffff16565b60c08501518551602001519193506110dc9161117e91610aff565b6040870151855161107e91610f33565b61119661210e565b6111bf83608001516111b9846020015186606001516116f790919063ffffffff16565b9061120c565b6080840151835160408601516111da92916111b991906116f7565b038152602082015160408401516111f0916116f7565b82516060850151611200916116f7565b01602082015292915050565b60008161121f5761121f60006004611521565b81670de0b6b3a7640000840281610b2c57fe5b6000610a0861125283602001518560200151611ce190919063ffffffff16565b83518551610a0291611ce1565b6000816112725761127260006004611521565b8261127f5750600061087b565b670de0b6b3a76400008381029084828161129557fe5b05146112a7576112a760006005611521565b8281816112b057fe5b05949350505050565b60006112c361210e565b6112e08a6111b96b012ced32a16a1b11e826288960261b8261120c565b6b012ced32a16a1b11e826288960261b908103825261130b908b906113059082610d1c565b90610d1c565b6b012ced32a16a1b11e826288960261b036020820152611329612128565b85518a03600081131561136557602083015161135b906113499087610aff565b61107e8b610bff60008690038f6116f7565b6020830152611390565b825161138a906113759087610aff565b60010161107e8b610a9f60008690038f610f33565b60208301525b61139861210e565b6113b786610a668d610bff8f89602001516116f790919063ffffffff16565b815283516113c5908c610f33565b602082018190526113df906001880190610a66908e610f33565b60010160208083019182526040805180820190915292516b012ced32a16a1b11e826288960261b90810384529151909103908201526114238c8a8f8e8e8c8c611d1a565b6000036040840152602080820151908a015161144a919061144490806116f7565b90610f85565b6040840180519190910190819052600012611466576000611479565b6114796101636104528560400151610962565b604084018190526020840151600091900313156114d4576114cd6114b982602001516b012ced32a16a1b11e826288960261b610aff90919063ffffffff16565b604085015160208601510390600101610fe5565b8352611508565b8051611505906114f4906b012ced32a16a1b11e826288960261b90610aff565b604085015160208601510390610fe5565b83525b5050602086015190510191505098975050505050505050565b81610a1d57610a1d81611fc2565b60c0820151825151602084015151600092839261154d920390610aff565b9050600061157a8560c0015186600001516020015187602001516020015103610aff90919063ffffffff16565b90506115a3826114448860400151610bff8a6080015189602001516116f790919063ffffffff16565b92506115d981600083136115c657606088015186516115c191610f33565b611444565b60608801516020870151611444916116f7565b90920195945050505050565b60c08201518251516020840151516000928392611603920390610aff565b905060006116308560c0015186602001516020015187600001516020015103610aff90919063ffffffff16565b9050611659826114448860600151610bff8a6080015189602001516116f790919063ffffffff16565b92506115d9816000831361167757604088015186516115c191610f33565b60408801516020870151611444916116f7565b60008282028315806116a45750828482816116a157fe5b05145b6116b4576116b460006003611521565b6b012ced32a16a1b11e826288960261b816112b0565b6116da816223aca960e91b612015565b50565b60006b012ced32a16a1b11e826288960261b838302610b2c565b6000670de0b6b3a7640000838302610b2c565b6000806117448460600151610bff8660400151600202611733898a610f3390919063ffffffff16565b61173d8b806116f7565b03906116f7565b9050600061175586600289026116f7565b606086015190915061176b90610bff83826116f7565b604086015161177e90610bff84826116f7565b830103915060006117b086608001516111b988608001516111b989606001518a608001516116dd90919063ffffffff16565b604086015160a08701516117c3916116dd565b0190506117fc86608001516117e987606001518860a001516116dd90919063ffffffff16565b60408801516080890151610ba1916116dd565b60c086015191019061181f9061181890610a39818181806116dd565b8290610aff565b905061182b8382610f85565b98975050505050505050565b6060820151600090819061185190610a9f81818980610f33565b604085015161186690610a9f81818b80610f33565b01905061188f8460600151610bff8660400151600202610bff898b6116f790919063ffffffff16565b6080850151606085015191909203916000916118b491906111b99082908290806116dd565b6118da86608001516111b9876060015188604001516002026116dd90919063ffffffff16565b60408601516118e990806116dd565b60c0870151910191909101915061190a9061181890610a39818181806116dd565b905061191a600083900382610f85565b925061195c6119458560c001516b012ced32a16a1b11e826288960261b610aff90919063ffffffff16565b6080870151611444906111b960081987018261120c565b9092019695505050505050565b6040820151600090819061198390610a9f81818980610f33565b606085015161199890610a9f81818b80610f33565b0190506119c18460400151610a9f8660600151600202610a9f898b610f3390919063ffffffff16565b6080858101519085015192909101916000916119e5916111b99082908290806116dd565b60a08501516119f490806116dd565b019050611a1d85608001516111b986608001518760a001516002026116dd90919063ffffffff16565b60c0850151910190611a399061181890610a39818181806116dd565b9050611a49600083900382610f85565b925061195c611a748560c001516b012ced32a16a1b11e826288960261b610aff90919063ffffffff16565b600819840190610f85565b6000670de0b6b3a76400008210611aac57670de0b6b3a7640000611aa4818404612076565b1b90506108c8565b600a8211611abf575063bc7c871c6108c8565b60648211611ad357506402540be4006108c8565b6103e88211611ae8575064075cdd47196108c8565b6127108211611afd575064174876e8006108c8565b620186a08211611b1357506449a0a4c7006108c8565b620f42408211611b29575064e8d4a510006108c8565b629896808211611b4057506502e0466fc6086108c8565b6305f5e1008211611b5857506509184e72a0006108c8565b633b9aca008211611b705750651cc2c05dbc536108c8565b6402540be4008211611b895750655af3107a40006108c8565b64174876e8008211611ba3575066011f9b83a95b456108c8565b64e8d4a510008211611bbd575066038d7ea4c680006108c8565b6509184e72a0008211611bd85750660b3c13249d90bb6108c8565b655af3107a40008211611bf35750662386f26fc100006108c8565b66038d7ea4c680008211611c0f5750667058bf6e27a7516108c8565b662386f26fc100008211611c2c575067016345785d8a00006108c8565b67016345785d8a00008211611c4a5750670463777a4d8c892d6108c8565b50806108c8565b6000828202831580611c6b575082848281611c6857fe5b04145b611c7b57611c7b60006003611521565b670de0b6b3a764000090049392505050565b6000828202831580611ca7575082848281611ca457fe5b04145b611cb757611cb760006003611521565b80611cc657600091505061087b565b670de0b6b3a764000060001982010460010191505092915050565b6000828202831580611cfb575082848281611cf857fe5b05145b611d0b57611d0b60006003611521565b670de0b6b3a7640000816112b0565b6000806040518060400160405280611d3b85866116dd90919063ffffffff16565b81528951602090910190611d4f9080610f33565b90529050611d5b612128565b815160208601518651600092611d76929091610a66916116dd565b90506000811315611db0576020830151611d939060028a02610f33565b808352611da990600783019061107e908a610f33565b8252611dde565b60208a0151611dc89060028a0290610bff90806116f7565b808352611ddb90829061107e908a6116f7565b82525b855160001315611e26578551611e1c90611df89087610aff565b60000360030161107e89600202610a9f8f8f60000151610f3390919063ffffffff16565b6020830152611e5d565b8551611e5790611e369087610aff565b61107e89600202610bff8f8f602001516000036116f790919063ffffffff16565b60208301525b6020808301518351018352835190870151611e7d9190610a6690806116dd565b6007019050611e99888460200151610f3390919063ffffffff16565b60208301819052611eb090829061107e908b610f33565b602080840191909152860151611eeb90611eca9087610aff565b61107e8a600202610bff8f8f602001516000036116f790919063ffffffff16565b6040830152611efa8b80610f33565b60408301516020840180519091019190910190819052600012611f2b576020820151611f26908a61120c565b611f3a565b6020820151611f3a908a610d1c565b60208301819052825101808352600012611f5f578151611f5a908a61120c565b611f6b565b8151611f6b908a610d1c565b825282518651611f809190610a6690806116dd565b60070190506000611fa288610a9f8a8760200151610f3390919063ffffffff16565b8351909150611fb18284610fe5565b019c9b505050505050505050505050565b62461bcd60e51b6000908152602060045260076024526642414c23000030600a808404818106603090810160081b95839006959095019082900491820690940160101b939093010160c81b604452606490fd5b62461bcd60e51b600090815260206004526007602452600a808404818106603090810160081b958390069590950190829004918206850160101b01602363ffffff0060e086901c160160181b0190930160c81b60445260e882901c90606490fd5b6000600160801b821061208e5760809190911c906040015b6801000000000000000082106120a95760409190911c906020015b64010000000082106120c05760209190911c906010015b6201000082106120d55760109190911c906008015b61010082106120e95760089190911c906004015b601082106120fc5760049190911c906002015b600482106108c857600101919050565bfe5b604051806040016040528060008152602001600081525090565b60405180606001604052806000815260200160008152602001600081525090565b600082601f830112612159578081fd5b8135602067ffffffffffffffff8083111561217057fe5b8183026040518382820101818110848211171561218957fe5b604052848152838101925086840182880185018910156121a7578687fd5b8692505b858310156121c95780358452928401926001929092019184016121ab565b50979650505050505050565b600061012082840312156121e7578081fd5b60405160e0810181811067ffffffffffffffff8211171561220457fe5b60405290508061221484846122cb565b815261222384604085016122cb565b60208201526080830135604082015260a0830135606082015260c0830135608082015260e083013560a082015261010083013560c08201525092915050565b600060a08284031215612273578081fd5b60405160a0810181811067ffffffffffffffff8211171561229057fe5b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b6000604082840312156122dc578081fd5b6040516040810181811067ffffffffffffffff821117156122f957fe5b604052823581526020928301359281019290925250919050565b60008060006101e08486031215612328578283fd5b833567ffffffffffffffff81111561233e578384fd5b61234a86828701612149565b93505061235a8560208601612262565b91506123698560c086016121d5565b90509250925092565b6000806000806102008587031215612388578081fd5b843567ffffffffffffffff81111561239e578182fd5b6123aa87828801612149565b9450506123ba8660208701612262565b92506123c98660c087016121d5565b939692955092936101e00135925050565b60008060008060008061026087890312156123f3578182fd5b863567ffffffffffffffff811115612409578283fd5b61241589828a01612149565b9650506020870135945060408701358015158114612431578283fd5b93506124408860608901612262565b92506124508861010089016121d5565b91506124608861022089016122cb565b90509295509295509295565b6000806101c0838503121561247f578182fd5b6124898484612262565b91506124988460a085016121d5565b90509250929050565b918252602082015260400190565b9081526020019056fe53616665436173743a2076616c756520646f65736e27742066697420696e20616e20696e74323536a264697066735822122060e9f1202b9aee0c6a5281c816d3a203a9ef7946d166ddb79d6fcea15c8a2d0e64736f6c63430007060033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.