ETH Price: $2,950.15 (-6.56%)
Gas: 8 Gwei

Contract

0xd3541aD19C9523c268eDe8792310867C57BE39e4
 
Transaction Hash
Method
Block
From
To
Value

There are no matching entries

Please try again later

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To Value
145339372022-04-06 18:38:29820 days ago1649270309  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PrimitiveEngine

Compiler Version
v0.8.6+commit.11564f7e

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion
File 1 of 20 : PrimitiveEngine.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.6;

import "./libraries/Margin.sol";
import "./libraries/ReplicationMath.sol";
import "./libraries/Reserve.sol";
import "./libraries/SafeCast.sol";
import "./libraries/Transfers.sol";
import "./libraries/Units.sol";

import "./interfaces/callback/IPrimitiveCreateCallback.sol";
import "./interfaces/callback/IPrimitiveDepositCallback.sol";
import "./interfaces/callback/IPrimitiveLiquidityCallback.sol";
import "./interfaces/callback/IPrimitiveSwapCallback.sol";

import "./interfaces/IERC20.sol";
import "./interfaces/IPrimitiveEngine.sol";
import "./interfaces/IPrimitiveFactory.sol";

/// @title   Primitive Engine
/// @author  Primitive
/// @notice  Replicating Market Maker
/// @dev     RMM-01
contract PrimitiveEngine is IPrimitiveEngine {
    using ReplicationMath for int128;
    using Units for uint256;
    using SafeCast for uint256;
    using Reserve for mapping(bytes32 => Reserve.Data);
    using Reserve for Reserve.Data;
    using Margin for mapping(address => Margin.Data);
    using Margin for Margin.Data;
    using Transfers for IERC20;

    /// @dev            Parameters of each pool
    /// @param strike   Strike price of pool with stable token decimals
    /// @param sigma    Implied volatility, with 1e4 decimals such that 10000 = 100%
    /// @param maturity Timestamp of pool expiration, in seconds
    /// @param lastTimestamp Timestamp of the pool's last update, in seconds
    /// @param gamma    Multiplied against deltaIn amounts to apply swap fee, gamma = 1 - fee %, scaled up by 1e4
    struct Calibration {
        uint128 strike;
        uint32 sigma;
        uint32 maturity;
        uint32 lastTimestamp;
        uint32 gamma;
    }

    /// @inheritdoc IPrimitiveEngineView
    uint256 public constant override PRECISION = 10**18;
    /// @inheritdoc IPrimitiveEngineView
    uint256 public constant override BUFFER = 120 seconds;
    /// @inheritdoc IPrimitiveEngineView
    uint256 public immutable override MIN_LIQUIDITY;
    /// @inheritdoc IPrimitiveEngineView
    uint256 public immutable override scaleFactorRisky;
    /// @inheritdoc IPrimitiveEngineView
    uint256 public immutable override scaleFactorStable;
    /// @inheritdoc IPrimitiveEngineView
    address public immutable override factory;
    /// @inheritdoc IPrimitiveEngineView
    address public immutable override risky;
    /// @inheritdoc IPrimitiveEngineView
    address public immutable override stable;
    /// @dev Reentrancy guard initialized to state
    uint256 private locked = 1;
    /// @inheritdoc IPrimitiveEngineView
    mapping(bytes32 => Calibration) public override calibrations;
    /// @inheritdoc IPrimitiveEngineView
    mapping(address => Margin.Data) public override margins;
    /// @inheritdoc IPrimitiveEngineView
    mapping(bytes32 => Reserve.Data) public override reserves;
    /// @inheritdoc IPrimitiveEngineView
    mapping(address => mapping(bytes32 => uint256)) public override liquidity;

    modifier lock() {
        if (locked != 1) revert LockedError();

        locked = 2;
        _;
        locked = 1;
    }

    /// @notice Deploys an Engine with two tokens, a 'Risky' and 'Stable'
    constructor() {
        (factory, risky, stable, scaleFactorRisky, scaleFactorStable, MIN_LIQUIDITY) = IPrimitiveFactory(msg.sender)
            .args();
    }

    /// @return Risky token balance of this contract
    function balanceRisky() private view returns (uint256) {
        (bool success, bytes memory data) = risky.staticcall(
            abi.encodeWithSelector(IERC20.balanceOf.selector, address(this))
        );
        if (!success || data.length != 32) revert BalanceError();
        return abi.decode(data, (uint256));
    }

    /// @return Stable token balance of this contract
    function balanceStable() private view returns (uint256) {
        (bool success, bytes memory data) = stable.staticcall(
            abi.encodeWithSelector(IERC20.balanceOf.selector, address(this))
        );
        if (!success || data.length != 32) revert BalanceError();
        return abi.decode(data, (uint256));
    }

    /// @notice Revert if expected amount does not exceed current balance
    function checkRiskyBalance(uint256 expectedRisky) private view {
        uint256 actualRisky = balanceRisky();
        if (actualRisky < expectedRisky) revert RiskyBalanceError(expectedRisky, actualRisky);
    }

    /// @notice Revert if expected amount does not exceed current balance
    function checkStableBalance(uint256 expectedStable) private view {
        uint256 actualStable = balanceStable();
        if (actualStable < expectedStable) revert StableBalanceError(expectedStable, actualStable);
    }

    /// @return blockTimestamp casted as a uint32
    function _blockTimestamp() internal view virtual returns (uint32 blockTimestamp) {
        // solhint-disable-next-line
        blockTimestamp = uint32(block.timestamp);
    }

    /// @inheritdoc IPrimitiveEngineActions
    function updateLastTimestamp(bytes32 poolId) external override lock returns (uint32 lastTimestamp) {
        lastTimestamp = _updateLastTimestamp(poolId);
    }

    /// @notice Sets the lastTimestamp of `poolId` to `block.timestamp`, max value is `maturity`
    /// @return lastTimestamp of the pool, used in calculating the time until expiry
    function _updateLastTimestamp(bytes32 poolId) internal virtual returns (uint32 lastTimestamp) {
        Calibration storage cal = calibrations[poolId];
        if (cal.lastTimestamp == 0) revert UninitializedError();

        lastTimestamp = _blockTimestamp();
        uint32 maturity = cal.maturity;
        if (lastTimestamp > maturity) lastTimestamp = maturity; // if expired, set to the maturity

        cal.lastTimestamp = lastTimestamp; // set state
        emit UpdateLastTimestamp(poolId);
    }

    /// @inheritdoc IPrimitiveEngineActions
    function create(
        uint128 strike,
        uint32 sigma,
        uint32 maturity,
        uint32 gamma,
        uint256 riskyPerLp,
        uint256 delLiquidity,
        bytes calldata data
    )
        external
        override
        lock
        returns (
            bytes32 poolId,
            uint256 delRisky,
            uint256 delStable
        )
    {
        (uint256 factor0, uint256 factor1) = (scaleFactorRisky, scaleFactorStable);
        poolId = keccak256(abi.encodePacked(address(this), strike, sigma, maturity, gamma));
        if (calibrations[poolId].lastTimestamp != 0) revert PoolDuplicateError();
        if (sigma > 1e7 || sigma < 1) revert SigmaError(sigma);
        if (strike == 0) revert StrikeError(strike);
        if (delLiquidity <= MIN_LIQUIDITY) revert MinLiquidityError(delLiquidity);
        if (riskyPerLp > PRECISION / factor0 || riskyPerLp == 0) revert RiskyPerLpError(riskyPerLp);
        if (gamma > Units.PERCENTAGE || gamma < 9000) revert GammaError(gamma);

        Calibration memory cal = Calibration({
            strike: strike,
            sigma: sigma,
            maturity: maturity,
            lastTimestamp: _blockTimestamp(),
            gamma: gamma
        });

        if (cal.lastTimestamp > cal.maturity) revert PoolExpiredError();
        uint32 tau = cal.maturity - cal.lastTimestamp; // time until expiry
        delStable = ReplicationMath.getStableGivenRisky(0, factor0, factor1, riskyPerLp, cal.strike, cal.sigma, tau);
        delRisky = (riskyPerLp * delLiquidity) / PRECISION; // riskyDecimals * 1e18 decimals / 1e18 = riskyDecimals
        delStable = (delStable * delLiquidity) / PRECISION;
        if (delRisky == 0 || delStable == 0) revert CalibrationError(delRisky, delStable);

        calibrations[poolId] = cal; // state update
        uint256 amount = delLiquidity - MIN_LIQUIDITY;
        liquidity[msg.sender][poolId] += amount; // burn min liquidity, at cost of msg.sender
        reserves[poolId].allocate(delRisky, delStable, delLiquidity, cal.lastTimestamp); // state update

        (uint256 balRisky, uint256 balStable) = (balanceRisky(), balanceStable());
        IPrimitiveCreateCallback(msg.sender).createCallback(delRisky, delStable, data);
        checkRiskyBalance(balRisky + delRisky);
        checkStableBalance(balStable + delStable);

        emit Create(msg.sender, cal.strike, cal.sigma, cal.maturity, cal.gamma, delRisky, delStable, amount);
    }

    // ===== Margin =====

    /// @inheritdoc IPrimitiveEngineActions
    function deposit(
        address recipient,
        uint256 delRisky,
        uint256 delStable,
        bytes calldata data
    ) external override lock {
        if (delRisky == 0 && delStable == 0) revert ZeroDeltasError();
        margins[recipient].deposit(delRisky, delStable); // state update

        uint256 balRisky;
        uint256 balStable;
        if (delRisky != 0) balRisky = balanceRisky();
        if (delStable != 0) balStable = balanceStable();
        IPrimitiveDepositCallback(msg.sender).depositCallback(delRisky, delStable, data); // agnostic payment
        if (delRisky != 0) checkRiskyBalance(balRisky + delRisky);
        if (delStable != 0) checkStableBalance(balStable + delStable);
        emit Deposit(msg.sender, recipient, delRisky, delStable);
    }

    /// @inheritdoc IPrimitiveEngineActions
    function withdraw(
        address recipient,
        uint256 delRisky,
        uint256 delStable
    ) external override lock {
        if (delRisky == 0 && delStable == 0) revert ZeroDeltasError();
        margins.withdraw(delRisky, delStable); // state update
        if (delRisky != 0) IERC20(risky).safeTransfer(recipient, delRisky);
        if (delStable != 0) IERC20(stable).safeTransfer(recipient, delStable);
        emit Withdraw(msg.sender, recipient, delRisky, delStable);
    }

    // ===== Liquidity =====

    /// @inheritdoc IPrimitiveEngineActions
    function allocate(
        bytes32 poolId,
        address recipient,
        uint256 delRisky,
        uint256 delStable,
        bool fromMargin,
        bytes calldata data
    ) external override lock returns (uint256 delLiquidity) {
        if (delRisky == 0 || delStable == 0) revert ZeroDeltasError();
        Reserve.Data storage reserve = reserves[poolId];
        if (reserve.blockTimestamp == 0) revert UninitializedError();
        uint32 timestamp = _blockTimestamp();

        uint256 liquidity0 = (delRisky * reserve.liquidity) / uint256(reserve.reserveRisky);
        uint256 liquidity1 = (delStable * reserve.liquidity) / uint256(reserve.reserveStable);
        delLiquidity = liquidity0 < liquidity1 ? liquidity0 : liquidity1;
        if (delLiquidity == 0) revert ZeroLiquidityError();

        liquidity[recipient][poolId] += delLiquidity; // increase position liquidity
        reserve.allocate(delRisky, delStable, delLiquidity, timestamp); // increase reserves and liquidity

        if (fromMargin) {
            margins.withdraw(delRisky, delStable); // removes tokens from `msg.sender` margin account
        } else {
            (uint256 balRisky, uint256 balStable) = (balanceRisky(), balanceStable());
            IPrimitiveLiquidityCallback(msg.sender).allocateCallback(delRisky, delStable, data); // agnostic payment
            checkRiskyBalance(balRisky + delRisky);
            checkStableBalance(balStable + delStable);
        }

        emit Allocate(msg.sender, recipient, poolId, delRisky, delStable, delLiquidity);
    }

    /// @inheritdoc IPrimitiveEngineActions
    function remove(bytes32 poolId, uint256 delLiquidity)
        external
        override
        lock
        returns (uint256 delRisky, uint256 delStable)
    {
        if (delLiquidity == 0) revert ZeroLiquidityError();
        Reserve.Data storage reserve = reserves[poolId];
        if (reserve.blockTimestamp == 0) revert UninitializedError();
        (delRisky, delStable) = reserve.getAmounts(delLiquidity);

        liquidity[msg.sender][poolId] -= delLiquidity; // state update
        reserve.remove(delRisky, delStable, delLiquidity, _blockTimestamp());
        margins[msg.sender].deposit(delRisky, delStable);

        emit Remove(msg.sender, poolId, delRisky, delStable, delLiquidity);
    }

    struct SwapDetails {
        address recipient;
        bool riskyForStable;
        bool fromMargin;
        bool toMargin;
        uint32 timestamp;
        bytes32 poolId;
        uint256 deltaIn;
        uint256 deltaOut;
    }

    /// @inheritdoc IPrimitiveEngineActions
    function swap(
        address recipient,
        bytes32 poolId,
        bool riskyForStable,
        uint256 deltaIn,
        uint256 deltaOut,
        bool fromMargin,
        bool toMargin,
        bytes calldata data
    ) external override lock {
        if (deltaIn == 0) revert DeltaInError();
        if (deltaOut == 0) revert DeltaOutError();

        SwapDetails memory details = SwapDetails({
            recipient: recipient,
            poolId: poolId,
            deltaIn: deltaIn,
            deltaOut: deltaOut,
            riskyForStable: riskyForStable,
            fromMargin: fromMargin,
            toMargin: toMargin,
            timestamp: _blockTimestamp()
        });

        uint32 lastTimestamp = _updateLastTimestamp(details.poolId); // updates lastTimestamp of `poolId`
        if (details.timestamp > lastTimestamp + BUFFER) revert PoolExpiredError(); // 120s buffer to allow final swaps
        int128 invariantX64 = invariantOf(details.poolId); // stored in memory to perform the invariant check

        {
            // swap scope, avoids stack too deep errors
            Calibration memory cal = calibrations[details.poolId];
            Reserve.Data storage reserve = reserves[details.poolId];
            uint32 tau = cal.maturity - cal.lastTimestamp;
            uint256 deltaInWithFee = (details.deltaIn * cal.gamma) / Units.PERCENTAGE; // amount * (1 - fee %)

            uint256 adjustedRisky;
            uint256 adjustedStable;
            if (details.riskyForStable) {
                adjustedRisky = uint256(reserve.reserveRisky) + deltaInWithFee;
                adjustedStable = uint256(reserve.reserveStable) - deltaOut;
            } else {
                adjustedRisky = uint256(reserve.reserveRisky) - deltaOut;
                adjustedStable = uint256(reserve.reserveStable) + deltaInWithFee;
            }
            adjustedRisky = (adjustedRisky * PRECISION) / reserve.liquidity;
            adjustedStable = (adjustedStable * PRECISION) / reserve.liquidity;

            int128 invariantAfter = ReplicationMath.calcInvariant(
                scaleFactorRisky,
                scaleFactorStable,
                adjustedRisky,
                adjustedStable,
                cal.strike,
                cal.sigma,
                tau
            );

            if (invariantX64 > invariantAfter) revert InvariantError(invariantX64, invariantAfter);
            reserve.swap(details.riskyForStable, details.deltaIn, details.deltaOut, details.timestamp); // state update
        }

        if (details.riskyForStable) {
            if (details.toMargin) {
                margins[details.recipient].deposit(0, details.deltaOut);
            } else {
                IERC20(stable).safeTransfer(details.recipient, details.deltaOut); // optimistic transfer out
            }

            if (details.fromMargin) {
                margins.withdraw(details.deltaIn, 0); // pay for swap
            } else {
                uint256 balRisky = balanceRisky();
                IPrimitiveSwapCallback(msg.sender).swapCallback(details.deltaIn, 0, data); // agnostic transfer in
                checkRiskyBalance(balRisky + details.deltaIn);
            }
        } else {
            if (details.toMargin) {
                margins[details.recipient].deposit(details.deltaOut, 0);
            } else {
                IERC20(risky).safeTransfer(details.recipient, details.deltaOut); // optimistic transfer out
            }

            if (details.fromMargin) {
                margins.withdraw(0, details.deltaIn); // pay for swap
            } else {
                uint256 balStable = balanceStable();
                IPrimitiveSwapCallback(msg.sender).swapCallback(0, details.deltaIn, data); // agnostic transfer in
                checkStableBalance(balStable + details.deltaIn);
            }
        }

        emit Swap(
            msg.sender,
            details.recipient,
            details.poolId,
            details.riskyForStable,
            details.deltaIn,
            details.deltaOut
        );
    }

    // ===== View =====

    /// @inheritdoc IPrimitiveEngineView
    function invariantOf(bytes32 poolId) public view override returns (int128 invariant) {
        Calibration memory cal = calibrations[poolId];
        uint32 tau = cal.maturity - cal.lastTimestamp; // cal maturity can never be less than lastTimestamp
        (uint256 riskyPerLiquidity, uint256 stablePerLiquidity) = reserves[poolId].getAmounts(PRECISION); // 1e18 liquidity
        invariant = ReplicationMath.calcInvariant(
            scaleFactorRisky,
            scaleFactorStable,
            riskyPerLiquidity,
            stablePerLiquidity,
            cal.strike,
            cal.sigma,
            tau
        );
    }
}

File 2 of 20 : Margin.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;

import "./SafeCast.sol";

/// @title   Margin Library
/// @author  Primitive
/// @dev     Uses a data struct with two uint128s to optimize for one storage slot
library Margin {
    using SafeCast for uint256;

    struct Data {
        uint128 balanceRisky; // Balance of the risky token, aka underlying asset
        uint128 balanceStable; // Balance of the stable token, aka "quote" asset
    }

    /// @notice             Adds to risky and stable token balances
    /// @param  margin      Margin data of an account in storage to manipulate
    /// @param  delRisky    Amount of risky tokens to add to margin
    /// @param  delStable   Amount of stable tokens to add to margin
    function deposit(
        Data storage margin,
        uint256 delRisky,
        uint256 delStable
    ) internal {
        if (delRisky != 0) margin.balanceRisky += delRisky.toUint128();
        if (delStable != 0) margin.balanceStable += delStable.toUint128();
    }

    /// @notice             Removes risky and stable token balance from `msg.sender`'s internal margin account
    /// @param  margins     Margin data mapping, uses `msg.sender`'s margin account
    /// @param  delRisky    Amount of risky tokens to subtract from margin
    /// @param  delStable   Amount of stable tokens to subtract from margin
    /// @return margin      Data storage of a margin account
    function withdraw(
        mapping(address => Data) storage margins,
        uint256 delRisky,
        uint256 delStable
    ) internal returns (Data storage margin) {
        margin = margins[msg.sender];
        if (delRisky != 0) margin.balanceRisky -= delRisky.toUint128();
        if (delStable != 0) margin.balanceStable -= delStable.toUint128();
    }
}

File 3 of 20 : ReplicationMath.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.4;

import "./ABDKMath64x64.sol";
import "./CumulativeNormalDistribution.sol";
import "./Units.sol";

/// @title   Replication Math
/// @author  Primitive
/// @notice  Alex Evans, Guillermo Angeris, and Tarun Chitra. Replicating Market Makers.
///          https://stanford.edu/~guillean/papers/rmms.pdf
library ReplicationMath {
    using ABDKMath64x64 for int128;
    using ABDKMath64x64 for uint256;
    using CumulativeNormalDistribution for int128;
    using Units for int128;
    using Units for uint256;

    int128 internal constant ONE_INT = 0x10000000000000000;

    /// @notice         Normalizes volatility with respect to square root of time until expiry
    /// @param   sigma  Unsigned 256-bit percentage as an integer with precision of 1e4, 10000 = 100%
    /// @param   tau    Time until expiry in seconds as an unsigned 256-bit integer
    /// @return  vol    Signed fixed point 64.64 number equal to sigma * sqrt(tau)
    function getProportionalVolatility(uint256 sigma, uint256 tau) internal pure returns (int128 vol) {
        int128 sqrtTauX64 = tau.toYears().sqrt();
        int128 sigmaX64 = sigma.percentageToX64();
        vol = sigmaX64.mul(sqrtTauX64);
    }

    /// @notice                 Uses riskyPerLiquidity and invariant to calculate stablePerLiquidity
    /// @dev                    Converts unsigned 256-bit values to fixed point 64.64 numbers w/ decimals of precision
    /// @param   invariantLastX64   Signed 64.64 fixed point number. Calculated w/ same `tau` as the parameter `tau`
    /// @param   scaleFactorRisky   Unsigned 256-bit integer scaling factor for `risky`, 10^(18 - risky.decimals())
    /// @param   scaleFactorStable  Unsigned 256-bit integer scaling factor for `stable`, 10^(18 - stable.decimals())
    /// @param   riskyPerLiquidity  Unsigned 256-bit integer of Pool's risky reserves *per liquidity*, 0 <= x <= 1
    /// @param   strike         Unsigned 256-bit integer value with precision equal to 10^(18 - scaleFactorStable)
    /// @param   sigma          Volatility of the Pool as an unsigned 256-bit integer w/ precision of 1e4, 10000 = 100%
    /// @param   tau            Time until expiry in seconds as an unsigned 256-bit integer
    /// @return  stablePerLiquidity = K*CDF(CDF^-1(1 - riskyPerLiquidity) - sigma*sqrt(tau)) + invariantLastX64 as uint
    function getStableGivenRisky(
        int128 invariantLastX64,
        uint256 scaleFactorRisky,
        uint256 scaleFactorStable,
        uint256 riskyPerLiquidity,
        uint256 strike,
        uint256 sigma,
        uint256 tau
    ) internal pure returns (uint256 stablePerLiquidity) {
        int128 strikeX64 = strike.scaleToX64(scaleFactorStable);
        int128 riskyX64 = riskyPerLiquidity.scaleToX64(scaleFactorRisky); // mul by 2^64, div by precision
        int128 oneMinusRiskyX64 = ONE_INT.sub(riskyX64);
        if (tau != 0) {
            int128 volX64 = getProportionalVolatility(sigma, tau);
            int128 phi = oneMinusRiskyX64.getInverseCDF();
            int128 input = phi.sub(volX64);
            int128 stableX64 = strikeX64.mul(input.getCDF()).add(invariantLastX64);
            stablePerLiquidity = stableX64.scaleFromX64(scaleFactorStable);
        } else {
            stablePerLiquidity = (strikeX64.mul(oneMinusRiskyX64).add(invariantLastX64)).scaleFromX64(
                scaleFactorStable
            );
        }
    }

    /// @notice                 Calculates the invariant of a curve
    /// @dev                    Per unit of replication, aka per unit of liquidity
    /// @param   scaleFactorRisky   Unsigned 256-bit integer scaling factor for `risky`, 10^(18 - risky.decimals())
    /// @param   scaleFactorStable  Unsigned 256-bit integer scaling factor for `stable`, 10^(18 - stable.decimals())
    /// @param   riskyPerLiquidity  Unsigned 256-bit integer of Pool's risky reserves *per liquidity*, 0 <= x <= 1
    /// @param   stablePerLiquidity Unsigned 256-bit integer of Pool's stable reserves *per liquidity*, 0 <= x <= strike
    /// @return  invariantX64       = stablePerLiquidity - K * CDF(CDF^-1(1 - riskyPerLiquidity) - sigma * sqrt(tau))
    function calcInvariant(
        uint256 scaleFactorRisky,
        uint256 scaleFactorStable,
        uint256 riskyPerLiquidity,
        uint256 stablePerLiquidity,
        uint256 strike,
        uint256 sigma,
        uint256 tau
    ) internal pure returns (int128 invariantX64) {
        uint256 output = getStableGivenRisky(
            0,
            scaleFactorRisky,
            scaleFactorStable,
            riskyPerLiquidity,
            strike,
            sigma,
            tau
        );
        int128 outputX64 = output.scaleToX64(scaleFactorStable);
        int128 stableX64 = stablePerLiquidity.scaleToX64(scaleFactorStable);
        invariantX64 = stableX64.sub(outputX64);
    }
}

File 4 of 20 : Reserve.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;

import "./SafeCast.sol";

/// @title   Reserves Library
/// @author  Primitive
/// @dev     Data structure library for an Engine's Reserves
library Reserve {
    using SafeCast for uint256;

    /// @notice                Stores global state of a pool
    /// @param reserveRisky    Risky token reserve
    /// @param reserveStable   Stable token reserve
    /// @param liquidity       Total supply of liquidity
    /// @param blockTimestamp  Last timestamp of which updated the accumulators
    /// @param cumulativeRisky Cumulative sum of the risky reserves
    /// @param cumulativeStable Cumulative sum of stable reserves
    /// @param cumulativeLiquidity Cumulative sum of total liquidity supply
    struct Data {
        uint128 reserveRisky;
        uint128 reserveStable;
        uint128 liquidity;
        uint32 blockTimestamp;
        uint256 cumulativeRisky;
        uint256 cumulativeStable;
        uint256 cumulativeLiquidity;
    }

    /// @notice                 Adds to the cumulative reserves
    /// @dev                    Overflow is desired on the cumulative values
    /// @param  res             Reserve storage to update
    /// @param  blockTimestamp  Checkpoint timestamp of update
    function update(Data storage res, uint32 blockTimestamp) internal {
        uint32 deltaTime = blockTimestamp - res.blockTimestamp;
        // overflow is desired
        if (deltaTime != 0) {
            unchecked {
                res.cumulativeRisky += uint256(res.reserveRisky) * deltaTime;
                res.cumulativeStable += uint256(res.reserveStable) * deltaTime;
                res.cumulativeLiquidity += uint256(res.liquidity) * deltaTime;
            }
            res.blockTimestamp = blockTimestamp;
        }
    }

    /// @notice                 Increases one reserve value and decreases the other
    /// @param  reserve         Reserve state to update
    /// @param  riskyForStable  Direction of swap
    /// @param  deltaIn         Amount of tokens paid, increases one reserve by
    /// @param  deltaOut        Amount of tokens sent out, decreases the other reserve by
    /// @param  blockTimestamp  Timestamp used to update cumulative reserves
    function swap(
        Data storage reserve,
        bool riskyForStable,
        uint256 deltaIn,
        uint256 deltaOut,
        uint32 blockTimestamp
    ) internal {
        update(reserve, blockTimestamp);
        if (riskyForStable) {
            reserve.reserveRisky += deltaIn.toUint128();
            reserve.reserveStable -= deltaOut.toUint128();
        } else {
            reserve.reserveRisky -= deltaOut.toUint128();
            reserve.reserveStable += deltaIn.toUint128();
        }
    }

    /// @notice                 Add to both reserves and total supply of liquidity
    /// @param  reserve         Reserve storage to manipulate
    /// @param  delRisky        Amount of risky tokens to add to the reserve
    /// @param  delStable       Amount of stable tokens to add to the reserve
    /// @param  delLiquidity    Amount of liquidity created with the provided tokens
    /// @param  blockTimestamp  Timestamp used to update cumulative reserves
    function allocate(
        Data storage reserve,
        uint256 delRisky,
        uint256 delStable,
        uint256 delLiquidity,
        uint32 blockTimestamp
    ) internal {
        update(reserve, blockTimestamp);
        reserve.reserveRisky += delRisky.toUint128();
        reserve.reserveStable += delStable.toUint128();
        reserve.liquidity += delLiquidity.toUint128();
    }

    /// @notice                 Remove from both reserves and total supply of liquidity
    /// @param  reserve         Reserve storage to manipulate
    /// @param  delRisky        Amount of risky tokens to remove to the reserve
    /// @param  delStable       Amount of stable tokens to remove to the reserve
    /// @param  delLiquidity    Amount of liquidity removed from total supply
    /// @param  blockTimestamp  Timestamp used to update cumulative reserves
    function remove(
        Data storage reserve,
        uint256 delRisky,
        uint256 delStable,
        uint256 delLiquidity,
        uint32 blockTimestamp
    ) internal {
        update(reserve, blockTimestamp);
        reserve.reserveRisky -= delRisky.toUint128();
        reserve.reserveStable -= delStable.toUint128();
        reserve.liquidity -= delLiquidity.toUint128();
    }

    /// @notice                 Calculates risky and stable token amounts of `delLiquidity`
    /// @param reserve          Reserve in memory to use reserves and liquidity of
    /// @param delLiquidity     Amount of liquidity to fetch underlying tokens of
    /// @return delRisky        Amount of risky tokens controlled by `delLiquidity`
    /// @return delStable       Amount of stable tokens controlled by `delLiquidity`
    function getAmounts(Data memory reserve, uint256 delLiquidity)
        internal
        pure
        returns (uint256 delRisky, uint256 delStable)
    {
        uint256 liq = uint256(reserve.liquidity);
        delRisky = (delLiquidity * uint256(reserve.reserveRisky)) / liq;
        delStable = (delLiquidity * uint256(reserve.reserveStable)) / liq;
    }
}

File 5 of 20 : SafeCast.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.0;

/// @title  SafeCast
/// @notice Safely cast between uint256 and uint128
library SafeCast {
    /// @notice reverts if x > type(uint128).max
    function toUint128(uint256 x) internal pure returns (uint128 z) {
        require(x <= type(uint128).max);
        z = uint128(x);
    }
}

File 6 of 20 : Transfers.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.6.0;

import "../interfaces/IERC20.sol";

/// @title  Transfers
library Transfers {
    /// @notice         Performs an ERC20 `transfer` call and checks return data
    /// @param  token   ERC20 token to transfer
    /// @param  to      Recipient of the ERC20 token
    /// @param  value   Amount of ERC20 to transfer
    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        (bool success, bytes memory returnData) = address(token).call(
            abi.encodeWithSelector(token.transfer.selector, to, value)
        );
        require(success && (returnData.length == 0 || abi.decode(returnData, (bool))), "Transfer fail");
    }
}

File 7 of 20 : Units.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.0;

import "./ABDKMath64x64.sol";

/// @title   Units library
/// @author  Primitive
/// @notice  Utility functions for unit conversions
library Units {
    using ABDKMath64x64 for int128;
    using ABDKMath64x64 for uint256;

    uint256 internal constant YEAR = 31556952; // 365.24219 ephemeris day = 1 year, in seconds
    uint256 internal constant PRECISION = 1e18; // precision to scale to
    uint256 internal constant PERCENTAGE = 1e4; // precision of percentages

    // ===== Unit Conversion =====

    /// @notice             Scales a wei value to a precision of 1e18 using the scaling factor
    /// @param   value      Unsigned 256-bit wei amount to convert with native decimals
    /// @param   factor     Scaling factor to multiply by, i.e. 10^(18 - value.decimals())
    /// @return  y          Unsigned 256-bit wei amount scaled to a precision of 1e18
    function scaleUp(uint256 value, uint256 factor) internal pure returns (uint256 y) {
        y = value * factor;
    }

    /// @notice             Scales a wei value from a precision of 1e18 to 10^(18 - precision)
    /// @param   value      Unsigned 256-bit wei amount with 18 decimals
    /// @param   factor     Scaling factor to divide by, i.e. 10^(18 - value.decimals())
    /// @return  y          Unsigned 256-bit wei amount scaled to 10^(18 - factor)
    function scaleDown(uint256 value, uint256 factor) internal pure returns (uint256 y) {
        y = value / factor;
    }

    /// @notice             Converts unsigned 256-bit wei value into a fixed point 64.64 number
    /// @param   value      Unsigned 256-bit wei amount, in native precision
    /// @param   factor     Scaling factor for `value`, used to calculate decimals of `value`
    /// @return  y          Signed 64.64 fixed point number scaled from native precision
    function scaleToX64(uint256 value, uint256 factor) internal pure returns (int128 y) {
        uint256 scaleFactor = PRECISION / factor;
        y = value.divu(scaleFactor);
    }

    /// @notice             Converts signed fixed point 64.64 number into unsigned 256-bit wei value
    /// @param   value      Signed fixed point 64.64 number to convert from precision of 10^18
    /// @param   factor     Scaling factor for `value`, used to calculate decimals of `value`
    /// @return  y          Unsigned 256-bit wei amount scaled to native precision of 10^(18 - factor)
    function scaleFromX64(int128 value, uint256 factor) internal pure returns (uint256 y) {
        uint256 scaleFactor = PRECISION / factor;
        y = value.mulu(scaleFactor);
    }

    /// @notice         Converts denormalized percentage integer to a fixed point 64.64 number
    /// @dev            Convert unsigned 256-bit integer number into signed 64.64 fixed point number
    /// @param denorm   Unsigned percentage integer with precision of 1e4
    /// @return         Signed 64.64 fixed point percentage with precision of 1e4
    function percentageToX64(uint256 denorm) internal pure returns (int128) {
        return denorm.divu(PERCENTAGE);
    }

    /// @notice         Converts unsigned seconds integer into years as a signed 64.64 fixed point number
    /// @dev            Convert unsigned 256-bit integer number into signed 64.64 fixed point number
    /// @param s        Unsigned 256-bit integer amount of seconds to convert into year units
    /// @return         Fixed point 64.64 number of years equal to `seconds`
    function toYears(uint256 s) internal pure returns (int128) {
        return s.divu(YEAR);
    }
}

File 8 of 20 : IPrimitiveCreateCallback.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.0;

/// @title  Primitive Create Callback
/// @author Primitive
interface IPrimitiveCreateCallback {
    /// @notice              Triggered when creating a new pool for an Engine
    /// @param  delRisky     Amount of risky tokens required to initialize risky reserve
    /// @param  delStable    Amount of stable tokens required to initialize stable reserve
    /// @param  data         Calldata passed on create function call
    function createCallback(
        uint256 delRisky,
        uint256 delStable,
        bytes calldata data
    ) external;
}

File 9 of 20 : IPrimitiveDepositCallback.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.0;

/// @title  Primitive Deposit Callback
/// @author Primitive
interface IPrimitiveDepositCallback {
    /// @notice              Triggered when depositing tokens to an Engine
    /// @param  delRisky     Amount of risky tokens required to deposit to risky margin balance
    /// @param  delStable    Amount of stable tokens required to deposit to stable margin balance
    /// @param  data         Calldata passed on deposit function call
    function depositCallback(
        uint256 delRisky,
        uint256 delStable,
        bytes calldata data
    ) external;
}

File 10 of 20 : IPrimitiveLiquidityCallback.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.0;

/// @title  Primitive Liquidity Callback
/// @author Primitive
interface IPrimitiveLiquidityCallback {
    /// @notice              Triggered when providing liquidity to an Engine
    /// @param  delRisky     Amount of risky tokens required to provide to risky reserve
    /// @param  delStable    Amount of stable tokens required to provide to stable reserve
    /// @param  data         Calldata passed on allocate function call
    function allocateCallback(
        uint256 delRisky,
        uint256 delStable,
        bytes calldata data
    ) external;
}

File 11 of 20 : IPrimitiveSwapCallback.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.0;

/// @title  Primitive Swap Callback
/// @author Primitive
interface IPrimitiveSwapCallback {
    /// @notice              Triggered when swapping tokens in an Engine
    /// @param  delRisky     Amount of risky tokens required to pay the swap with
    /// @param  delStable    Amount of stable tokens required to pay the swap with
    /// @param  data         Calldata passed on swap function call
    function swapCallback(
        uint256 delRisky,
        uint256 delStable,
        bytes calldata data
    ) external;
}

File 12 of 20 : IERC20.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.0;

interface IERC20 {
    function totalSupply() external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);

    function transfer(address recipient, uint256 amount) external returns (bool);

    function allowance(address owner, address spender) external view returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

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

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

    function decimals() external view returns (uint8);
}

File 13 of 20 : IPrimitiveEngine.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.4;

import "./engine/IPrimitiveEngineActions.sol";
import "./engine/IPrimitiveEngineEvents.sol";
import "./engine/IPrimitiveEngineView.sol";
import "./engine/IPrimitiveEngineErrors.sol";

/// @title Primitive Engine Interface
interface IPrimitiveEngine is
    IPrimitiveEngineActions,
    IPrimitiveEngineEvents,
    IPrimitiveEngineView,
    IPrimitiveEngineErrors
{

}

File 14 of 20 : IPrimitiveFactory.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.4;

/// @title   Primitive Factory Interface
/// @author  Primitive
interface IPrimitiveFactory {
    /// @notice         Created a new engine contract!
    /// @param  from    Calling `msg.sender` of deploy
    /// @param  risky   Risky token of Engine to deploy
    /// @param  stable  Stable token of Engine to deploy
    /// @param  engine  Deployed engine address
    event DeployEngine(address indexed from, address indexed risky, address indexed stable, address engine);

    /// @notice         Deploys a new Engine contract and sets the `getEngine` mapping for the tokens
    /// @param  risky   Risky token, the underlying token
    /// @param  stable  Stable token, the quote token
    function deploy(address risky, address stable) external returns (address engine);

    // ===== View =====

    /// @notice         Used to scale the minimum amount of liquidity to lowest precision
    /// @dev            E.g. if the lowest decimal token is 6, min liquidity w/ 18 decimals
    ///                 cannot be 1000 wei, therefore the token decimals
    ///                 divided by the min liquidity factor is the amount of minimum liquidity
    ///                 MIN_LIQUIDITY = 10 ^ (Decimals / MIN_LIQUIDITY_FACTOR)
    function MIN_LIQUIDITY_FACTOR() external pure returns (uint256);

    /// @notice                    Called within Engine constructor so Engine can set immutable
    ///                            variables without constructor args
    /// @return factory            Smart contract deploying the Engine contract
    /// @return risky              Risky token
    /// @return stable             Stable token
    /// @return scaleFactorRisky   Scale factor of the risky token, 10^(18 - riskyTokenDecimals)
    /// @return scaleFactorStable  Scale factor of the stable token, 10^(18 - stableTokenDecimals)
    /// @return minLiquidity       Minimum amount of liquidity on pool creation
    function args()
        external
        view
        returns (
            address factory,
            address risky,
            address stable,
            uint256 scaleFactorRisky,
            uint256 scaleFactorStable,
            uint256 minLiquidity
        );

    /// @notice         Fetches engine address of a token pair which has been deployed from this factory
    /// @param risky    Risky token, the underlying token
    /// @param stable   Stable token, the quote token
    /// @return engine  Engine address for a risky and stable token
    function getEngine(address risky, address stable) external view returns (address engine);

    /// @notice         Deployer does not have any access controls to wield
    /// @return         Deployer of this factory contract
    function deployer() external view returns (address);
}

File 15 of 20 : ABDKMath64x64.sol
// SPDX-License-Identifier: BSD-4-Clause
/*
 * ABDK Math 64.64 Smart Contract Library.  Copyright © 2019 by ABDK Consulting.
 * Author: Mikhail Vladimirov <[email protected]>
 */
pragma solidity ^0.8.0;

/**
 * Smart contract library of mathematical functions operating with signed
 * 64.64-bit fixed point numbers.  Signed 64.64-bit fixed point number is
 * basically a simple fraction whose numerator is signed 128-bit integer and
 * denominator is 2^64.  As long as denominator is always the same, there is no
 * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are
 * represented by int128 type holding only the numerator.
 */
library ABDKMath64x64 {
    /*
     * Minimum value signed 64.64-bit fixed point number may have.
     */
    int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;

    /*
     * Maximum value signed 64.64-bit fixed point number may have.
     */
    int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

    /**
     * Convert signed 256-bit integer number into signed 64.64-bit fixed point
     * number.  Revert on overflow.
     *
     * @param x signed 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function fromInt(int256 x) internal pure returns (int128) {
        unchecked {
            require(x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF);
            return int128(x << 64);
        }
    }

    /**
     * Convert signed 64.64 fixed point number into signed 64-bit integer number
     * rounding down.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64-bit integer number
     */
    function toInt(int128 x) internal pure returns (int64) {
        unchecked {
            return int64(x >> 64);
        }
    }

    /**
     * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point
     * number.  Revert on overflow.
     *
     * @param x unsigned 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function fromUInt(uint256 x) internal pure returns (int128) {
        unchecked {
            require(x <= 0x7FFFFFFFFFFFFFFF);
            return int128(int256(x << 64));
        }
    }

    /**
     * Convert signed 64.64 fixed point number into unsigned 64-bit integer
     * number rounding down.  Revert on underflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return unsigned 64-bit integer number
     */
    function toUInt(int128 x) internal pure returns (uint64) {
        unchecked {
            require(x >= 0);
            return uint64(uint128(x >> 64));
        }
    }

    /**
     * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point
     * number rounding down.  Revert on overflow.
     *
     * @param x signed 128.128-bin fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function from128x128(int256 x) internal pure returns (int128) {
        unchecked {
            int256 result = x >> 64;
            require(result >= MIN_64x64 && result <= MAX_64x64);
            return int128(result);
        }
    }

    /**
     * Convert signed 64.64 fixed point number into signed 128.128 fixed point
     * number.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 128.128 fixed point number
     */
    function to128x128(int128 x) internal pure returns (int256) {
        unchecked {
            return int256(x) << 64;
        }
    }

    /**
     * Calculate x + y.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function add(int128 x, int128 y) internal pure returns (int128) {
        unchecked {
            int256 result = int256(x) + y;
            require(result >= MIN_64x64 && result <= MAX_64x64);
            return int128(result);
        }
    }

    /**
     * Calculate x - y.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function sub(int128 x, int128 y) internal pure returns (int128) {
        unchecked {
            int256 result = int256(x) - y;
            require(result >= MIN_64x64 && result <= MAX_64x64);
            return int128(result);
        }
    }

    /**
     * Calculate x * y rounding down.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function mul(int128 x, int128 y) internal pure returns (int128) {
        unchecked {
            int256 result = (int256(x) * y) >> 64;
            require(result >= MIN_64x64 && result <= MAX_64x64);
            return int128(result);
        }
    }

    /**
     * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point
     * number and y is signed 256-bit integer number.  Revert on overflow.
     *
     * @param x signed 64.64 fixed point number
     * @param y signed 256-bit integer number
     * @return signed 256-bit integer number
     */
    function muli(int128 x, int256 y) internal pure returns (int256) {
        unchecked {
            if (x == MIN_64x64) {
                require(
                    y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF &&
                        y <= 0x1000000000000000000000000000000000000000000000000
                );
                return -y << 63;
            } else {
                bool negativeResult = false;
                if (x < 0) {
                    x = -x;
                    negativeResult = true;
                }
                if (y < 0) {
                    y = -y; // We rely on overflow behavior here
                    negativeResult = !negativeResult;
                }
                uint256 absoluteResult = mulu(x, uint256(y));
                if (negativeResult) {
                    require(absoluteResult <= 0x8000000000000000000000000000000000000000000000000000000000000000);
                    return -int256(absoluteResult); // We rely on overflow behavior here
                } else {
                    require(absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
                    return int256(absoluteResult);
                }
            }
        }
    }

    /**
     * Calculate x * y rounding down, where x is signed 64.64 fixed point number
     * and y is unsigned 256-bit integer number.  Revert on overflow.
     *
     * @param x signed 64.64 fixed point number
     * @param y unsigned 256-bit integer number
     * @return unsigned 256-bit integer number
     */
    function mulu(int128 x, uint256 y) internal pure returns (uint256) {
        unchecked {
            if (y == 0) return 0;

            require(x >= 0);

            uint256 lo = (uint256(int256(x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64;
            uint256 hi = uint256(int256(x)) * (y >> 128);

            require(hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
            hi <<= 64;

            require(hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo);
            return hi + lo;
        }
    }

    /**
     * Calculate x / y rounding towards zero.  Revert on overflow or when y is
     * zero.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function div(int128 x, int128 y) internal pure returns (int128) {
        unchecked {
            require(y != 0);
            int256 result = (int256(x) << 64) / y;
            require(result >= MIN_64x64 && result <= MAX_64x64);
            return int128(result);
        }
    }

    /**
     * Calculate x / y rounding towards zero, where x and y are signed 256-bit
     * integer numbers.  Revert on overflow or when y is zero.
     *
     * @param x signed 256-bit integer number
     * @param y signed 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function divi(int256 x, int256 y) internal pure returns (int128) {
        unchecked {
            require(y != 0);

            bool negativeResult = false;
            if (x < 0) {
                x = -x; // We rely on overflow behavior here
                negativeResult = true;
            }
            if (y < 0) {
                y = -y; // We rely on overflow behavior here
                negativeResult = !negativeResult;
            }
            uint128 absoluteResult = divuu(uint256(x), uint256(y));
            if (negativeResult) {
                require(absoluteResult <= 0x80000000000000000000000000000000);
                return -int128(absoluteResult); // We rely on overflow behavior here
            } else {
                require(absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
                return int128(absoluteResult); // We rely on overflow behavior here
            }
        }
    }

    /**
     * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
     * integer numbers.  Revert on overflow or when y is zero.
     *
     * @param x unsigned 256-bit integer number
     * @param y unsigned 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function divu(uint256 x, uint256 y) internal pure returns (int128) {
        unchecked {
            require(y != 0);
            uint128 result = divuu(x, y);
            require(result <= uint128(MAX_64x64));
            return int128(result);
        }
    }

    /**
     * Calculate -x.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function neg(int128 x) internal pure returns (int128) {
        unchecked {
            require(x != MIN_64x64);
            return -x;
        }
    }

    /**
     * Calculate |x|.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function abs(int128 x) internal pure returns (int128) {
        unchecked {
            require(x != MIN_64x64);
            return x < 0 ? -x : x;
        }
    }

    /**
     * Calculate 1 / x rounding towards zero.  Revert on overflow or when x is
     * zero.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function inv(int128 x) internal pure returns (int128) {
        unchecked {
            require(x != 0);
            int256 result = int256(0x100000000000000000000000000000000) / x;
            require(result >= MIN_64x64 && result <= MAX_64x64);
            return int128(result);
        }
    }

    /**
     * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function avg(int128 x, int128 y) internal pure returns (int128) {
        unchecked {
            return int128((int256(x) + int256(y)) >> 1);
        }
    }

    /**
     * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down.
     * Revert on overflow or in case x * y is negative.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function gavg(int128 x, int128 y) internal pure returns (int128) {
        unchecked {
            int256 m = int256(x) * int256(y);
            require(m >= 0);
            require(m < 0x4000000000000000000000000000000000000000000000000000000000000000);
            return int128(sqrtu(uint256(m)));
        }
    }

    /**
     * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number
     * and y is unsigned 256-bit integer number.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y uint256 value
     * @return signed 64.64-bit fixed point number
     */
    function pow(int128 x, uint256 y) internal pure returns (int128) {
        unchecked {
            bool negative = x < 0 && y & 1 == 1;

            uint256 absX = uint128(x < 0 ? -x : x);
            uint256 absResult;
            absResult = 0x100000000000000000000000000000000;

            if (absX <= 0x10000000000000000) {
                absX <<= 63;
                while (y != 0) {
                    if (y & 0x1 != 0) {
                        absResult = (absResult * absX) >> 127;
                    }
                    absX = (absX * absX) >> 127;

                    if (y & 0x2 != 0) {
                        absResult = (absResult * absX) >> 127;
                    }
                    absX = (absX * absX) >> 127;

                    if (y & 0x4 != 0) {
                        absResult = (absResult * absX) >> 127;
                    }
                    absX = (absX * absX) >> 127;

                    if (y & 0x8 != 0) {
                        absResult = (absResult * absX) >> 127;
                    }
                    absX = (absX * absX) >> 127;

                    y >>= 4;
                }

                absResult >>= 64;
            } else {
                uint256 absXShift = 63;
                if (absX < 0x1000000000000000000000000) {
                    absX <<= 32;
                    absXShift -= 32;
                }
                if (absX < 0x10000000000000000000000000000) {
                    absX <<= 16;
                    absXShift -= 16;
                }
                if (absX < 0x1000000000000000000000000000000) {
                    absX <<= 8;
                    absXShift -= 8;
                }
                if (absX < 0x10000000000000000000000000000000) {
                    absX <<= 4;
                    absXShift -= 4;
                }
                if (absX < 0x40000000000000000000000000000000) {
                    absX <<= 2;
                    absXShift -= 2;
                }
                if (absX < 0x80000000000000000000000000000000) {
                    absX <<= 1;
                    absXShift -= 1;
                }

                uint256 resultShift = 0;
                while (y != 0) {
                    require(absXShift < 64);

                    if (y & 0x1 != 0) {
                        absResult = (absResult * absX) >> 127;
                        resultShift += absXShift;
                        if (absResult > 0x100000000000000000000000000000000) {
                            absResult >>= 1;
                            resultShift += 1;
                        }
                    }
                    absX = (absX * absX) >> 127;
                    absXShift <<= 1;
                    if (absX >= 0x100000000000000000000000000000000) {
                        absX >>= 1;
                        absXShift += 1;
                    }

                    y >>= 1;
                }

                require(resultShift < 64);
                absResult >>= 64 - resultShift;
            }
            int256 result = negative ? -int256(absResult) : int256(absResult);
            require(result >= MIN_64x64 && result <= MAX_64x64);
            return int128(result);
        }
    }

    /**
     * Calculate sqrt (x) rounding down.  Revert if x < 0.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function sqrt(int128 x) internal pure returns (int128) {
        unchecked {
            require(x >= 0);
            return int128(sqrtu(uint256(int256(x)) << 64));
        }
    }

    /**
     * Calculate binary logarithm of x.  Revert if x <= 0.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function log_2(int128 x) internal pure returns (int128) {
        unchecked {
            require(x > 0);

            int256 msb = 0;
            int256 xc = x;
            if (xc >= 0x10000000000000000) {
                xc >>= 64;
                msb += 64;
            }
            if (xc >= 0x100000000) {
                xc >>= 32;
                msb += 32;
            }
            if (xc >= 0x10000) {
                xc >>= 16;
                msb += 16;
            }
            if (xc >= 0x100) {
                xc >>= 8;
                msb += 8;
            }
            if (xc >= 0x10) {
                xc >>= 4;
                msb += 4;
            }
            if (xc >= 0x4) {
                xc >>= 2;
                msb += 2;
            }
            if (xc >= 0x2) msb += 1; // No need to shift xc anymore

            int256 result = (msb - 64) << 64;
            uint256 ux = uint256(int256(x)) << uint256(127 - msb);
            for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) {
                ux *= ux;
                uint256 b = ux >> 255;
                ux >>= 127 + b;
                result += bit * int256(b);
            }

            return int128(result);
        }
    }

    /**
     * Calculate natural logarithm of x.  Revert if x <= 0.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function ln(int128 x) internal pure returns (int128) {
        unchecked {
            require(x > 0);

            return int128(int256((uint256(int256(log_2(x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF) >> 128));
        }
    }

    /**
     * Calculate binary exponent of x.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function exp_2(int128 x) internal pure returns (int128) {
        unchecked {
            require(x < 0x400000000000000000); // Overflow

            if (x < -0x400000000000000000) return 0; // Underflow

            uint256 result = 0x80000000000000000000000000000000;

            if (x & 0x8000000000000000 > 0) result = (result * 0x16A09E667F3BCC908B2FB1366EA957D3E) >> 128;
            if (x & 0x4000000000000000 > 0) result = (result * 0x1306FE0A31B7152DE8D5A46305C85EDEC) >> 128;
            if (x & 0x2000000000000000 > 0) result = (result * 0x1172B83C7D517ADCDF7C8C50EB14A791F) >> 128;
            if (x & 0x1000000000000000 > 0) result = (result * 0x10B5586CF9890F6298B92B71842A98363) >> 128;
            if (x & 0x800000000000000 > 0) result = (result * 0x1059B0D31585743AE7C548EB68CA417FD) >> 128;
            if (x & 0x400000000000000 > 0) result = (result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8) >> 128;
            if (x & 0x200000000000000 > 0) result = (result * 0x10163DA9FB33356D84A66AE336DCDFA3F) >> 128;
            if (x & 0x100000000000000 > 0) result = (result * 0x100B1AFA5ABCBED6129AB13EC11DC9543) >> 128;
            if (x & 0x80000000000000 > 0) result = (result * 0x10058C86DA1C09EA1FF19D294CF2F679B) >> 128;
            if (x & 0x40000000000000 > 0) result = (result * 0x1002C605E2E8CEC506D21BFC89A23A00F) >> 128;
            if (x & 0x20000000000000 > 0) result = (result * 0x100162F3904051FA128BCA9C55C31E5DF) >> 128;
            if (x & 0x10000000000000 > 0) result = (result * 0x1000B175EFFDC76BA38E31671CA939725) >> 128;
            if (x & 0x8000000000000 > 0) result = (result * 0x100058BA01FB9F96D6CACD4B180917C3D) >> 128;
            if (x & 0x4000000000000 > 0) result = (result * 0x10002C5CC37DA9491D0985C348C68E7B3) >> 128;
            if (x & 0x2000000000000 > 0) result = (result * 0x1000162E525EE054754457D5995292026) >> 128;
            if (x & 0x1000000000000 > 0) result = (result * 0x10000B17255775C040618BF4A4ADE83FC) >> 128;
            if (x & 0x800000000000 > 0) result = (result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB) >> 128;
            if (x & 0x400000000000 > 0) result = (result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9) >> 128;
            if (x & 0x200000000000 > 0) result = (result * 0x10000162E43F4F831060E02D839A9D16D) >> 128;
            if (x & 0x100000000000 > 0) result = (result * 0x100000B1721BCFC99D9F890EA06911763) >> 128;
            if (x & 0x80000000000 > 0) result = (result * 0x10000058B90CF1E6D97F9CA14DBCC1628) >> 128;
            if (x & 0x40000000000 > 0) result = (result * 0x1000002C5C863B73F016468F6BAC5CA2B) >> 128;
            if (x & 0x20000000000 > 0) result = (result * 0x100000162E430E5A18F6119E3C02282A5) >> 128;
            if (x & 0x10000000000 > 0) result = (result * 0x1000000B1721835514B86E6D96EFD1BFE) >> 128;
            if (x & 0x8000000000 > 0) result = (result * 0x100000058B90C0B48C6BE5DF846C5B2EF) >> 128;
            if (x & 0x4000000000 > 0) result = (result * 0x10000002C5C8601CC6B9E94213C72737A) >> 128;
            if (x & 0x2000000000 > 0) result = (result * 0x1000000162E42FFF037DF38AA2B219F06) >> 128;
            if (x & 0x1000000000 > 0) result = (result * 0x10000000B17217FBA9C739AA5819F44F9) >> 128;
            if (x & 0x800000000 > 0) result = (result * 0x1000000058B90BFCDEE5ACD3C1CEDC823) >> 128;
            if (x & 0x400000000 > 0) result = (result * 0x100000002C5C85FE31F35A6A30DA1BE50) >> 128;
            if (x & 0x200000000 > 0) result = (result * 0x10000000162E42FF0999CE3541B9FFFCF) >> 128;
            if (x & 0x100000000 > 0) result = (result * 0x100000000B17217F80F4EF5AADDA45554) >> 128;
            if (x & 0x80000000 > 0) result = (result * 0x10000000058B90BFBF8479BD5A81B51AD) >> 128;
            if (x & 0x40000000 > 0) result = (result * 0x1000000002C5C85FDF84BD62AE30A74CC) >> 128;
            if (x & 0x20000000 > 0) result = (result * 0x100000000162E42FEFB2FED257559BDAA) >> 128;
            if (x & 0x10000000 > 0) result = (result * 0x1000000000B17217F7D5A7716BBA4A9AE) >> 128;
            if (x & 0x8000000 > 0) result = (result * 0x100000000058B90BFBE9DDBAC5E109CCE) >> 128;
            if (x & 0x4000000 > 0) result = (result * 0x10000000002C5C85FDF4B15DE6F17EB0D) >> 128;
            if (x & 0x2000000 > 0) result = (result * 0x1000000000162E42FEFA494F1478FDE05) >> 128;
            if (x & 0x1000000 > 0) result = (result * 0x10000000000B17217F7D20CF927C8E94C) >> 128;
            if (x & 0x800000 > 0) result = (result * 0x1000000000058B90BFBE8F71CB4E4B33D) >> 128;
            if (x & 0x400000 > 0) result = (result * 0x100000000002C5C85FDF477B662B26945) >> 128;
            if (x & 0x200000 > 0) result = (result * 0x10000000000162E42FEFA3AE53369388C) >> 128;
            if (x & 0x100000 > 0) result = (result * 0x100000000000B17217F7D1D351A389D40) >> 128;
            if (x & 0x80000 > 0) result = (result * 0x10000000000058B90BFBE8E8B2D3D4EDE) >> 128;
            if (x & 0x40000 > 0) result = (result * 0x1000000000002C5C85FDF4741BEA6E77E) >> 128;
            if (x & 0x20000 > 0) result = (result * 0x100000000000162E42FEFA39FE95583C2) >> 128;
            if (x & 0x10000 > 0) result = (result * 0x1000000000000B17217F7D1CFB72B45E1) >> 128;
            if (x & 0x8000 > 0) result = (result * 0x100000000000058B90BFBE8E7CC35C3F0) >> 128;
            if (x & 0x4000 > 0) result = (result * 0x10000000000002C5C85FDF473E242EA38) >> 128;
            if (x & 0x2000 > 0) result = (result * 0x1000000000000162E42FEFA39F02B772C) >> 128;
            if (x & 0x1000 > 0) result = (result * 0x10000000000000B17217F7D1CF7D83C1A) >> 128;
            if (x & 0x800 > 0) result = (result * 0x1000000000000058B90BFBE8E7BDCBE2E) >> 128;
            if (x & 0x400 > 0) result = (result * 0x100000000000002C5C85FDF473DEA871F) >> 128;
            if (x & 0x200 > 0) result = (result * 0x10000000000000162E42FEFA39EF44D91) >> 128;
            if (x & 0x100 > 0) result = (result * 0x100000000000000B17217F7D1CF79E949) >> 128;
            if (x & 0x80 > 0) result = (result * 0x10000000000000058B90BFBE8E7BCE544) >> 128;
            if (x & 0x40 > 0) result = (result * 0x1000000000000002C5C85FDF473DE6ECA) >> 128;
            if (x & 0x20 > 0) result = (result * 0x100000000000000162E42FEFA39EF366F) >> 128;
            if (x & 0x10 > 0) result = (result * 0x1000000000000000B17217F7D1CF79AFA) >> 128;
            if (x & 0x8 > 0) result = (result * 0x100000000000000058B90BFBE8E7BCD6D) >> 128;
            if (x & 0x4 > 0) result = (result * 0x10000000000000002C5C85FDF473DE6B2) >> 128;
            if (x & 0x2 > 0) result = (result * 0x1000000000000000162E42FEFA39EF358) >> 128;
            if (x & 0x1 > 0) result = (result * 0x10000000000000000B17217F7D1CF79AB) >> 128;

            result >>= uint256(int256(63 - (x >> 64)));
            require(result <= uint256(int256(MAX_64x64)));

            return int128(int256(result));
        }
    }

    /**
     * Calculate natural exponent of x.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function exp(int128 x) internal pure returns (int128) {
        unchecked {
            require(x < 0x400000000000000000); // Overflow

            if (x < -0x400000000000000000) return 0; // Underflow

            return exp_2(int128((int256(x) * 0x171547652B82FE1777D0FFDA0D23A7D12) >> 128));
        }
    }

    /**
     * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
     * integer numbers.  Revert on overflow or when y is zero.
     *
     * @param x unsigned 256-bit integer number
     * @param y unsigned 256-bit integer number
     * @return unsigned 64.64-bit fixed point number
     */
    function divuu(uint256 x, uint256 y) private pure returns (uint128) {
        unchecked {
            require(y != 0);

            uint256 result;

            if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) result = (x << 64) / y;
            else {
                uint256 msb = 192;
                uint256 xc = x >> 192;
                if (xc >= 0x100000000) {
                    xc >>= 32;
                    msb += 32;
                }
                if (xc >= 0x10000) {
                    xc >>= 16;
                    msb += 16;
                }
                if (xc >= 0x100) {
                    xc >>= 8;
                    msb += 8;
                }
                if (xc >= 0x10) {
                    xc >>= 4;
                    msb += 4;
                }
                if (xc >= 0x4) {
                    xc >>= 2;
                    msb += 2;
                }
                if (xc >= 0x2) msb += 1; // No need to shift xc anymore

                result = (x << (255 - msb)) / (((y - 1) >> (msb - 191)) + 1);
                require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

                uint256 hi = result * (y >> 128);
                uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

                uint256 xh = x >> 192;
                uint256 xl = x << 64;

                if (xl < lo) xh -= 1;
                xl -= lo; // We rely on overflow behavior here
                lo = hi << 128;
                if (xl < lo) xh -= 1;
                xl -= lo; // We rely on overflow behavior here

                assert(xh == hi >> 128);

                result += xl / y;
            }

            require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
            return uint128(result);
        }
    }

    /**
     * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer
     * number.
     *
     * @param x unsigned 256-bit integer number
     * @return unsigned 128-bit integer number
     */
    function sqrtu(uint256 x) private pure returns (uint128) {
        unchecked {
            if (x == 0) return 0;
            else {
                uint256 xx = x;
                uint256 r = 1;
                if (xx >= 0x100000000000000000000000000000000) {
                    xx >>= 128;
                    r <<= 64;
                }
                if (xx >= 0x10000000000000000) {
                    xx >>= 64;
                    r <<= 32;
                }
                if (xx >= 0x100000000) {
                    xx >>= 32;
                    r <<= 16;
                }
                if (xx >= 0x10000) {
                    xx >>= 16;
                    r <<= 8;
                }
                if (xx >= 0x100) {
                    xx >>= 8;
                    r <<= 4;
                }
                if (xx >= 0x10) {
                    xx >>= 4;
                    r <<= 2;
                }
                if (xx >= 0x8) {
                    r <<= 1;
                }
                r = (r + x / r) >> 1;
                r = (r + x / r) >> 1;
                r = (r + x / r) >> 1;
                r = (r + x / r) >> 1;
                r = (r + x / r) >> 1;
                r = (r + x / r) >> 1;
                r = (r + x / r) >> 1; // Seven iterations should be enough
                uint256 r1 = x / r;
                return uint128(r < r1 ? r : r1);
            }
        }
    }
}

File 16 of 20 : CumulativeNormalDistribution.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.4;

import "./ABDKMath64x64.sol";

/// @title   Cumulative Normal Distribution Math Library
/// @author  Primitive
library CumulativeNormalDistribution {
    using ABDKMath64x64 for int128;
    using ABDKMath64x64 for uint256;

    /// @notice Thrown on passing an arg that is out of the input range for these math functions
    error InverseOutOfBounds(int128 value);

    int128 public constant ONE_INT = 0x10000000000000000;
    int128 public constant TWO_INT = 0x20000000000000000;
    int128 public constant CDF0 = 0x53dd02a4f5ee2e46;
    int128 public constant CDF1 = 0x413c831bb169f874;
    int128 public constant CDF2 = -0x48d4c730f051a5fe;
    int128 public constant CDF3 = 0x16a09e667f3bcc908;
    int128 public constant CDF4 = -0x17401c57014c38f14;
    int128 public constant CDF5 = 0x10fb844255a12d72e;

    /// @notice Uses Abramowitz and Stegun approximation:
    ///         https://en.wikipedia.org/wiki/Abramowitz_and_Stegun
    /// @dev    Maximum error: 3.15x10-3
    /// @return Standard Normal Cumulative Distribution Function of `x`
    function getCDF(int128 x) internal pure returns (int128) {
        int128 z = x.div(CDF3);
        int128 t = ONE_INT.div(ONE_INT.add(CDF0.mul(z.abs())));
        int128 erf = getErrorFunction(z, t);
        if (z < 0) {
            erf = erf.neg();
        }
        int128 result = (HALF_INT).mul(ONE_INT.add(erf));
        return result;
    }

    /// @notice Uses Abramowitz and Stegun approximation:
    ///         https://en.wikipedia.org/wiki/Error_function
    /// @dev    Maximum error: 1.5×10−7
    /// @return Error Function for approximating the Standard Normal CDF
    function getErrorFunction(int128 z, int128 t) internal pure returns (int128) {
        int128 step1 = t.mul(CDF3.add(t.mul(CDF4.add(t.mul(CDF5)))));
        int128 step2 = CDF1.add(t.mul(CDF2.add(step1)));
        int128 result = ONE_INT.sub(t.mul(step2.mul((z.mul(z).neg()).exp())));
        return result;
    }

    int128 public constant HALF_INT = 0x8000000000000000;
    int128 public constant INVERSE0 = 0x26A8F3C1F21B336E;
    int128 public constant INVERSE1 = -0x87C57E5DA70D3C90;
    int128 public constant INVERSE2 = 0x15D71F5721242C787;
    int128 public constant INVERSE3 = 0x21D0A04B0E9B94F1;
    int128 public constant INVERSE4 = -0xC2BF5D74C724E53F;

    int128 public constant LOW_TAIL = 0x666666666666666; // 0.025
    int128 public constant HIGH_TAIL = 0xF999999999999999; // 0.975

    /// @notice  Returns the inverse CDF, or quantile function of `p`.
    /// @dev     Source: https://arxiv.org/pdf/1002.0567.pdf
    ///          Maximum error of central region is 1.16x10−4
    /// @return  fcentral(p) = q * (a2 + (a1r + a0) / (r^2 + b1r +b0))
    function getInverseCDF(int128 p) internal pure returns (int128) {
        if (p >= ONE_INT || p <= 0) revert InverseOutOfBounds(p);
        // Short circuit for the central region, central region inclusive of tails
        if (p <= HIGH_TAIL && p >= LOW_TAIL) {
            return central(p);
        } else if (p < LOW_TAIL) {
            return tail(p);
        } else {
            int128 negativeTail = -tail(ONE_INT.sub(p));
            return negativeTail;
        }
    }

    /// @dev    Maximum error: 1.16x10−4
    /// @return Inverse CDF around the central area of 0.025 <= p <= 0.975
    function central(int128 p) internal pure returns (int128) {
        int128 q = p.sub(HALF_INT);
        int128 r = q.mul(q);
        int128 result = q.mul(
            INVERSE2.add((INVERSE1.mul(r).add(INVERSE0)).div((r.mul(r).add(INVERSE4.mul(r)).add(INVERSE3))))
        );
        return result;
    }

    int128 public constant C0 = 0x10E56D75CE8BCE9FAE;
    int128 public constant C1 = -0x2CB2447D36D513DAE;
    int128 public constant C2 = -0x8BB4226952BD69EDF;
    int128 public constant C3 = -0x1000BF627FA188411;
    int128 public constant C0_D = 0x10AEAC93F55267A9A5;
    int128 public constant C1_D = 0x41ED34A2561490236;
    int128 public constant C2_D = 0x7A1E70F720ECA43;
    int128 public constant D0 = 0x72C7D592D021FB1DB;
    int128 public constant D1 = 0x8C27B4617F5F800EA;

    /// @dev    Maximum error: 2.458x10-5
    /// @return Inverse CDF of the tail, defined for p < 0.0465, used with p < 0.025
    function tail(int128 p) internal pure returns (int128) {
        int128 r = ONE_INT.div(p.mul(p)).ln().sqrt();
        int128 step0 = C3.mul(r).add(C2_D);
        int128 numerator = C1_D.mul(r).add(C0_D);
        int128 denominator = r.mul(r).add(D1.mul(r)).add(D0);
        int128 result = step0.add(numerator.div(denominator));
        return result;
    }
}

File 17 of 20 : IPrimitiveEngineActions.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.0;

/// @title  Action functions for the Primitive Engine contract
/// @author Primitive
interface IPrimitiveEngineActions {
    // ===== Pool Updates =====

    /// @notice             Updates the time until expiry of the pool by setting its last timestamp value
    /// @param  poolId      Keccak256 hash of engine address, strike, sigma, maturity, and gamma
    /// @return lastTimestamp Timestamp loaded into the state of the pool's Calibration.lastTimestamp
    function updateLastTimestamp(bytes32 poolId) external returns (uint32 lastTimestamp);

    /// @notice             Initializes a curve with parameters in the `calibrations` storage mapping in the Engine
    /// @param  strike      Marginal price of the pool's risky token at maturity, with the same decimals as the stable token, valid [0, 2^128-1]
    /// @param  sigma       AKA Implied Volatility in basis points, determines the price impact of swaps, valid for (1, 10_000_000)
    /// @param  maturity    Timestamp which starts the BUFFER countdown until swaps will cease, in seconds, valid for (block.timestamp, 2^32-1]
    /// @param  gamma       Multiplied against swap in amounts to apply fee, equal to 1 - fee % but units are in basis points, valid for (9_000, 10_000)
    /// @param  riskyPerLp  Risky reserve per liq. with risky decimals, = 1 - N(d1), d1 = (ln(S/K)+(r*σ^2/2))/σ√τ, valid for [0, 1e^(risky token decimals))
    /// @param  delLiquidity Amount of liquidity units to allocate to the curve, wei value with 18 decimals of precision
    /// @param  data        Arbitrary data that is passed to the createCallback function
    /// @return poolId      Keccak256 hash of engine address, strike, sigma, maturity, and gamma
    /// @return delRisky    Total amount of risky tokens provided to reserves
    /// @return delStable   Total amount of stable tokens provided to reserves
    function create(
        uint128 strike,
        uint32 sigma,
        uint32 maturity,
        uint32 gamma,
        uint256 riskyPerLp,
        uint256 delLiquidity,
        bytes calldata data
    )
        external
        returns (
            bytes32 poolId,
            uint256 delRisky,
            uint256 delStable
        );

    // ===== Margin ====

    /// @notice             Adds risky and/or stable tokens to a `recipient`'s internal balance account
    /// @param  recipient   Recipient margin account of the deposited tokens
    /// @param  delRisky    Amount of risky tokens to deposit
    /// @param  delStable   Amount of stable tokens to deposit
    /// @param  data        Arbitrary data that is passed to the depositCallback function
    function deposit(
        address recipient,
        uint256 delRisky,
        uint256 delStable,
        bytes calldata data
    ) external;

    /// @notice             Removes risky and/or stable tokens from a `msg.sender`'s internal balance account
    /// @param  recipient   Address that tokens are transferred to
    /// @param  delRisky    Amount of risky tokens to withdraw
    /// @param  delStable   Amount of stable tokens to withdraw
    function withdraw(
        address recipient,
        uint256 delRisky,
        uint256 delStable
    ) external;

    // ===== Liquidity =====

    /// @notice             Allocates risky and stable tokens to a specific curve with `poolId`
    /// @param  poolId      Keccak256 hash of engine address, strike, sigma, maturity, and gamma
    /// @param  recipient   Address to give the allocated liquidity to
    /// @param  delRisky    Amount of risky tokens to add
    /// @param  delStable   Amount of stable tokens to add
    /// @param  fromMargin  Whether the `msg.sender` pays with their margin balance, or must send tokens
    /// @param  data        Arbitrary data that is passed to the allocateCallback function
    /// @return delLiquidity Amount of liquidity given to `recipient`
    function allocate(
        bytes32 poolId,
        address recipient,
        uint256 delRisky,
        uint256 delStable,
        bool fromMargin,
        bytes calldata data
    ) external returns (uint256 delLiquidity);

    /// @notice               Unallocates risky and stable tokens from a specific curve with `poolId`
    /// @param  poolId        Keccak256 hash of engine address, strike, sigma, maturity, and gamma
    /// @param  delLiquidity  Amount of liquidity to remove
    /// @return delRisky      Amount of risky tokens received from removed liquidity
    /// @return delStable     Amount of stable tokens received from removed liquidity
    function remove(bytes32 poolId, uint256 delLiquidity) external returns (uint256 delRisky, uint256 delStable);

    // ===== Swaps =====

    /// @notice             Swaps between `risky` and `stable` tokens
    /// @param  recipient   Address that receives output token `deltaOut` amount
    /// @param  poolId      Keccak256 hash of engine address, strike, sigma, maturity, and gamma
    /// @param  riskyForStable If true, swap risky to stable, else swap stable to risky
    /// @param  deltaIn     Amount of tokens to swap in
    /// @param  deltaOut    Amount of tokens to swap out
    /// @param  fromMargin  Whether the `msg.sender` uses their margin balance, or must send tokens
    /// @param  toMargin    Whether the `deltaOut` amount is transferred or deposited into margin
    /// @param  data        Arbitrary data that is passed to the swapCallback function
    function swap(
        address recipient,
        bytes32 poolId,
        bool riskyForStable,
        uint256 deltaIn,
        uint256 deltaOut,
        bool fromMargin,
        bool toMargin,
        bytes calldata data
    ) external;
}

File 18 of 20 : IPrimitiveEngineEvents.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.0;

/// @title  Events of the Primitive Engine contract
/// @author Primitive
interface IPrimitiveEngineEvents {
    /// @notice             Creates a pool with liquidity
    /// @dev                Keccak256 hash of the engine address, strike, sigma, maturity, and gamma
    /// @param  from        Calling `msg.sender` of the create function
    /// @param  strike      Marginal price of the pool's risky token at maturity, with the same decimals as the stable token, valid [0, 2^128-1]
    /// @param  sigma       AKA Implied Volatility in basis points, determines the price impact of swaps, valid for (1, 10_000_000)
    /// @param  maturity    Timestamp which starts the BUFFER countdown until swaps will cease, in seconds, valid for (block.timestamp, 2^32-1]
    /// @param  gamma       Multiplied against swap in amounts to apply fee, equal to 1 - fee % but units are in basis points, valid for (9000, 10_000)
    /// @param  delRisky    Amount of risky tokens deposited
    /// @param  delStable   Amount of stable tokens deposited
    /// @param  delLiquidity Amount of liquidity granted to `recipient`
    event Create(
        address indexed from,
        uint128 strike,
        uint32 sigma,
        uint32 indexed maturity,
        uint32 indexed gamma,
        uint256 delRisky,
        uint256 delStable,
        uint256 delLiquidity
    );

    /// @notice             Updates the time until expiry of the pool with `poolId`
    /// @param  poolId      Keccak256 hash of the engine address, strike, sigma, maturity, and gamma
    event UpdateLastTimestamp(bytes32 indexed poolId);

    // ===== Margin ====

    /// @notice             Added stable and/or risky tokens to a margin account
    /// @param  from        Method caller `msg.sender`
    /// @param  recipient   Margin account recieving deposits
    /// @param  delRisky    Amount of risky tokens deposited
    /// @param  delStable   Amount of stable tokens deposited
    event Deposit(address indexed from, address indexed recipient, uint256 delRisky, uint256 delStable);

    /// @notice             Removes stable and/or risky from a margin account
    /// @param  from        Method caller `msg.sender`
    /// @param  recipient   Address that tokens are sent to
    /// @param  delRisky    Amount of risky tokens withdrawn
    /// @param  delStable   Amount of stable tokens withdrawn
    event Withdraw(address indexed from, address indexed recipient, uint256 delRisky, uint256 delStable);

    // ===== Liquidity =====

    /// @notice             Adds liquidity of risky and stable tokens to a specified `poolId`
    /// @param  from        Method caller `msg.sender`
    /// @param  recipient   Address that receives liquidity
    /// @param  poolId      Keccak256 hash of the engine address, strike, sigma, maturity, and gamma
    /// @param  delRisky    Amount of risky tokens deposited
    /// @param  delStable   Amount of stable tokens deposited
    /// @param  delLiquidity Amount of liquidity granted to `recipient`
    event Allocate(
        address indexed from,
        address indexed recipient,
        bytes32 indexed poolId,
        uint256 delRisky,
        uint256 delStable,
        uint256 delLiquidity
    );

    /// @notice             Adds liquidity of risky and stable tokens to a specified `poolId`
    /// @param  from        Method caller `msg.sender`
    /// @param  poolId      Keccak256 hash of the engine address, strike, sigma, maturity, and gamma
    /// @param  delRisky    Amount of risky tokens deposited
    /// @param  delStable   Amount of stable tokens deposited
    /// @param  delLiquidity Amount of liquidity decreased from `from`
    event Remove(
        address indexed from,
        bytes32 indexed poolId,
        uint256 delRisky,
        uint256 delStable,
        uint256 delLiquidity
    );

    // ===== Swaps =====

    /// @notice             Swaps between `risky` and `stable` assets
    /// @param  from        Method caller `msg.sender`
    /// @param  recipient   Address that receives `deltaOut` amount of tokens
    /// @param  poolId      Keccak256 hash of the engine address, strike, sigma, maturity, and gamma
    /// @param  riskyForStable  If true, swaps risky to stable, else swaps stable to risky
    /// @param  deltaIn     Amount of tokens added to reserves
    /// @param  deltaOut    Amount of tokens removed from reserves
    event Swap(
        address indexed from,
        address indexed recipient,
        bytes32 indexed poolId,
        bool riskyForStable,
        uint256 deltaIn,
        uint256 deltaOut
    );
}

File 19 of 20 : IPrimitiveEngineView.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.0;

/// @title  View functions of the Primitive Engine contract
/// @author Primitive
interface IPrimitiveEngineView {
    // ===== View =====

    /// @notice             Fetches the current invariant, notation is usually `k`, based on risky and stable token reserves of pool with `poolId`
    /// @param  poolId      Keccak256 hash of the engine address, strike, sigma, maturity, and gamma
    /// @return invariant   Signed fixed point 64.64 number, invariant of `poolId`
    function invariantOf(bytes32 poolId) external view returns (int128 invariant);

    // ===== Constants =====

    /// @return Precision units to scale to when doing token related calculations
    function PRECISION() external view returns (uint256);

    /// @return Amount of seconds after pool expiry which allows swaps, no swaps after buffer
    function BUFFER() external view returns (uint256);

    // ===== Immutables =====

    /// @return Amount of liquidity burned on `create()` calls
    function MIN_LIQUIDITY() external view returns (uint256);

    //// @return Factory address which deployed this engine contract
    function factory() external view returns (address);

    //// @return Risky token address, a more accurate name is the underlying token
    function risky() external view returns (address);

    /// @return Stable token address, a more accurate name is the quote token
    function stable() external view returns (address);

    /// @return Multiplier to scale amounts to/from, equal to 10^(18 - riskyDecimals)
    function scaleFactorRisky() external view returns (uint256);

    /// @return Multiplier to scale amounts to/from, equal to 10^(18 - stableDecimals)
    function scaleFactorStable() external view returns (uint256);

    // ===== Pool State =====

    /// @notice                      Fetches the global reserve state for a pool with `poolId`
    /// @param  poolId               Keccak256 hash of the engine address, strike, sigma, maturity, and gamma
    /// @return reserveRisky         Risky token balance in the reserve
    /// @return reserveStable        Stable token balance in the reserve
    /// @return liquidity            Total supply of liquidity for the curve
    /// @return blockTimestamp       Timestamp when the cumulative reserve values were last updated
    /// @return cumulativeRisky      Cumulative sum of risky token reserves of the previous update
    /// @return cumulativeStable     Cumulative sum of stable token reserves of the previous update
    /// @return cumulativeLiquidity  Cumulative sum of total supply of liquidity of the previous update
    function reserves(bytes32 poolId)
        external
        view
        returns (
            uint128 reserveRisky,
            uint128 reserveStable,
            uint128 liquidity,
            uint32 blockTimestamp,
            uint256 cumulativeRisky,
            uint256 cumulativeStable,
            uint256 cumulativeLiquidity
        );

    /// @notice                 Fetches `Calibration` pool parameters
    /// @param  poolId          Keccak256 hash of the engine address, strike, sigma, maturity, and gamma
    /// @return strike          Marginal price of the pool's risky token at maturity, with the same decimals as the stable token, valid [0, 2^128-1]
    /// @return sigma           AKA Implied Volatility in basis points, determines the price impact of swaps, valid for (1, 10_000_000)
    /// @return maturity        Timestamp which starts the BUFFER countdown until swaps will cease, in seconds, valid for (block.timestamp, 2^32-1]
    /// @return lastTimestamp   Last timestamp used to calculate time until expiry, aka "tau"
    /// @return gamma           Multiplied against swap in amounts to apply fee, equal to 1 - fee % but units are in basis points, valid for (9_000, 10_000)
    function calibrations(bytes32 poolId)
        external
        view
        returns (
            uint128 strike,
            uint32 sigma,
            uint32 maturity,
            uint32 lastTimestamp,
            uint32 gamma
        );

    /// @notice             Fetches position liquidity an account address and poolId
    /// @param  poolId      Keccak256 hash of the engine address, strike, sigma, maturity, and gamma
    /// @return liquidity   Liquidity owned by `account` in `poolId`
    function liquidity(address account, bytes32 poolId) external view returns (uint256 liquidity);

    /// @notice                 Fetches the margin balances of `account`
    /// @param  account         Margin account to fetch
    /// @return balanceRisky    Balance of the risky token
    /// @return balanceStable   Balance of the stable token
    function margins(address account) external view returns (uint128 balanceRisky, uint128 balanceStable);
}

File 20 of 20 : IPrimitiveEngineErrors.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.4;

/// @title  Errors for the Primitive Engine contract
/// @author Primitive
/// @notice Custom errors are encoded with their selector and arguments
/// @dev    Peripheral smart contracts should try catch and check if data matches another custom error
interface IPrimitiveEngineErrors {
    /// @notice Thrown on attempted re-entrancy on a function with a re-entrancy guard
    error LockedError();

    /// @notice Thrown when the balanceOf function is not successful and does not return data
    error BalanceError();

    /// @notice Thrown in create when a pool with computed poolId already exists
    error PoolDuplicateError();

    /// @notice Thrown when calling an expired pool, where block.timestamp > maturity, + BUFFER if swap
    error PoolExpiredError();

    /// @notice Thrown when liquidity is lower than or equal to the minimum amount of liquidity
    error MinLiquidityError(uint256 value);

    /// @notice Thrown when riskyPerLp is outside the range of acceptable values, 0 < riskyPerLp <= 1eRiskyDecimals
    error RiskyPerLpError(uint256 value);

    /// @notice Thrown when sigma is outside the range of acceptable values, 1 <= sigma <= 1e7 with 4 precision
    error SigmaError(uint256 value);

    /// @notice Thrown when strike is not valid, i.e. equal to 0 or greater than 2^128
    error StrikeError(uint256 value);

    /// @notice Thrown when gamma, equal to 1 - fee %, is outside its bounds: 9_000 <= gamma <= 10_000; 1_000 = 10% fee
    error GammaError(uint256 value);

    /// @notice Thrown when the parameters of a new pool are invalid, causing initial reserves to be 0
    error CalibrationError(uint256 delRisky, uint256 delStable);

    /// @notice         Thrown when the expected risky balance is less than the actual balance
    /// @param expected Expected risky balance
    /// @param actual   Actual risky balance
    error RiskyBalanceError(uint256 expected, uint256 actual);

    /// @notice         Thrown when the expected stable balance is less than the actual balance
    /// @param expected Expected stable balance
    /// @param actual   Actual stable balance
    error StableBalanceError(uint256 expected, uint256 actual);

    /// @notice Thrown when the pool with poolId has not been created
    error UninitializedError();

    /// @notice Thrown when the risky or stable amount is 0
    error ZeroDeltasError();

    /// @notice Thrown when the liquidity parameter is 0
    error ZeroLiquidityError();

    /// @notice Thrown when the deltaIn parameter is 0
    error DeltaInError();

    /// @notice Thrown when the deltaOut parameter is 0
    error DeltaOutError();

    /// @notice                 Thrown when the invariant check fails
    /// @dev                    Most important check as it verifies the validity of a desired swap
    /// @param  invariant       Pre-swap invariant updated with new tau
    /// @param  nextInvariant   Post-swap invariant after the swap amounts are applied to reserves
    error InvariantError(int128 invariant, int128 nextInvariant);
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BalanceError","type":"error"},{"inputs":[{"internalType":"uint256","name":"delRisky","type":"uint256"},{"internalType":"uint256","name":"delStable","type":"uint256"}],"name":"CalibrationError","type":"error"},{"inputs":[],"name":"DeltaInError","type":"error"},{"inputs":[],"name":"DeltaOutError","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"GammaError","type":"error"},{"inputs":[{"internalType":"int128","name":"invariant","type":"int128"},{"internalType":"int128","name":"nextInvariant","type":"int128"}],"name":"InvariantError","type":"error"},{"inputs":[{"internalType":"int128","name":"value","type":"int128"}],"name":"InverseOutOfBounds","type":"error"},{"inputs":[],"name":"LockedError","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"MinLiquidityError","type":"error"},{"inputs":[],"name":"PoolDuplicateError","type":"error"},{"inputs":[],"name":"PoolExpiredError","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"RiskyBalanceError","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"RiskyPerLpError","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"SigmaError","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"StableBalanceError","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"StrikeError","type":"error"},{"inputs":[],"name":"UninitializedError","type":"error"},{"inputs":[],"name":"ZeroDeltasError","type":"error"},{"inputs":[],"name":"ZeroLiquidityError","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"delRisky","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delStable","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delLiquidity","type":"uint256"}],"name":"Allocate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint128","name":"strike","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"sigma","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"maturity","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"gamma","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"delRisky","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delStable","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delLiquidity","type":"uint256"}],"name":"Create","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"delRisky","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delStable","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"delRisky","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delStable","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delLiquidity","type":"uint256"}],"name":"Remove","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"},{"indexed":false,"internalType":"bool","name":"riskyForStable","type":"bool"},{"indexed":false,"internalType":"uint256","name":"deltaIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"deltaOut","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolId","type":"bytes32"}],"name":"UpdateLastTimestamp","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"delRisky","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delStable","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"BUFFER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_LIQUIDITY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"delRisky","type":"uint256"},{"internalType":"uint256","name":"delStable","type":"uint256"},{"internalType":"bool","name":"fromMargin","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"allocate","outputs":[{"internalType":"uint256","name":"delLiquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"calibrations","outputs":[{"internalType":"uint128","name":"strike","type":"uint128"},{"internalType":"uint32","name":"sigma","type":"uint32"},{"internalType":"uint32","name":"maturity","type":"uint32"},{"internalType":"uint32","name":"lastTimestamp","type":"uint32"},{"internalType":"uint32","name":"gamma","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"strike","type":"uint128"},{"internalType":"uint32","name":"sigma","type":"uint32"},{"internalType":"uint32","name":"maturity","type":"uint32"},{"internalType":"uint32","name":"gamma","type":"uint32"},{"internalType":"uint256","name":"riskyPerLp","type":"uint256"},{"internalType":"uint256","name":"delLiquidity","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"create","outputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"},{"internalType":"uint256","name":"delRisky","type":"uint256"},{"internalType":"uint256","name":"delStable","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"delRisky","type":"uint256"},{"internalType":"uint256","name":"delStable","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"}],"name":"invariantOf","outputs":[{"internalType":"int128","name":"invariant","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"liquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"margins","outputs":[{"internalType":"uint128","name":"balanceRisky","type":"uint128"},{"internalType":"uint128","name":"balanceStable","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"},{"internalType":"uint256","name":"delLiquidity","type":"uint256"}],"name":"remove","outputs":[{"internalType":"uint256","name":"delRisky","type":"uint256"},{"internalType":"uint256","name":"delStable","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"reserves","outputs":[{"internalType":"uint128","name":"reserveRisky","type":"uint128"},{"internalType":"uint128","name":"reserveStable","type":"uint128"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint32","name":"blockTimestamp","type":"uint32"},{"internalType":"uint256","name":"cumulativeRisky","type":"uint256"},{"internalType":"uint256","name":"cumulativeStable","type":"uint256"},{"internalType":"uint256","name":"cumulativeLiquidity","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"risky","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"scaleFactorRisky","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"scaleFactorStable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stable","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes32","name":"poolId","type":"bytes32"},{"internalType":"bool","name":"riskyForStable","type":"bool"},{"internalType":"uint256","name":"deltaIn","type":"uint256"},{"internalType":"uint256","name":"deltaOut","type":"uint256"},{"internalType":"bool","name":"fromMargin","type":"bool"},{"internalType":"bool","name":"toMargin","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolId","type":"bytes32"}],"name":"updateLastTimestamp","outputs":[{"internalType":"uint32","name":"lastTimestamp","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"delRisky","type":"uint256"},{"internalType":"uint256","name":"delStable","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

61014060405260016000553480156200001757600080fd5b50336001600160a01b0316634e9b75b66040518163ffffffff1660e01b815260040160c06040518083038186803b1580156200005257600080fd5b505afa15801562000067573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200008d9190620000de565b60805260c05260a0526001600160601b0319606091821b81166101205291811b8216610100529190911b1660e05262000144565b80516001600160a01b0381168114620000d957600080fd5b919050565b60008060008060008060c08789031215620000f857600080fd5b6200010387620000c1565b95506200011360208801620000c1565b94506200012360408801620000c1565b9350606087015192506080870151915060a087015190509295509295509295565b60805160a05160c05160e05160601c6101005160601c6101205160601c614188620001ff6000396000818161028201528181610af3015281816116850152611ed90152600081816104dc01528181610ab90152818161179a0152611d95015260006105030152600081816103a8015281816106bd01528181610c4101526115670152600081816103f50152818161069c01528181610c1f015261154601526000818161024d01528181610d7e01526110b601526141886000f3fe608060405234801561001057600080fd5b506004361061016b5760003560e01c80638fbc3ecd116100cd578063be00763a11610081578063c45a015511610066578063c45a0155146104fe578063ca28fcd614610525578063d2957b8f1461053857600080fd5b8063be00763a146104a9578063c08165d4146104d757600080fd5b80639f8cfade116100b25780639f8cfade1461045f578063aaf5eb6814610487578063b5c5f6721461049657600080fd5b80638fbc3ecd146104425780639e48ff5a1461044a57600080fd5b80633882046511610124578063546fecae11610109578063546fecae146103ca57806355ebb825146103f05780636b35bb6b1461041757600080fd5b8063388204651461034f5780634dd0d056146103a357600080fd5b806321b77d631161015557806321b77d631461024857806322be3de11461027d57806323c5b952146102bc57600080fd5b8062bbf1e41461017057806311f9908614610220575b600080fd5b6101cf61017e366004613e1e565b6003602081905260009182526040909120805460018201546002830154938301546004909301546001600160801b0380841695600160801b948590048216959184169490930463ffffffff16929187565b604080516001600160801b0398891681529688166020880152949096169385019390935263ffffffff919091166060840152608083015260a082015260c081019190915260e0015b60405180910390f35b61023361022e366004613e1e565b61054b565b60405163ffffffff9091168152602001610217565b61026f7f000000000000000000000000000000000000000000000000000000000000000081565b604051908152602001610217565b6102a47f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610217565b6103126102ca366004613e1e565b6001602052600090815260409020546001600160801b0381169063ffffffff600160801b8204811691600160a01b8104821691600160c01b8204811691600160e01b90041685565b604080516001600160801b03909616865263ffffffff9485166020870152928416928501929092528216606084015216608082015260a001610217565b61038361035d366004613c75565b6002602052600090815260409020546001600160801b0380821691600160801b90041682565b604080516001600160801b03938416815292909116602083015201610217565b61026f7f000000000000000000000000000000000000000000000000000000000000000081565b6103dd6103d8366004613e1e565b610588565b604051600f9190910b8152602001610217565b61026f7f000000000000000000000000000000000000000000000000000000000000000081565b61026f610425366004613c90565b600460209081526000928352604080842090915290825290205481565b61026f607881565b61045d610458366004613d99565b61070d565b005b61047261046d366004613ebb565b6108a1565b60408051928352602083019190915201610217565b61026f670de0b6b3a764000081565b61045d6104a4366004613d66565b610a47565b6104bc6104b7366004613edd565b610b6a565b60408051938452602084019290925290820152606001610217565b6102a47f000000000000000000000000000000000000000000000000000000000000000081565b6102a47f000000000000000000000000000000000000000000000000000000000000000081565b61045d610533366004613cba565b611260565b61026f610546366004613e37565b6118ea565b6000805460011461056f57604051635cd4e48360e01b815260040160405180910390fd5b600260005561057d82611b7a565b600160005592915050565b6000818152600160209081526040808320815160a08101835290546001600160801b038116825263ffffffff600160801b8204811694830194909452600160a01b81048416928201839052600160c01b8104841660608301819052600160e01b909104909316608082015291839161060091906140e4565b6000858152600360208181526040808420815160e08101835281546001600160801b038082168352600160801b9182900481169583019590955260018301549485169382019390935263ffffffff9290930482166060840152600281015460808401529283015460a083015260049092015460c0820152929350909182916106939190670de0b6b3a764000090611c2416565b915091506107037f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000848488600001516001600160801b0316896020015163ffffffff168963ffffffff16611c83565b9695505050505050565b60005460011461073057604051635cd4e48360e01b815260040160405180910390fd5b600260005583158015610741575082155b1561075f576040516381552f0360e01b815260040160405180910390fd5b6001600160a01b0385166000908152600260205260409020610782908585611cd1565b600080851561079657610793611d7b565b91505b84156107a7576107a4611ebf565b90505b6040517fc536e605000000000000000000000000000000000000000000000000000000008152339063c536e605906107e9908990899089908990600401614001565b600060405180830381600087803b15801561080357600080fd5b505af1158015610817573d6000803e3d6000fd5b505050508560001461083557610835610830878461404c565b611f0b565b841561084d5761084d610848868361404c565b611f5f565b60408051878152602081018790526001600160a01b0389169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3505060016000555050505050565b6000806000546001146108c757604051635cd4e48360e01b815260040160405180910390fd5b6002600055826108ea5760405163e5664db760e01b815260040160405180910390fd5b60008481526003602052604090206001810154600160801b900463ffffffff1661092757604051631d2ad63f60e01b815260040160405180910390fd5b6040805160e08101825282546001600160801b038082168352600160801b918290048116602084015260018501549081169383019390935263ffffffff92048216606082015260028301546080820152600383015460a0820152600483015460c0820152610997918690611c2416565b3360009081526004602090815260408083208a84529091528120805493965091945086926109c69084906140cd565b909155506109d990508184848742611faf565b3360009081526002602052604090206109f3908484611cd1565b6040805184815260208101849052908101859052859033907f59c6598fc34aefe62579a03667748ef83b4682874f311affb26b318095359b0e9060600160405180910390a350600160005590939092509050565b600054600114610a6a57604051635cd4e48360e01b815260040160405180910390fd5b600260005581158015610a7b575080155b15610a99576040516381552f0360e01b815260040160405180910390fd5b610aa5600283836120a1565b508115610ae057610ae06001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016848461215c565b8015610b1a57610b1a6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016848361215c565b60408051838152602081018390526001600160a01b0385169133917ff341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567910160405180910390a35050600160005550565b60008060008054600114610b9157604051635cd4e48360e01b815260040160405180910390fd5b60026000556040516bffffffffffffffffffffffff193060601b1660208201527fffffffffffffffffffffffffffffffff0000000000000000000000000000000060808d901b1660348201527fffffffff0000000000000000000000000000000000000000000000000000000060e08c811b821660448401528b811b821660488401528a901b16604c8201527f0000000000000000000000000000000000000000000000000000000000000000907f00000000000000000000000000000000000000000000000000000000000000009060500160408051808303601f19018152918152815160209283012060008181526001909352912054909550600160c01b900463ffffffff1615610cd0576040517fd946062c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b629896808c63ffffffff161180610ced575060018c63ffffffff16105b15610d31576040517f87a2c00100000000000000000000000000000000000000000000000000000000815263ffffffff8d1660048201526024015b60405180910390fd5b6001600160801b038d16610d7c576040517fb3f4bc550000000000000000000000000000000000000000000000000000000081526001600160801b038e166004820152602401610d28565b7f00000000000000000000000000000000000000000000000000000000000000008811610dd8576040517f325951a500000000000000000000000000000000000000000000000000000000815260048101899052602401610d28565b610dea82670de0b6b3a7640000614064565b891180610df5575088155b15610e2f576040517f47ac541e000000000000000000000000000000000000000000000000000000008152600481018a9052602401610d28565b6127108a63ffffffff161180610e4c57506123288a63ffffffff16105b15610e8b576040517ffdb23f4a00000000000000000000000000000000000000000000000000000000815263ffffffff8b166004820152602401610d28565b60006040518060a001604052808f6001600160801b031681526020018e63ffffffff1681526020018d63ffffffff168152602001610ec64290565b63ffffffff1681526020018c63ffffffff168152509050806040015163ffffffff16816060015163ffffffff161115610f125760405163536e15e160e01b815260040160405180910390fd5b600081606001518260400151610f2891906140e4565b9050610f57600085858e86600001516001600160801b0316876020015163ffffffff168763ffffffff166122aa565b9450670de0b6b3a7640000610f6c8b8d614086565b610f769190614064565b9550670de0b6b3a7640000610f8b8b87614086565b610f959190614064565b9450851580610fa2575084155b15610fe3576040517f492d6d170000000000000000000000000000000000000000000000000000000081526004810187905260248101869052604401610d28565b6000878152600160209081526040808320855181549387015192870151606088015160808901516001600160801b039093167fffffffffffffffffffffffff000000000000000000000000000000000000000090961695909517600160801b63ffffffff95861602177fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff16600160a01b9185169190910263ffffffff60c01b191617600160c01b94841694909402939093176001600160e01b0316600160e01b92909316919091029190911790556110db7f00000000000000000000000000000000000000000000000000000000000000008c6140cd565b3360009081526004602090815260408083208c845290915281208054929350839290919061110a90849061404c565b90915550506060830151600089815260036020526040902061113191899089908f90612380565b60008061113c611d7b565b611144611ebf565b91509150336001600160a01b031663c171d27e8a8a8f8f6040518563ffffffff1660e01b815260040161117a9493929190614001565b600060405180830381600087803b15801561119457600080fd5b505af11580156111a8573d6000803e3d6000fd5b505050506111bb8983610830919061404c565b6111c8610848898361404c565b60808086015160408088015188516020808b015184516001600160801b03909316835263ffffffff908116918301919091529281018e9052606081018d90529384018790529181169291169033907ff38f2bc2b712352b1d4a9ea95291a1c688835ac9f70a7a9d0aec57712c8b19129060a00160405180910390a4505050505050506001600081905550985098509895505050505050565b60005460011461128357604051635cd4e48360e01b815260040160405180910390fd5b6002600055856112bf576040517f28226f1a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b846112f6576040517f564b486500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006040518061010001604052808b6001600160a01b031681526020018915158152602001861515815260200185151581526020016113324290565b63ffffffff1681526020018a8152602001888152602001878152509050600061135e8260a00151611b7a565b9050611371607863ffffffff831661404c565b826080015163ffffffff16111561139b5760405163536e15e160e01b815260040160405180910390fd5b60006113aa8360a00151610588565b60a08085018051600090815260016020908152604080832081519586018252546001600160801b038116865263ffffffff600160801b8204811687850152600160a01b82048116878401908152600160c01b8304821660608901908152600160e01b909304909116608088015294518452600390925282209051925194955092939091611436916140e4565b90506000612710846080015163ffffffff168860c001516114579190614086565b6114619190614064565b90506000808860200151156114ad5784546114869084906001600160801b031661404c565b85549092506114a6908f90600160801b90046001600160801b03166140cd565b90506114e6565b84546114c3908f906001600160801b03166140cd565b85549092506114e3908490600160801b90046001600160801b031661404c565b90505b60018501546001600160801b0316611506670de0b6b3a764000084614086565b6115109190614064565b60018601549092506001600160801b0316611533670de0b6b3a764000083614086565b61153d9190614064565b905060006115ad7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000085858b600001516001600160801b03168c6020015163ffffffff168b63ffffffff16611c83565b905080600f0b88600f0b13156115fd576040517fcd4424b0000000000000000000000000000000000000000000000000000000008152600f89810b600483015282900b6024820152604401610d28565b6116268a602001518b60c001518c60e001518d608001518a61244790949392919063ffffffff16565b5050505050505082602001511561174d578260600151156116705760e083015183516001600160a01b0316600090815260026020526040812061166b92909190611cd1565b6116ab565b825160e08401516116ab916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169161215c565b8260400151156116ce5760c08301516116c89060029060006120a1565b50611860565b60006116d8611d7b565b60c085015160405163491dc51560e11b8152919250339163923b8a2a91611708916000908b908b90600401614001565b600060405180830381600087803b15801561172257600080fd5b505af1158015611736573d6000803e3d6000fd5b505050506116c88460c0015182610830919061404c565b8260600151156117855760e083015183516001600160a01b03166000908152600260205260408120611780929091611cd1565b6117c0565b825160e08401516117c0916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169161215c565b8260400151156117de5760c08301516116c8906002906000906120a1565b60006117e8611ebf565b60c085015160405163491dc51560e11b8152919250339163923b8a2a9161181991600091908b908b90600401614001565b600060405180830381600087803b15801561183357600080fd5b505af1158015611847573d6000803e3d6000fd5b5050505061185e8460c0015182610848919061404c565b505b8260a0015183600001516001600160a01b0316336001600160a01b03167fe42b42fe878e256d1efbfb2ba292aadeac0228bbaa31f4ef8537522bdfa3ea1986602001518760c001518860e001516040516118cf9392919092151583526020830191909152604082015260600190565b60405180910390a45050600160005550505050505050505050565b6000805460011461190e57604051635cd4e48360e01b815260040160405180910390fd5b600260005585158061191e575084155b1561193c576040516381552f0360e01b815260040160405180910390fd5b60008881526003602052604090206001810154600160801b900463ffffffff1661197957604051631d2ad63f60e01b815260040160405180910390fd5b8054600182015442916000916001600160801b039182169161199c91168b614086565b6119a69190614064565b835460018501549192506000916001600160801b03600160801b9092048216916119d191168b614086565b6119db9190614064565b90508082106119ea57806119ec565b815b945084611a0c5760405163e5664db760e01b815260040160405180910390fd5b6001600160a01b038b1660009081526004602090815260408083208f845290915281208054879290611a3f90849061404c565b90915550611a529050848b8b8887612380565b8715611a6a57611a6460028b8b6120a1565b50611b15565b600080611a75611d7b565b611a7d611ebf565b6040517f151a8bf80000000000000000000000000000000000000000000000000000000081529193509150339063151a8bf890611ac4908f908f908e908e90600401614001565b600060405180830381600087803b158015611ade57600080fd5b505af1158015611af2573d6000803e3d6000fd5b50505050611b058c83610830919061404c565b611b126108488c8361404c565b50505b604080518b8152602081018b90529081018690528c906001600160a01b038d169033907f86e4803447f73017bf832b0ac69cb7afa3333a5fa823ec42a9f6cf8d17b50ecc9060600160405180910390a450506001600055509098975050505050505050565b60008181526001602052604081208054600160c01b900463ffffffff16611bb457604051631d2ad63f60e01b815260040160405180910390fd5b8054429250600160a01b900463ffffffff908116908316811015611bd6578092505b815463ffffffff60c01b1916600160c01b63ffffffff85160217825560405184907fd69bdb2831892ad6046f30af0f12362aef46cce34d58ed7ad9b3ef8c44b1b46690600090a25050919050565b6040820151825160009182916001600160801b03918216918291611c49911686614086565b611c539190614064565b92508085602001516001600160801b031685611c6f9190614086565b611c799190614064565b9150509250929050565b600080611c9660008a8a8a8989896122aa565b90506000611ca4828a612566565b90506000611cb2888b612566565b9050611cc2600f82900b8361258f565b9b9a5050505050505050505050565b8115611d2057611ce0826125d5565b83548490600090611cfb9084906001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b8015611d7657611d2f816125d5565b83548490601090611d51908490600160801b90046001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b505050565b604051306024820152600090819081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823160e01b906044015b60408051601f198184030181529181526020820180516001600160e01b03167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611e199190613fc6565b600060405180830381855afa9150503d8060008114611e54576040519150601f19603f3d011682016040523d82523d6000602084013e611e59565b606091505b5091509150811580611e6d57508051602014155b15611ea4576040517fad4ea8a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80806020019051810190611eb89190613f84565b9250505090565b604051306024820152600090819081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823160e01b90604401611dc3565b6000611f15611d7b565b905081811015611f5b576040517f9ce3223a0000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401610d28565b5050565b6000611f69611ebf565b905081811015611f5b576040517fc66162eb0000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401610d28565b611fb985826125ef565b611fc2846125d5565b85548690600090611fdd9084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061200a836125d5565b8554869060109061202c908490600160801b90046001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550612059826125d5565b6001860180546000906120769084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505050505050565b33600090815260208490526040902082156120ff576120bf836125d5565b815482906000906120da9084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b81156121555761210e826125d5565b81548290601090612130908490600160801b90046001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b9392505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b03167fa9059cbb0000000000000000000000000000000000000000000000000000000017905291516000928392908716916121d19190613fc6565b6000604051808303816000865af19150503d806000811461220e576040519150601f19603f3d011682016040523d82523d6000602084013e612213565b606091505b509150915081801561223d57508051158061223d57508080602001905181019061223d9190613e01565b6122a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f5472616e73666572206661696c000000000000000000000000000000000000006044820152606401610d28565b5050505050565b6000806122b78588612566565b905060006122c5878a612566565b905060006122d7600160401b8361258f565b905084156123535760006122eb878761269c565b905060006122fb83600f0b6126d9565b9050600061230d600f83900b8461258f565b905060006123388f61232f61232485600f0b6127ac565b600f8b900b9061284d565b600f0b9061288c565b9050612348600f82900b8e6128c8565b975050505050612372565b611cc2896123698d61232f600f88900b8661284d565b600f0b906128c8565b505050979650505050505050565b61238a85826125ef565b612393846125d5565b855486906000906123ae9084906001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506123db836125d5565b855486906010906123fd908490600160801b90046001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061242a826125d5565b6001860180546000906120769084906001600160801b0316614021565b61245185826125ef565b83156124f357612460836125d5565b8554869060009061247b9084906001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506124a8826125d5565b855486906010906124ca908490600160801b90046001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506122a3565b6124fc826125d5565b855486906000906125179084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550612544836125d5565b85548690601090612076908490600160801b90046001600160801b0316614021565b60008061257b83670de0b6b3a7640000614064565b905061258784826128ed565b949350505050565b6000600f82810b9084900b0360016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113155b6125cc57600080fd5b90505b92915050565b60006001600160801b038211156125eb57600080fd5b5090565b600182015460009061260e90600160801b900463ffffffff16836140e4565b905063ffffffff811615611d7657825460028401805463ffffffff9384166001600160801b038085168202909201909255600386018054600160801b9485900483168402019055600186018054600490970180549288169093029091019091557fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff9094169290911602179055565b6000806126b36126ab8461292d565b600f0b61293d565b905060006126c08561295f565b90506126d0600f82900b8361284d565b95945050505050565b6000600160401b600f83900b1215806126f65750600082600f0b13155b15612733576040517f72cc98e8000000000000000000000000000000000000000000000000000000008152600f83900b6004820152602401610d28565b67f999999999999999600f83900b1380159061275b5750670666666666666666600f83900b12155b15612769576125cf8261296d565b670666666666666666600f83900b1215612786576125cf82612a18565b600061279e612799600160401b8561258f565b612a18565b61215590614101565b919050565b6000806127c6600f84900b68016a09e667f3bcc908612ae2565b905060006128046127f96127ee6127df85600f0b612b43565b6753dd02a4f5ee2e469061284d565b600160401b9061288c565b600160401b90612ae2565b905060006128128383612b76565b9050600083600f0b121561282f5761282c81600f0b612c46565b90505b6000610703612842600160401b8461288c565b678000000000000000905b6000600f83810b9083900b0260401d60016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113156125cc57600080fd5b6000600f83810b9083900b0160016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113156125cc57600080fd5b6000806128dd83670de0b6b3a7640000614064565b9050612587600f85900b82612c61565b6000816128f957600080fd5b60006129058484612cda565b90506f7fffffffffffffffffffffffffffffff6001600160801b03821611156125cc57600080fd5b60006125cf826301e185586128ed565b60008082600f0b121561294f57600080fd5b6125cf604083600f0b901b612e4d565b60006125cf826127106128ed565b600080612986600f84900b67800000000000000061258f565b90506000612998600f83900b8361284d565b905060006126d0612a0d6129fd6129d56721d0a04b0e9b94f161232f6129c767c2bf5d74c724e53e198961284d565b61232f600f8a900b8a61284d565b6129f46726a8f3c1f21b336e61232f6787c57e5da70d3c8f198961284d565b600f0b90612ae2565b68015d71f5721242c7879061288c565b600f85900b9061284d565b600080612a3a6126ab612a326127f9600f87900b8761284d565b600f0b612fbb565b90506000612a5e6707a1e70f720eca4361232f6801000bf627fa188410198561284d565b90506000612a826810aeac93f55267a9a561232f68041ed34a25614902368661284d565b90506000612ab768072c7d592d021fb1db61232f612aa96808c27b4617f5f800ea8861284d565b61232f600f89900b8961284d565b90506000612ad7612acc600f85900b84612ae2565b600f86900b9061288c565b979650505050505050565b600081600f0b60001415612af557600080fd5b600082600f0b604085600f0b901b81612b1057612b10614154565b05905060016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113156125cc57600080fd5b6000600f82900b60016001607f1b03191415612b5e57600080fd5b600082600f0b12612b6f57816125cf565b5060000390565b600080612bc8612a0d612bb8612bad612b9c600f88900b68010fb844255a12d72e61284d565b68017401c57014c38f13199061288c565b600f87900b9061284d565b68016a09e667f3bcc9089061288c565b90506000612bf4612be5612bad6748d4c730f051a5fd198561288c565b67413c831bb169f8749061288c565b90506000610703612c3b612c30612c25612c1d612c15600f8c900b8c61284d565b600f0b612c46565b600f0b612ff5565b600f86900b9061284d565b600f88900b9061284d565b600160401b9061258f565b6000600f82900b60016001607f1b03191415612b6f57600080fd5b600081612c70575060006125cf565b600083600f0b1215612c8157600080fd5b600f83900b6001600160801b038316810260401c90608084901c0277ffffffffffffffffffffffffffffffffffffffffffffffff811115612cc157600080fd5b60401b8119811115612cd257600080fd5b019392505050565b600081612ce657600080fd5b600077ffffffffffffffffffffffffffffffffffffffffffffffff8411612d225782604085901b81612d1a57612d1a614154565b049050612e39565b60c084811c6401000000008110612d3b576020918201911c5b620100008110612d4d576010918201911c5b6101008110612d5e576008918201911c5b60108110612d6e576004918201911c5b60048110612d7e576002918201911c5b60028110612d8d576001820191505b60bf820360018603901c6001018260ff0387901b81612dae57612dae614154565b0492506001600160801b03831115612dc557600080fd5b608085901c83026001600160801b038616840260c088901c604089901b82811015612df1576001820391505b608084901b92900382811015612e08576001820391505b829003608084901c8214612e1e57612e1e614128565b888181612e2d57612e2d614154565b04870196505050505050505b6001600160801b038111156125cc57600080fd5b600081612e5c57506000919050565b816001600160801b8210612e755760809190911c9060401b5b600160401b8210612e8b5760409190911c9060201b5b6401000000008210612ea25760209190911c9060101b5b620100008210612eb75760109190911c9060081b5b6101008210612ecb5760089190911c9060041b5b60108210612ede5760049190911c9060021b5b60088210612eea5760011b5b6001818581612efb57612efb614154565b048201901c90506001818581612f1357612f13614154565b048201901c90506001818581612f2b57612f2b614154565b048201901c90506001818581612f4357612f43614154565b048201901c90506001818581612f5b57612f5b614154565b048201901c90506001818581612f7357612f73614154565b048201901c90506001818581612f8b57612f8b614154565b048201901c90506000818581612fa357612fa3614154565b049050808210612fb357806126d0565b509392505050565b60008082600f0b13612fcc57600080fd5b6080612fd78361304f565b600f0b6fb17217f7d1cf79abc9e3b39803f2f6af02901c9050919050565b60006840000000000000000082600f0b1261300f57600080fd5b683fffffffffffffffff1982600f0b121561302c57506000919050565b6125cf608083600f0b700171547652b82fe1777d0ffda0d23a7d1202901d61312e565b60008082600f0b1361306057600080fd5b6000600f83900b600160401b811261307a576040918201911d5b640100000000811261308e576020918201911d5b6201000081126130a0576010918201911d5b61010081126130b1576008918201911d5b601081126130c1576004918201911d5b600481126130d1576002918201911d5b600281126130e0576001820191505b603f19820160401b600f85900b607f8490031b6780000000000000005b60008113156131235790800260ff81901c8281029390930192607f011c9060011d6130fd565b509095945050505050565b60006840000000000000000082600f0b1261314857600080fd5b683fffffffffffffffff1982600f0b121561316557506000919050565b6f8000000000000000000000000000000060006780000000000000008416600f0b13156131a35770016a09e667f3bcc908b2fb1366ea957d3e0260801c5b60008367400000000000000016600f0b13156131d0577001306fe0a31b7152de8d5a46305c85edec0260801c5b60008367200000000000000016600f0b13156131fd577001172b83c7d517adcdf7c8c50eb14a791f0260801c5b60008367100000000000000016600f0b131561322a5770010b5586cf9890f6298b92b71842a983630260801c5b60008367080000000000000016600f0b1315613257577001059b0d31585743ae7c548eb68ca417fd0260801c5b60008367040000000000000016600f0b131561328457700102c9a3e778060ee6f7caca4f7a29bde80260801c5b60008367020000000000000016600f0b13156132b15770010163da9fb33356d84a66ae336dcdfa3f0260801c5b60008367010000000000000016600f0b13156132de57700100b1afa5abcbed6129ab13ec11dc95430260801c5b600083668000000000000016600f0b131561330a5770010058c86da1c09ea1ff19d294cf2f679b0260801c5b600083664000000000000016600f0b1315613336577001002c605e2e8cec506d21bfc89a23a00f0260801c5b600083662000000000000016600f0b131561336257700100162f3904051fa128bca9c55c31e5df0260801c5b600083661000000000000016600f0b131561338e577001000b175effdc76ba38e31671ca9397250260801c5b600083660800000000000016600f0b13156133ba57700100058ba01fb9f96d6cacd4b180917c3d0260801c5b600083660400000000000016600f0b13156133e65770010002c5cc37da9491d0985c348c68e7b30260801c5b600083660200000000000016600f0b1315613412577001000162e525ee054754457d59952920260260801c5b600083660100000000000016600f0b131561343e5770010000b17255775c040618bf4a4ade83fc0260801c5b6000836580000000000016600f0b1315613469577001000058b91b5bc9ae2eed81e9b7d4cfab0260801c5b6000836540000000000016600f0b131561349457700100002c5c89d5ec6ca4d7c8acc017b7c90260801c5b6000836520000000000016600f0b13156134bf5770010000162e43f4f831060e02d839a9d16d0260801c5b6000836510000000000016600f0b13156134ea57700100000b1721bcfc99d9f890ea069117630260801c5b6000836508000000000016600f0b13156135155770010000058b90cf1e6d97f9ca14dbcc16280260801c5b6000836504000000000016600f0b1315613540577001000002c5c863b73f016468f6bac5ca2b0260801c5b6000836502000000000016600f0b131561356b57700100000162e430e5a18f6119e3c02282a50260801c5b6000836501000000000016600f0b1315613596577001000000b1721835514b86e6d96efd1bfe0260801c5b60008364800000000016600f0b13156135c057700100000058b90c0b48c6be5df846c5b2ef0260801c5b60008364400000000016600f0b13156135ea5770010000002c5c8601cc6b9e94213c72737a0260801c5b60008364200000000016600f0b1315613614577001000000162e42fff037df38aa2b219f060260801c5b60008364100000000016600f0b131561363e5770010000000b17217fba9c739aa5819f44f90260801c5b60008364080000000016600f0b1315613668577001000000058b90bfcdee5acd3c1cedc8230260801c5b60008364040000000016600f0b131561369257700100000002c5c85fe31f35a6a30da1be500260801c5b60008364020000000016600f0b13156136bc5770010000000162e42ff0999ce3541b9fffcf0260801c5b60008364010000000016600f0b13156136e657700100000000b17217f80f4ef5aadda455540260801c5b600083638000000016600f0b131561370f5770010000000058b90bfbf8479bd5a81b51ad0260801c5b600083634000000016600f0b1315613738577001000000002c5c85fdf84bd62ae30a74cc0260801c5b600083632000000016600f0b131561376157700100000000162e42fefb2fed257559bdaa0260801c5b600083631000000016600f0b131561378a577001000000000b17217f7d5a7716bba4a9ae0260801c5b600083630800000016600f0b13156137b357700100000000058b90bfbe9ddbac5e109cce0260801c5b600083630400000016600f0b13156137dc5770010000000002c5c85fdf4b15de6f17eb0d0260801c5b600083630200000016600f0b1315613805577001000000000162e42fefa494f1478fde050260801c5b600083630100000016600f0b131561382e5770010000000000b17217f7d20cf927c8e94c0260801c5b6000836280000016600f0b1315613856577001000000000058b90bfbe8f71cb4e4b33d0260801c5b6000836240000016600f0b131561387e57700100000000002c5c85fdf477b662b269450260801c5b6000836220000016600f0b13156138a65770010000000000162e42fefa3ae53369388c0260801c5b6000836210000016600f0b13156138ce57700100000000000b17217f7d1d351a389d400260801c5b6000836208000016600f0b13156138f65770010000000000058b90bfbe8e8b2d3d4ede0260801c5b6000836204000016600f0b131561391e577001000000000002c5c85fdf4741bea6e77e0260801c5b6000836202000016600f0b131561394657700100000000000162e42fefa39fe95583c20260801c5b6000836201000016600f0b131561396e577001000000000000b17217f7d1cfb72b45e10260801c5b60008361800016600f0b131561399557700100000000000058b90bfbe8e7cc35c3f00260801c5b60008361400016600f0b13156139bc5770010000000000002c5c85fdf473e242ea380260801c5b60008361200016600f0b13156139e3577001000000000000162e42fefa39f02b772c0260801c5b60008361100016600f0b1315613a0a5770010000000000000b17217f7d1cf7d83c1a0260801c5b60008361080016600f0b1315613a31577001000000000000058b90bfbe8e7bdcbe2e0260801c5b60008361040016600f0b1315613a5857700100000000000002c5c85fdf473dea871f0260801c5b60008361020016600f0b1315613a7f5770010000000000000162e42fefa39ef44d910260801c5b60008361010016600f0b1315613aa657700100000000000000b17217f7d1cf79e9490260801c5b600083608016600f0b1315613acc5770010000000000000058b90bfbe8e7bce5440260801c5b600083604016600f0b1315613af2577001000000000000002c5c85fdf473de6eca0260801c5b600083602016600f0b1315613b1857700100000000000000162e42fefa39ef366f0260801c5b600083601016600f0b1315613b3e577001000000000000000b17217f7d1cf79afa0260801c5b600083600816600f0b1315613b6457700100000000000000058b90bfbe8e7bcd6d0260801c5b600083600416600f0b1315613b8a5770010000000000000002c5c85fdf473de6b20260801c5b600083600216600f0b1315613bb0577001000000000000000162e42fefa39ef3580260801c5b600083600116600f0b1315613bd65770010000000000000000b17217f7d1cf79ab0260801c5b600f83810b60401d603f03900b1c6f7fffffffffffffffffffffffffffffff8111156125cf57600080fd5b80356001600160a01b03811681146127a757600080fd5b60008083601f840112613c2a57600080fd5b50813567ffffffffffffffff811115613c4257600080fd5b602083019150836020828501011115613c5a57600080fd5b9250929050565b803563ffffffff811681146127a757600080fd5b600060208284031215613c8757600080fd5b61215582613c01565b60008060408385031215613ca357600080fd5b613cac83613c01565b946020939093013593505050565b60008060008060008060008060006101008a8c031215613cd957600080fd5b613ce28a613c01565b985060208a0135975060408a0135613cf98161416a565b965060608a0135955060808a0135945060a08a0135613d178161416a565b935060c08a0135613d278161416a565b925060e08a013567ffffffffffffffff811115613d4357600080fd5b613d4f8c828d01613c18565b915080935050809150509295985092959850929598565b600080600060608486031215613d7b57600080fd5b613d8484613c01565b95602085013595506040909401359392505050565b600080600080600060808688031215613db157600080fd5b613dba86613c01565b94506020860135935060408601359250606086013567ffffffffffffffff811115613de457600080fd5b613df088828901613c18565b969995985093965092949392505050565b600060208284031215613e1357600080fd5b81516125cc8161416a565b600060208284031215613e3057600080fd5b5035919050565b600080600080600080600060c0888a031215613e5257600080fd5b87359650613e6260208901613c01565b955060408801359450606088013593506080880135613e808161416a565b925060a088013567ffffffffffffffff811115613e9c57600080fd5b613ea88a828b01613c18565b989b979a50959850939692959293505050565b60008060408385031215613ece57600080fd5b50508035926020909101359150565b60008060008060008060008060e0898b031215613ef957600080fd5b88356001600160801b0381168114613f1057600080fd5b9750613f1e60208a01613c61565b9650613f2c60408a01613c61565b9550613f3a60608a01613c61565b94506080890135935060a0890135925060c089013567ffffffffffffffff811115613f6457600080fd5b613f708b828c01613c18565b999c989b5096995094979396929594505050565b600060208284031215613f9657600080fd5b5051919050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000825160005b81811015613fe75760208186018101518583015201613fcd565b81811115613ff6576000828501525b509190910192915050565b848152836020820152606060408201526000610703606083018486613f9d565b60006001600160801b038083168185168083038211156140435761404361413e565b01949350505050565b6000821982111561405f5761405f61413e565b500190565b60008261408157634e487b7160e01b600052601260045260246000fd5b500490565b60008160001904831182151516156140a0576140a061413e565b500290565b60006001600160801b03838116908316818110156140c5576140c561413e565b039392505050565b6000828210156140df576140df61413e565b500390565b600063ffffffff838116908316818110156140c5576140c561413e565b600081600f0b60016001607f1b031981141561411f5761411f61413e565b60000392915050565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b801515811461417857600080fd5b5056fea164736f6c6343000806000a

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061016b5760003560e01c80638fbc3ecd116100cd578063be00763a11610081578063c45a015511610066578063c45a0155146104fe578063ca28fcd614610525578063d2957b8f1461053857600080fd5b8063be00763a146104a9578063c08165d4146104d757600080fd5b80639f8cfade116100b25780639f8cfade1461045f578063aaf5eb6814610487578063b5c5f6721461049657600080fd5b80638fbc3ecd146104425780639e48ff5a1461044a57600080fd5b80633882046511610124578063546fecae11610109578063546fecae146103ca57806355ebb825146103f05780636b35bb6b1461041757600080fd5b8063388204651461034f5780634dd0d056146103a357600080fd5b806321b77d631161015557806321b77d631461024857806322be3de11461027d57806323c5b952146102bc57600080fd5b8062bbf1e41461017057806311f9908614610220575b600080fd5b6101cf61017e366004613e1e565b6003602081905260009182526040909120805460018201546002830154938301546004909301546001600160801b0380841695600160801b948590048216959184169490930463ffffffff16929187565b604080516001600160801b0398891681529688166020880152949096169385019390935263ffffffff919091166060840152608083015260a082015260c081019190915260e0015b60405180910390f35b61023361022e366004613e1e565b61054b565b60405163ffffffff9091168152602001610217565b61026f7f000000000000000000000000000000000000000000000000000000000000000a81565b604051908152602001610217565b6102a47f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b6040516001600160a01b039091168152602001610217565b6103126102ca366004613e1e565b6001602052600090815260409020546001600160801b0381169063ffffffff600160801b8204811691600160a01b8104821691600160c01b8204811691600160e01b90041685565b604080516001600160801b03909616865263ffffffff9485166020870152928416928501929092528216606084015216608082015260a001610217565b61038361035d366004613c75565b6002602052600090815260409020546001600160801b0380821691600160801b90041682565b604080516001600160801b03938416815292909116602083015201610217565b61026f7f000000000000000000000000000000000000000000000000000000e8d4a5100081565b6103dd6103d8366004613e1e565b610588565b604051600f9190910b8152602001610217565b61026f7f000000000000000000000000000000000000000000000000000000000000000181565b61026f610425366004613c90565b600460209081526000928352604080842090915290825290205481565b61026f607881565b61045d610458366004613d99565b61070d565b005b61047261046d366004613ebb565b6108a1565b60408051928352602083019190915201610217565b61026f670de0b6b3a764000081565b61045d6104a4366004613d66565b610a47565b6104bc6104b7366004613edd565b610b6a565b60408051938452602084019290925290820152606001610217565b6102a47f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6102a47f0000000000000000000000005ca2d631a37b21e5de2bcb0cbb892d723a96b06881565b61045d610533366004613cba565b611260565b61026f610546366004613e37565b6118ea565b6000805460011461056f57604051635cd4e48360e01b815260040160405180910390fd5b600260005561057d82611b7a565b600160005592915050565b6000818152600160209081526040808320815160a08101835290546001600160801b038116825263ffffffff600160801b8204811694830194909452600160a01b81048416928201839052600160c01b8104841660608301819052600160e01b909104909316608082015291839161060091906140e4565b6000858152600360208181526040808420815160e08101835281546001600160801b038082168352600160801b9182900481169583019590955260018301549485169382019390935263ffffffff9290930482166060840152600281015460808401529283015460a083015260049092015460c0820152929350909182916106939190670de0b6b3a764000090611c2416565b915091506107037f00000000000000000000000000000000000000000000000000000000000000017f000000000000000000000000000000000000000000000000000000e8d4a51000848488600001516001600160801b0316896020015163ffffffff168963ffffffff16611c83565b9695505050505050565b60005460011461073057604051635cd4e48360e01b815260040160405180910390fd5b600260005583158015610741575082155b1561075f576040516381552f0360e01b815260040160405180910390fd5b6001600160a01b0385166000908152600260205260409020610782908585611cd1565b600080851561079657610793611d7b565b91505b84156107a7576107a4611ebf565b90505b6040517fc536e605000000000000000000000000000000000000000000000000000000008152339063c536e605906107e9908990899089908990600401614001565b600060405180830381600087803b15801561080357600080fd5b505af1158015610817573d6000803e3d6000fd5b505050508560001461083557610835610830878461404c565b611f0b565b841561084d5761084d610848868361404c565b611f5f565b60408051878152602081018790526001600160a01b0389169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3505060016000555050505050565b6000806000546001146108c757604051635cd4e48360e01b815260040160405180910390fd5b6002600055826108ea5760405163e5664db760e01b815260040160405180910390fd5b60008481526003602052604090206001810154600160801b900463ffffffff1661092757604051631d2ad63f60e01b815260040160405180910390fd5b6040805160e08101825282546001600160801b038082168352600160801b918290048116602084015260018501549081169383019390935263ffffffff92048216606082015260028301546080820152600383015460a0820152600483015460c0820152610997918690611c2416565b3360009081526004602090815260408083208a84529091528120805493965091945086926109c69084906140cd565b909155506109d990508184848742611faf565b3360009081526002602052604090206109f3908484611cd1565b6040805184815260208101849052908101859052859033907f59c6598fc34aefe62579a03667748ef83b4682874f311affb26b318095359b0e9060600160405180910390a350600160005590939092509050565b600054600114610a6a57604051635cd4e48360e01b815260040160405180910390fd5b600260005581158015610a7b575080155b15610a99576040516381552f0360e01b815260040160405180910390fd5b610aa5600283836120a1565b508115610ae057610ae06001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216848461215c565b8015610b1a57610b1a6001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816848361215c565b60408051838152602081018390526001600160a01b0385169133917ff341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567910160405180910390a35050600160005550565b60008060008054600114610b9157604051635cd4e48360e01b815260040160405180910390fd5b60026000556040516bffffffffffffffffffffffff193060601b1660208201527fffffffffffffffffffffffffffffffff0000000000000000000000000000000060808d901b1660348201527fffffffff0000000000000000000000000000000000000000000000000000000060e08c811b821660448401528b811b821660488401528a901b16604c8201527f0000000000000000000000000000000000000000000000000000000000000001907f000000000000000000000000000000000000000000000000000000e8d4a510009060500160408051808303601f19018152918152815160209283012060008181526001909352912054909550600160c01b900463ffffffff1615610cd0576040517fd946062c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b629896808c63ffffffff161180610ced575060018c63ffffffff16105b15610d31576040517f87a2c00100000000000000000000000000000000000000000000000000000000815263ffffffff8d1660048201526024015b60405180910390fd5b6001600160801b038d16610d7c576040517fb3f4bc550000000000000000000000000000000000000000000000000000000081526001600160801b038e166004820152602401610d28565b7f000000000000000000000000000000000000000000000000000000000000000a8811610dd8576040517f325951a500000000000000000000000000000000000000000000000000000000815260048101899052602401610d28565b610dea82670de0b6b3a7640000614064565b891180610df5575088155b15610e2f576040517f47ac541e000000000000000000000000000000000000000000000000000000008152600481018a9052602401610d28565b6127108a63ffffffff161180610e4c57506123288a63ffffffff16105b15610e8b576040517ffdb23f4a00000000000000000000000000000000000000000000000000000000815263ffffffff8b166004820152602401610d28565b60006040518060a001604052808f6001600160801b031681526020018e63ffffffff1681526020018d63ffffffff168152602001610ec64290565b63ffffffff1681526020018c63ffffffff168152509050806040015163ffffffff16816060015163ffffffff161115610f125760405163536e15e160e01b815260040160405180910390fd5b600081606001518260400151610f2891906140e4565b9050610f57600085858e86600001516001600160801b0316876020015163ffffffff168763ffffffff166122aa565b9450670de0b6b3a7640000610f6c8b8d614086565b610f769190614064565b9550670de0b6b3a7640000610f8b8b87614086565b610f959190614064565b9450851580610fa2575084155b15610fe3576040517f492d6d170000000000000000000000000000000000000000000000000000000081526004810187905260248101869052604401610d28565b6000878152600160209081526040808320855181549387015192870151606088015160808901516001600160801b039093167fffffffffffffffffffffffff000000000000000000000000000000000000000090961695909517600160801b63ffffffff95861602177fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff16600160a01b9185169190910263ffffffff60c01b191617600160c01b94841694909402939093176001600160e01b0316600160e01b92909316919091029190911790556110db7f000000000000000000000000000000000000000000000000000000000000000a8c6140cd565b3360009081526004602090815260408083208c845290915281208054929350839290919061110a90849061404c565b90915550506060830151600089815260036020526040902061113191899089908f90612380565b60008061113c611d7b565b611144611ebf565b91509150336001600160a01b031663c171d27e8a8a8f8f6040518563ffffffff1660e01b815260040161117a9493929190614001565b600060405180830381600087803b15801561119457600080fd5b505af11580156111a8573d6000803e3d6000fd5b505050506111bb8983610830919061404c565b6111c8610848898361404c565b60808086015160408088015188516020808b015184516001600160801b03909316835263ffffffff908116918301919091529281018e9052606081018d90529384018790529181169291169033907ff38f2bc2b712352b1d4a9ea95291a1c688835ac9f70a7a9d0aec57712c8b19129060a00160405180910390a4505050505050506001600081905550985098509895505050505050565b60005460011461128357604051635cd4e48360e01b815260040160405180910390fd5b6002600055856112bf576040517f28226f1a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b846112f6576040517f564b486500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006040518061010001604052808b6001600160a01b031681526020018915158152602001861515815260200185151581526020016113324290565b63ffffffff1681526020018a8152602001888152602001878152509050600061135e8260a00151611b7a565b9050611371607863ffffffff831661404c565b826080015163ffffffff16111561139b5760405163536e15e160e01b815260040160405180910390fd5b60006113aa8360a00151610588565b60a08085018051600090815260016020908152604080832081519586018252546001600160801b038116865263ffffffff600160801b8204811687850152600160a01b82048116878401908152600160c01b8304821660608901908152600160e01b909304909116608088015294518452600390925282209051925194955092939091611436916140e4565b90506000612710846080015163ffffffff168860c001516114579190614086565b6114619190614064565b90506000808860200151156114ad5784546114869084906001600160801b031661404c565b85549092506114a6908f90600160801b90046001600160801b03166140cd565b90506114e6565b84546114c3908f906001600160801b03166140cd565b85549092506114e3908490600160801b90046001600160801b031661404c565b90505b60018501546001600160801b0316611506670de0b6b3a764000084614086565b6115109190614064565b60018601549092506001600160801b0316611533670de0b6b3a764000083614086565b61153d9190614064565b905060006115ad7f00000000000000000000000000000000000000000000000000000000000000017f000000000000000000000000000000000000000000000000000000e8d4a5100085858b600001516001600160801b03168c6020015163ffffffff168b63ffffffff16611c83565b905080600f0b88600f0b13156115fd576040517fcd4424b0000000000000000000000000000000000000000000000000000000008152600f89810b600483015282900b6024820152604401610d28565b6116268a602001518b60c001518c60e001518d608001518a61244790949392919063ffffffff16565b5050505050505082602001511561174d578260600151156116705760e083015183516001600160a01b0316600090815260026020526040812061166b92909190611cd1565b6116ab565b825160e08401516116ab916001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48169161215c565b8260400151156116ce5760c08301516116c89060029060006120a1565b50611860565b60006116d8611d7b565b60c085015160405163491dc51560e11b8152919250339163923b8a2a91611708916000908b908b90600401614001565b600060405180830381600087803b15801561172257600080fd5b505af1158015611736573d6000803e3d6000fd5b505050506116c88460c0015182610830919061404c565b8260600151156117855760e083015183516001600160a01b03166000908152600260205260408120611780929091611cd1565b6117c0565b825160e08401516117c0916001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2169161215c565b8260400151156117de5760c08301516116c8906002906000906120a1565b60006117e8611ebf565b60c085015160405163491dc51560e11b8152919250339163923b8a2a9161181991600091908b908b90600401614001565b600060405180830381600087803b15801561183357600080fd5b505af1158015611847573d6000803e3d6000fd5b5050505061185e8460c0015182610848919061404c565b505b8260a0015183600001516001600160a01b0316336001600160a01b03167fe42b42fe878e256d1efbfb2ba292aadeac0228bbaa31f4ef8537522bdfa3ea1986602001518760c001518860e001516040516118cf9392919092151583526020830191909152604082015260600190565b60405180910390a45050600160005550505050505050505050565b6000805460011461190e57604051635cd4e48360e01b815260040160405180910390fd5b600260005585158061191e575084155b1561193c576040516381552f0360e01b815260040160405180910390fd5b60008881526003602052604090206001810154600160801b900463ffffffff1661197957604051631d2ad63f60e01b815260040160405180910390fd5b8054600182015442916000916001600160801b039182169161199c91168b614086565b6119a69190614064565b835460018501549192506000916001600160801b03600160801b9092048216916119d191168b614086565b6119db9190614064565b90508082106119ea57806119ec565b815b945084611a0c5760405163e5664db760e01b815260040160405180910390fd5b6001600160a01b038b1660009081526004602090815260408083208f845290915281208054879290611a3f90849061404c565b90915550611a529050848b8b8887612380565b8715611a6a57611a6460028b8b6120a1565b50611b15565b600080611a75611d7b565b611a7d611ebf565b6040517f151a8bf80000000000000000000000000000000000000000000000000000000081529193509150339063151a8bf890611ac4908f908f908e908e90600401614001565b600060405180830381600087803b158015611ade57600080fd5b505af1158015611af2573d6000803e3d6000fd5b50505050611b058c83610830919061404c565b611b126108488c8361404c565b50505b604080518b8152602081018b90529081018690528c906001600160a01b038d169033907f86e4803447f73017bf832b0ac69cb7afa3333a5fa823ec42a9f6cf8d17b50ecc9060600160405180910390a450506001600055509098975050505050505050565b60008181526001602052604081208054600160c01b900463ffffffff16611bb457604051631d2ad63f60e01b815260040160405180910390fd5b8054429250600160a01b900463ffffffff908116908316811015611bd6578092505b815463ffffffff60c01b1916600160c01b63ffffffff85160217825560405184907fd69bdb2831892ad6046f30af0f12362aef46cce34d58ed7ad9b3ef8c44b1b46690600090a25050919050565b6040820151825160009182916001600160801b03918216918291611c49911686614086565b611c539190614064565b92508085602001516001600160801b031685611c6f9190614086565b611c799190614064565b9150509250929050565b600080611c9660008a8a8a8989896122aa565b90506000611ca4828a612566565b90506000611cb2888b612566565b9050611cc2600f82900b8361258f565b9b9a5050505050505050505050565b8115611d2057611ce0826125d5565b83548490600090611cfb9084906001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b8015611d7657611d2f816125d5565b83548490601090611d51908490600160801b90046001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b505050565b604051306024820152600090819081906001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823160e01b906044015b60408051601f198184030181529181526020820180516001600160e01b03167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611e199190613fc6565b600060405180830381855afa9150503d8060008114611e54576040519150601f19603f3d011682016040523d82523d6000602084013e611e59565b606091505b5091509150811580611e6d57508051602014155b15611ea4576040517fad4ea8a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80806020019051810190611eb89190613f84565b9250505090565b604051306024820152600090819081906001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816906370a0823160e01b90604401611dc3565b6000611f15611d7b565b905081811015611f5b576040517f9ce3223a0000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401610d28565b5050565b6000611f69611ebf565b905081811015611f5b576040517fc66162eb0000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401610d28565b611fb985826125ef565b611fc2846125d5565b85548690600090611fdd9084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061200a836125d5565b8554869060109061202c908490600160801b90046001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550612059826125d5565b6001860180546000906120769084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505050505050565b33600090815260208490526040902082156120ff576120bf836125d5565b815482906000906120da9084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b81156121555761210e826125d5565b81548290601090612130908490600160801b90046001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b9392505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b03167fa9059cbb0000000000000000000000000000000000000000000000000000000017905291516000928392908716916121d19190613fc6565b6000604051808303816000865af19150503d806000811461220e576040519150601f19603f3d011682016040523d82523d6000602084013e612213565b606091505b509150915081801561223d57508051158061223d57508080602001905181019061223d9190613e01565b6122a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f5472616e73666572206661696c000000000000000000000000000000000000006044820152606401610d28565b5050505050565b6000806122b78588612566565b905060006122c5878a612566565b905060006122d7600160401b8361258f565b905084156123535760006122eb878761269c565b905060006122fb83600f0b6126d9565b9050600061230d600f83900b8461258f565b905060006123388f61232f61232485600f0b6127ac565b600f8b900b9061284d565b600f0b9061288c565b9050612348600f82900b8e6128c8565b975050505050612372565b611cc2896123698d61232f600f88900b8661284d565b600f0b906128c8565b505050979650505050505050565b61238a85826125ef565b612393846125d5565b855486906000906123ae9084906001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506123db836125d5565b855486906010906123fd908490600160801b90046001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555061242a826125d5565b6001860180546000906120769084906001600160801b0316614021565b61245185826125ef565b83156124f357612460836125d5565b8554869060009061247b9084906001600160801b0316614021565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506124a8826125d5565b855486906010906124ca908490600160801b90046001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506122a3565b6124fc826125d5565b855486906000906125179084906001600160801b03166140a5565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550612544836125d5565b85548690601090612076908490600160801b90046001600160801b0316614021565b60008061257b83670de0b6b3a7640000614064565b905061258784826128ed565b949350505050565b6000600f82810b9084900b0360016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113155b6125cc57600080fd5b90505b92915050565b60006001600160801b038211156125eb57600080fd5b5090565b600182015460009061260e90600160801b900463ffffffff16836140e4565b905063ffffffff811615611d7657825460028401805463ffffffff9384166001600160801b038085168202909201909255600386018054600160801b9485900483168402019055600186018054600490970180549288169093029091019091557fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff9094169290911602179055565b6000806126b36126ab8461292d565b600f0b61293d565b905060006126c08561295f565b90506126d0600f82900b8361284d565b95945050505050565b6000600160401b600f83900b1215806126f65750600082600f0b13155b15612733576040517f72cc98e8000000000000000000000000000000000000000000000000000000008152600f83900b6004820152602401610d28565b67f999999999999999600f83900b1380159061275b5750670666666666666666600f83900b12155b15612769576125cf8261296d565b670666666666666666600f83900b1215612786576125cf82612a18565b600061279e612799600160401b8561258f565b612a18565b61215590614101565b919050565b6000806127c6600f84900b68016a09e667f3bcc908612ae2565b905060006128046127f96127ee6127df85600f0b612b43565b6753dd02a4f5ee2e469061284d565b600160401b9061288c565b600160401b90612ae2565b905060006128128383612b76565b9050600083600f0b121561282f5761282c81600f0b612c46565b90505b6000610703612842600160401b8461288c565b678000000000000000905b6000600f83810b9083900b0260401d60016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113156125cc57600080fd5b6000600f83810b9083900b0160016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113156125cc57600080fd5b6000806128dd83670de0b6b3a7640000614064565b9050612587600f85900b82612c61565b6000816128f957600080fd5b60006129058484612cda565b90506f7fffffffffffffffffffffffffffffff6001600160801b03821611156125cc57600080fd5b60006125cf826301e185586128ed565b60008082600f0b121561294f57600080fd5b6125cf604083600f0b901b612e4d565b60006125cf826127106128ed565b600080612986600f84900b67800000000000000061258f565b90506000612998600f83900b8361284d565b905060006126d0612a0d6129fd6129d56721d0a04b0e9b94f161232f6129c767c2bf5d74c724e53e198961284d565b61232f600f8a900b8a61284d565b6129f46726a8f3c1f21b336e61232f6787c57e5da70d3c8f198961284d565b600f0b90612ae2565b68015d71f5721242c7879061288c565b600f85900b9061284d565b600080612a3a6126ab612a326127f9600f87900b8761284d565b600f0b612fbb565b90506000612a5e6707a1e70f720eca4361232f6801000bf627fa188410198561284d565b90506000612a826810aeac93f55267a9a561232f68041ed34a25614902368661284d565b90506000612ab768072c7d592d021fb1db61232f612aa96808c27b4617f5f800ea8861284d565b61232f600f89900b8961284d565b90506000612ad7612acc600f85900b84612ae2565b600f86900b9061288c565b979650505050505050565b600081600f0b60001415612af557600080fd5b600082600f0b604085600f0b901b81612b1057612b10614154565b05905060016001607f1b031981128015906125c357506f7fffffffffffffffffffffffffffffff8113156125cc57600080fd5b6000600f82900b60016001607f1b03191415612b5e57600080fd5b600082600f0b12612b6f57816125cf565b5060000390565b600080612bc8612a0d612bb8612bad612b9c600f88900b68010fb844255a12d72e61284d565b68017401c57014c38f13199061288c565b600f87900b9061284d565b68016a09e667f3bcc9089061288c565b90506000612bf4612be5612bad6748d4c730f051a5fd198561288c565b67413c831bb169f8749061288c565b90506000610703612c3b612c30612c25612c1d612c15600f8c900b8c61284d565b600f0b612c46565b600f0b612ff5565b600f86900b9061284d565b600f88900b9061284d565b600160401b9061258f565b6000600f82900b60016001607f1b03191415612b6f57600080fd5b600081612c70575060006125cf565b600083600f0b1215612c8157600080fd5b600f83900b6001600160801b038316810260401c90608084901c0277ffffffffffffffffffffffffffffffffffffffffffffffff811115612cc157600080fd5b60401b8119811115612cd257600080fd5b019392505050565b600081612ce657600080fd5b600077ffffffffffffffffffffffffffffffffffffffffffffffff8411612d225782604085901b81612d1a57612d1a614154565b049050612e39565b60c084811c6401000000008110612d3b576020918201911c5b620100008110612d4d576010918201911c5b6101008110612d5e576008918201911c5b60108110612d6e576004918201911c5b60048110612d7e576002918201911c5b60028110612d8d576001820191505b60bf820360018603901c6001018260ff0387901b81612dae57612dae614154565b0492506001600160801b03831115612dc557600080fd5b608085901c83026001600160801b038616840260c088901c604089901b82811015612df1576001820391505b608084901b92900382811015612e08576001820391505b829003608084901c8214612e1e57612e1e614128565b888181612e2d57612e2d614154565b04870196505050505050505b6001600160801b038111156125cc57600080fd5b600081612e5c57506000919050565b816001600160801b8210612e755760809190911c9060401b5b600160401b8210612e8b5760409190911c9060201b5b6401000000008210612ea25760209190911c9060101b5b620100008210612eb75760109190911c9060081b5b6101008210612ecb5760089190911c9060041b5b60108210612ede5760049190911c9060021b5b60088210612eea5760011b5b6001818581612efb57612efb614154565b048201901c90506001818581612f1357612f13614154565b048201901c90506001818581612f2b57612f2b614154565b048201901c90506001818581612f4357612f43614154565b048201901c90506001818581612f5b57612f5b614154565b048201901c90506001818581612f7357612f73614154565b048201901c90506001818581612f8b57612f8b614154565b048201901c90506000818581612fa357612fa3614154565b049050808210612fb357806126d0565b509392505050565b60008082600f0b13612fcc57600080fd5b6080612fd78361304f565b600f0b6fb17217f7d1cf79abc9e3b39803f2f6af02901c9050919050565b60006840000000000000000082600f0b1261300f57600080fd5b683fffffffffffffffff1982600f0b121561302c57506000919050565b6125cf608083600f0b700171547652b82fe1777d0ffda0d23a7d1202901d61312e565b60008082600f0b1361306057600080fd5b6000600f83900b600160401b811261307a576040918201911d5b640100000000811261308e576020918201911d5b6201000081126130a0576010918201911d5b61010081126130b1576008918201911d5b601081126130c1576004918201911d5b600481126130d1576002918201911d5b600281126130e0576001820191505b603f19820160401b600f85900b607f8490031b6780000000000000005b60008113156131235790800260ff81901c8281029390930192607f011c9060011d6130fd565b509095945050505050565b60006840000000000000000082600f0b1261314857600080fd5b683fffffffffffffffff1982600f0b121561316557506000919050565b6f8000000000000000000000000000000060006780000000000000008416600f0b13156131a35770016a09e667f3bcc908b2fb1366ea957d3e0260801c5b60008367400000000000000016600f0b13156131d0577001306fe0a31b7152de8d5a46305c85edec0260801c5b60008367200000000000000016600f0b13156131fd577001172b83c7d517adcdf7c8c50eb14a791f0260801c5b60008367100000000000000016600f0b131561322a5770010b5586cf9890f6298b92b71842a983630260801c5b60008367080000000000000016600f0b1315613257577001059b0d31585743ae7c548eb68ca417fd0260801c5b60008367040000000000000016600f0b131561328457700102c9a3e778060ee6f7caca4f7a29bde80260801c5b60008367020000000000000016600f0b13156132b15770010163da9fb33356d84a66ae336dcdfa3f0260801c5b60008367010000000000000016600f0b13156132de57700100b1afa5abcbed6129ab13ec11dc95430260801c5b600083668000000000000016600f0b131561330a5770010058c86da1c09ea1ff19d294cf2f679b0260801c5b600083664000000000000016600f0b1315613336577001002c605e2e8cec506d21bfc89a23a00f0260801c5b600083662000000000000016600f0b131561336257700100162f3904051fa128bca9c55c31e5df0260801c5b600083661000000000000016600f0b131561338e577001000b175effdc76ba38e31671ca9397250260801c5b600083660800000000000016600f0b13156133ba57700100058ba01fb9f96d6cacd4b180917c3d0260801c5b600083660400000000000016600f0b13156133e65770010002c5cc37da9491d0985c348c68e7b30260801c5b600083660200000000000016600f0b1315613412577001000162e525ee054754457d59952920260260801c5b600083660100000000000016600f0b131561343e5770010000b17255775c040618bf4a4ade83fc0260801c5b6000836580000000000016600f0b1315613469577001000058b91b5bc9ae2eed81e9b7d4cfab0260801c5b6000836540000000000016600f0b131561349457700100002c5c89d5ec6ca4d7c8acc017b7c90260801c5b6000836520000000000016600f0b13156134bf5770010000162e43f4f831060e02d839a9d16d0260801c5b6000836510000000000016600f0b13156134ea57700100000b1721bcfc99d9f890ea069117630260801c5b6000836508000000000016600f0b13156135155770010000058b90cf1e6d97f9ca14dbcc16280260801c5b6000836504000000000016600f0b1315613540577001000002c5c863b73f016468f6bac5ca2b0260801c5b6000836502000000000016600f0b131561356b57700100000162e430e5a18f6119e3c02282a50260801c5b6000836501000000000016600f0b1315613596577001000000b1721835514b86e6d96efd1bfe0260801c5b60008364800000000016600f0b13156135c057700100000058b90c0b48c6be5df846c5b2ef0260801c5b60008364400000000016600f0b13156135ea5770010000002c5c8601cc6b9e94213c72737a0260801c5b60008364200000000016600f0b1315613614577001000000162e42fff037df38aa2b219f060260801c5b60008364100000000016600f0b131561363e5770010000000b17217fba9c739aa5819f44f90260801c5b60008364080000000016600f0b1315613668577001000000058b90bfcdee5acd3c1cedc8230260801c5b60008364040000000016600f0b131561369257700100000002c5c85fe31f35a6a30da1be500260801c5b60008364020000000016600f0b13156136bc5770010000000162e42ff0999ce3541b9fffcf0260801c5b60008364010000000016600f0b13156136e657700100000000b17217f80f4ef5aadda455540260801c5b600083638000000016600f0b131561370f5770010000000058b90bfbf8479bd5a81b51ad0260801c5b600083634000000016600f0b1315613738577001000000002c5c85fdf84bd62ae30a74cc0260801c5b600083632000000016600f0b131561376157700100000000162e42fefb2fed257559bdaa0260801c5b600083631000000016600f0b131561378a577001000000000b17217f7d5a7716bba4a9ae0260801c5b600083630800000016600f0b13156137b357700100000000058b90bfbe9ddbac5e109cce0260801c5b600083630400000016600f0b13156137dc5770010000000002c5c85fdf4b15de6f17eb0d0260801c5b600083630200000016600f0b1315613805577001000000000162e42fefa494f1478fde050260801c5b600083630100000016600f0b131561382e5770010000000000b17217f7d20cf927c8e94c0260801c5b6000836280000016600f0b1315613856577001000000000058b90bfbe8f71cb4e4b33d0260801c5b6000836240000016600f0b131561387e57700100000000002c5c85fdf477b662b269450260801c5b6000836220000016600f0b13156138a65770010000000000162e42fefa3ae53369388c0260801c5b6000836210000016600f0b13156138ce57700100000000000b17217f7d1d351a389d400260801c5b6000836208000016600f0b13156138f65770010000000000058b90bfbe8e8b2d3d4ede0260801c5b6000836204000016600f0b131561391e577001000000000002c5c85fdf4741bea6e77e0260801c5b6000836202000016600f0b131561394657700100000000000162e42fefa39fe95583c20260801c5b6000836201000016600f0b131561396e577001000000000000b17217f7d1cfb72b45e10260801c5b60008361800016600f0b131561399557700100000000000058b90bfbe8e7cc35c3f00260801c5b60008361400016600f0b13156139bc5770010000000000002c5c85fdf473e242ea380260801c5b60008361200016600f0b13156139e3577001000000000000162e42fefa39f02b772c0260801c5b60008361100016600f0b1315613a0a5770010000000000000b17217f7d1cf7d83c1a0260801c5b60008361080016600f0b1315613a31577001000000000000058b90bfbe8e7bdcbe2e0260801c5b60008361040016600f0b1315613a5857700100000000000002c5c85fdf473dea871f0260801c5b60008361020016600f0b1315613a7f5770010000000000000162e42fefa39ef44d910260801c5b60008361010016600f0b1315613aa657700100000000000000b17217f7d1cf79e9490260801c5b600083608016600f0b1315613acc5770010000000000000058b90bfbe8e7bce5440260801c5b600083604016600f0b1315613af2577001000000000000002c5c85fdf473de6eca0260801c5b600083602016600f0b1315613b1857700100000000000000162e42fefa39ef366f0260801c5b600083601016600f0b1315613b3e577001000000000000000b17217f7d1cf79afa0260801c5b600083600816600f0b1315613b6457700100000000000000058b90bfbe8e7bcd6d0260801c5b600083600416600f0b1315613b8a5770010000000000000002c5c85fdf473de6b20260801c5b600083600216600f0b1315613bb0577001000000000000000162e42fefa39ef3580260801c5b600083600116600f0b1315613bd65770010000000000000000b17217f7d1cf79ab0260801c5b600f83810b60401d603f03900b1c6f7fffffffffffffffffffffffffffffff8111156125cf57600080fd5b80356001600160a01b03811681146127a757600080fd5b60008083601f840112613c2a57600080fd5b50813567ffffffffffffffff811115613c4257600080fd5b602083019150836020828501011115613c5a57600080fd5b9250929050565b803563ffffffff811681146127a757600080fd5b600060208284031215613c8757600080fd5b61215582613c01565b60008060408385031215613ca357600080fd5b613cac83613c01565b946020939093013593505050565b60008060008060008060008060006101008a8c031215613cd957600080fd5b613ce28a613c01565b985060208a0135975060408a0135613cf98161416a565b965060608a0135955060808a0135945060a08a0135613d178161416a565b935060c08a0135613d278161416a565b925060e08a013567ffffffffffffffff811115613d4357600080fd5b613d4f8c828d01613c18565b915080935050809150509295985092959850929598565b600080600060608486031215613d7b57600080fd5b613d8484613c01565b95602085013595506040909401359392505050565b600080600080600060808688031215613db157600080fd5b613dba86613c01565b94506020860135935060408601359250606086013567ffffffffffffffff811115613de457600080fd5b613df088828901613c18565b969995985093965092949392505050565b600060208284031215613e1357600080fd5b81516125cc8161416a565b600060208284031215613e3057600080fd5b5035919050565b600080600080600080600060c0888a031215613e5257600080fd5b87359650613e6260208901613c01565b955060408801359450606088013593506080880135613e808161416a565b925060a088013567ffffffffffffffff811115613e9c57600080fd5b613ea88a828b01613c18565b989b979a50959850939692959293505050565b60008060408385031215613ece57600080fd5b50508035926020909101359150565b60008060008060008060008060e0898b031215613ef957600080fd5b88356001600160801b0381168114613f1057600080fd5b9750613f1e60208a01613c61565b9650613f2c60408a01613c61565b9550613f3a60608a01613c61565b94506080890135935060a0890135925060c089013567ffffffffffffffff811115613f6457600080fd5b613f708b828c01613c18565b999c989b5096995094979396929594505050565b600060208284031215613f9657600080fd5b5051919050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000825160005b81811015613fe75760208186018101518583015201613fcd565b81811115613ff6576000828501525b509190910192915050565b848152836020820152606060408201526000610703606083018486613f9d565b60006001600160801b038083168185168083038211156140435761404361413e565b01949350505050565b6000821982111561405f5761405f61413e565b500190565b60008261408157634e487b7160e01b600052601260045260246000fd5b500490565b60008160001904831182151516156140a0576140a061413e565b500290565b60006001600160801b03838116908316818110156140c5576140c561413e565b039392505050565b6000828210156140df576140df61413e565b500390565b600063ffffffff838116908316818110156140c5576140c561413e565b600081600f0b60016001607f1b031981141561411f5761411f61413e565b60000392915050565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b801515811461417857600080fd5b5056fea164736f6c6343000806000a

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.