ETH Price: $1,793.91 (+10.36%)

Contract

0x759E817F0C40B11C775d1071d466B5ff5c6ce28e

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Perform Upkeep M...3293287242025-04-23 9:14:3840 mins ago1745399678IN
0x759E817F...f5c6ce28e
0 ETH0.000004180.01
Perform Upkeep M...3293287222025-04-23 9:14:3740 mins ago1745399677IN
0x759E817F...f5c6ce28e
0 ETH0.000004180.01
Perform Upkeep M...3293287212025-04-23 9:14:3740 mins ago1745399677IN
0x759E817F...f5c6ce28e
0 ETH0.000009120.01
Perform Upkeep M...3293143032025-04-23 8:14:371 hr ago1745396077IN
0x759E817F...f5c6ce28e
0 ETH0.00000290.0129
Perform Upkeep M...3293143022025-04-23 8:14:371 hr ago1745396077IN
0x759E817F...f5c6ce28e
0 ETH0.000002890.012868
Perform Upkeep M...3293143022025-04-23 8:14:371 hr ago1745396077IN
0x759E817F...f5c6ce28e
0 ETH0.000009240.012868
Perform Upkeep M...3292999412025-04-23 7:14:372 hrs ago1745392477IN
0x759E817F...f5c6ce28e
0 ETH0.000004780.012824
Perform Upkeep M...3292999402025-04-23 7:14:372 hrs ago1745392477IN
0x759E817F...f5c6ce28e
0 ETH0.000004790.01293
Perform Upkeep M...3292999402025-04-23 7:14:372 hrs ago1745392477IN
0x759E817F...f5c6ce28e
0 ETH0.000011120.01293
Perform Upkeep M...3292855022025-04-23 6:14:373 hrs ago1745388877IN
0x759E817F...f5c6ce28e
0 ETH0.000004270.01
Perform Upkeep M...3292855012025-04-23 6:14:373 hrs ago1745388877IN
0x759E817F...f5c6ce28e
0 ETH0.000004270.01
Perform Upkeep M...3292855012025-04-23 6:14:373 hrs ago1745388877IN
0x759E817F...f5c6ce28e
0 ETH0.000009180.01
Perform Upkeep M...3292710322025-04-23 5:14:374 hrs ago1745385277IN
0x759E817F...f5c6ce28e
0 ETH0.00000350.01892
Perform Upkeep M...3292710322025-04-23 5:14:374 hrs ago1745385277IN
0x759E817F...f5c6ce28e
0 ETH0.000012820.01892
Perform Upkeep M...3292710312025-04-23 5:14:364 hrs ago1745385276IN
0x759E817F...f5c6ce28e
0 ETH0.000003490.018781
Perform Upkeep M...3292566102025-04-23 4:14:375 hrs ago1745381677IN
0x759E817F...f5c6ce28e
0 ETH0.000003010.01
Perform Upkeep M...3292566082025-04-23 4:14:375 hrs ago1745381677IN
0x759E817F...f5c6ce28e
0 ETH0.000003010.01
Perform Upkeep M...3292566082025-04-23 4:14:375 hrs ago1745381677IN
0x759E817F...f5c6ce28e
0 ETH0.000007940.01
Perform Upkeep M...3292422082025-04-23 3:14:376 hrs ago1745378077IN
0x759E817F...f5c6ce28e
0 ETH0.000004050.01
Perform Upkeep M...3292422072025-04-23 3:14:376 hrs ago1745378077IN
0x759E817F...f5c6ce28e
0 ETH0.000008970.01
Perform Upkeep M...3292422062025-04-23 3:14:366 hrs ago1745378076IN
0x759E817F...f5c6ce28e
0 ETH0.000004050.01
Perform Upkeep M...3292277282025-04-23 2:14:377 hrs ago1745374477IN
0x759E817F...f5c6ce28e
0 ETH0.000004120.01
Perform Upkeep M...3292277272025-04-23 2:14:377 hrs ago1745374477IN
0x759E817F...f5c6ce28e
0 ETH0.000009020.01
Perform Upkeep M...3292277262025-04-23 2:14:367 hrs ago1745374476IN
0x759E817F...f5c6ce28e
0 ETH0.000004120.01
Perform Upkeep M...3292133382025-04-23 1:14:408 hrs ago1745370880IN
0x759E817F...f5c6ce28e
0 ETH0.000006410.083158
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
720802752023-03-21 9:52:27764 days ago1679392347
0x759E817F...f5c6ce28e
0 ETH
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PoolKeeper

Compiler Version
v0.8.7+commit.e28d00a7

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 15 : PoolKeeper.sol
//SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity 0.8.7;

import "../interfaces/IPoolKeeper.sol";
import "../interfaces/IOracleWrapper.sol";
import "../interfaces/IPoolFactory.sol";
import "../interfaces/ILeveragedPool.sol";
import "../interfaces/IERC20DecimalsWrapper.sol";
import "../interfaces/IERC20DecimalsWrapper.sol";
import "./PoolSwapLibrary.sol";

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "abdk-libraries-solidity/ABDKMathQuad.sol";
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol";

/// @title The manager contract for multiple markets and the pools in them
contract PoolKeeper is IPoolKeeper, Ownable {
    /* Constants */
    uint256 public constant BASE_TIP = 5; // 5% base tip
    uint256 public constant TIP_DELTA_PER_BLOCK = 5; // 5% increase per block
    uint256 public constant BLOCK_TIME = 13; /* in seconds */
    uint256 public constant MAX_DECIMALS = 18;

    // #### Global variables
    /**
     * @notice Format: Pool address => last executionPrice
     */
    mapping(address => int256) public executionPrice;

    IPoolFactory public factory;
    bytes16 constant fixedPoint = 0x403abc16d674ec800000000000000000; // 1 ether

    uint256 public gasPrice = 10 gwei;

    // #### Functions
    constructor(address _factory) {
        require(_factory != address(0), "Factory cannot be 0 address");
        factory = IPoolFactory(_factory);
    }

    /**
     * @notice When a pool is created, this function is called by the factory to initiate price trackings
     * @param _poolAddress The address of the newly-created pools
     */
    function newPool(address _poolAddress) external override onlyFactory {
        address oracleWrapper = ILeveragedPool(_poolAddress).oracleWrapper();
        int256 firstPrice = IOracleWrapper(oracleWrapper).getPrice();
        require(firstPrice > 0, "First price is non-positive");
        int256 startingPrice = ABDKMathQuad.toInt(ABDKMathQuad.mul(ABDKMathQuad.fromInt(firstPrice), fixedPoint));
        emit PoolAdded(_poolAddress, firstPrice);
        executionPrice[_poolAddress] = startingPrice;
    }

    // Keeper network
    /**
     * @notice Check if upkeep is required
     * @param _pool The address of the pool to upkeep
     * @return upkeepNeeded Whether or not upkeep is needed for this single pool
     */
    function checkUpkeepSinglePool(address _pool) public view override returns (bool) {
        if (!factory.isValidPool(_pool)) {
            return false;
        }

        // The update interval has passed
        return ILeveragedPool(_pool).intervalPassed();
    }

    /**
     * @notice Checks multiple pools if any of them need updating
     * @param _pools The array of pools to check
     * @return upkeepNeeded Whether or not at least one pool needs upkeeping
     */
    function checkUpkeepMultiplePools(address[] calldata _pools) external view override returns (bool) {
        for (uint256 i = 0; i < _pools.length; i++) {
            if (checkUpkeepSinglePool(_pools[i])) {
                // One has been found that requires upkeeping
                return true;
            }
        }
        return false;
    }

    /**
     * @notice Called by keepers to perform an update on a single pool
     * @param _pool The pool code to perform the update for
     */
    function performUpkeepSinglePool(address _pool) public override {
        uint256 startGas = gasleft();

        // validate the pool, check that the interval time has passed
        if (!checkUpkeepSinglePool(_pool)) {
            return;
        }
        ILeveragedPool pool = ILeveragedPool(_pool);
        (int256 latestPrice, bytes memory data, uint256 savedPreviousUpdatedTimestamp, uint256 updateInterval) = pool
            .getUpkeepInformation();

        // Start a new round
        // Get price in WAD format
        int256 lastExecutionPrice = executionPrice[_pool];
        executionPrice[_pool] = latestPrice;

        // This allows us to still batch multiple calls to executePriceChange, even if some are invalid
        // Without reverting the entire transaction
        try pool.poolUpkeep(lastExecutionPrice, latestPrice) {
            // If poolUpkeep is successful, refund the keeper for their gas costs
            uint256 gasSpent = startGas - gasleft();

            payKeeper(_pool, gasPrice, gasSpent, savedPreviousUpdatedTimestamp, updateInterval);
            emit UpkeepSuccessful(_pool, data, lastExecutionPrice, latestPrice);
        } catch Error(string memory reason) {
            // If poolUpkeep fails for any other reason, emit event
            emit PoolUpkeepError(_pool, reason);
        }
    }

    /**
     * @notice Called by keepers to perform an update on multiple pools
     * @param pools pool codes to perform the update for
     */
    function performUpkeepMultiplePools(address[] calldata pools) external override {
        for (uint256 i = 0; i < pools.length; i++) {
            performUpkeepSinglePool(pools[i]);
        }
    }

    /**
     * @notice Pay keeper for upkeep
     * @param _pool Address of the given pool
     * @param _gasPrice Price of a single gas unit (in ETH)
     * @param _gasSpent Number of gas units spent
     * @param _savedPreviousUpdatedTimestamp Last timestamp when the pool's price execution happened
     * @param _updateInterval Pool interval of the given pool
     */
    function payKeeper(
        address _pool,
        uint256 _gasPrice,
        uint256 _gasSpent,
        uint256 _savedPreviousUpdatedTimestamp,
        uint256 _updateInterval
    ) internal {
        uint256 reward = keeperReward(_pool, _gasPrice, _gasSpent, _savedPreviousUpdatedTimestamp, _updateInterval);
        if (ILeveragedPool(_pool).payKeeperFromBalances(msg.sender, reward)) {
            emit KeeperPaid(_pool, msg.sender, reward);
        } else {
            // Usually occurs if pool just started and does not have any funds
            emit KeeperPaymentError(_pool, msg.sender, reward);
        }
    }

    /**
     * @notice Payment keeper receives for performing upkeep on a given pool
     * @param _pool Address of the given pool
     * @param _gasPrice Price of a single gas unit (in ETH)
     * @param _gasSpent Number of gas units spent
     * @param _savedPreviousUpdatedTimestamp Last timestamp when the pool's price execution happened
     * @param _poolInterval Pool interval of the given pool
     * @return Number of settlement tokens to give to the keeper for work performed
     */
    function keeperReward(
        address _pool,
        uint256 _gasPrice,
        uint256 _gasSpent,
        uint256 _savedPreviousUpdatedTimestamp,
        uint256 _poolInterval
    ) public view returns (uint256) {
        // keeper gas cost in wei. WAD formatted
        uint256 _keeperGas = keeperGas(_pool, _gasPrice, _gasSpent);

        // tip percent in wad units
        bytes16 _tipPercent = ABDKMathQuad.fromUInt(keeperTip(_savedPreviousUpdatedTimestamp, _poolInterval));

        // amount of settlement tokens to give to the keeper
        _tipPercent = ABDKMathQuad.div(_tipPercent, ABDKMathQuad.fromUInt(100));
        int256 wadRewardValue = ABDKMathQuad.toInt(
            ABDKMathQuad.add(
                ABDKMathQuad.fromUInt(_keeperGas),
                ABDKMathQuad.div((ABDKMathQuad.mul(ABDKMathQuad.fromUInt(_keeperGas), _tipPercent)), fixedPoint)
            )
        );
        uint256 decimals = IERC20DecimalsWrapper(ILeveragedPool(_pool).quoteToken()).decimals();
        uint256 deWadifiedReward = PoolSwapLibrary.fromWad(uint256(wadRewardValue), decimals);
        // _keeperGas + _keeperGas * percentTip
        return deWadifiedReward;
    }

    /**
     * @notice Compensation a keeper will receive for their gas expenditure
     * @param _pool Address of the given pool
     * @param _gasPrice Price of a single gas unit (in ETH)
     * @param _gasSpent Number of gas units spent
     * @return Keeper's gas compensation
     */
    function keeperGas(
        address _pool,
        uint256 _gasPrice,
        uint256 _gasSpent
    ) public view returns (uint256) {
        int256 settlementTokenPrice = IOracleWrapper(ILeveragedPool(_pool).settlementEthOracle()).getPrice();

        if (settlementTokenPrice <= 0) {
            return 0;
        } else {
            /* safe due to explicit bounds check above */
            /* (wei * Settlement / ETH) / fixed point (10^18) = amount in settlement */
            bytes16 _weiSpent = ABDKMathQuad.fromUInt(_gasPrice * _gasSpent);
            bytes16 _settlementTokenPrice = ABDKMathQuad.fromUInt(uint256(settlementTokenPrice));
            return
                ABDKMathQuad.toUInt(ABDKMathQuad.div(ABDKMathQuad.mul(_weiSpent, _settlementTokenPrice), fixedPoint));
        }
    }

    /**
     * @notice Tip a keeper will receive for successfully updating the specified pool
     * @param _savedPreviousUpdatedTimestamp Last timestamp when the pool's price execution happened
     * @param _poolInterval Pool interval of the given pool
     * @return Percent of the `keeperGas` cost to add to payment, as a percent
     */
    function keeperTip(uint256 _savedPreviousUpdatedTimestamp, uint256 _poolInterval) public view returns (uint256) {
        /* the number of blocks that have elapsed since the given pool's updateInterval passed */
        uint256 elapsedBlocksNumerator = (block.timestamp - (_savedPreviousUpdatedTimestamp + _poolInterval));

        uint256 keeperTip = BASE_TIP + (TIP_DELTA_PER_BLOCK * elapsedBlocksNumerator) / BLOCK_TIME;

        // In case of network outages or otherwise, we want to cap the tip so that the keeper cost isn't unbounded
        if (keeperTip > 100) {
            return 100;
        } else {
            return keeperTip;
        }
    }

    function setFactory(address _factory) external override onlyOwner {
        factory = IPoolFactory(_factory);
    }

    /**
     * @notice Sets the gas price to be used in compensating keepers for successful upkeep
     * @param _price Price (in ETH) per unit gas
     * @dev Only owner
     */
    function setGasPrice(uint256 _price) external onlyOwner {
        gasPrice = _price;
    }

    modifier onlyFactory() {
        require(msg.sender == address(factory), "Caller not factory");
        _;
    }
}

File 2 of 15 : IPoolKeeper.sol
//SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity 0.8.7;

/// @title The manager contract interface for multiple markets and the pools in them
interface IPoolKeeper {
    // #### Events
    /**
     * @notice Creates a notification when a pool is created
     * @param poolAddress The pool address of the newly created pool
     * @param firstPrice The price of the market oracle when the pool was created
     */
    event PoolAdded(address indexed poolAddress, int256 indexed firstPrice);

    /**
     * @notice Creates a notification when a call to LeveragedPool:poolUpkeep is successful
     * @param pool The pool address being upkept
     * @param data Extra data about the price fetch. This could be roundID in the case of Chainlink Oracles
     * @param startPrice The previous price of the pool
     * @param endPrice The new price of the pool
     */
    event UpkeepSuccessful(address indexed pool, bytes data, int256 indexed startPrice, int256 indexed endPrice);

    /**
     * @notice Creates a notification when a keeper is paid for doing upkeep for a pool
     * @param _pool Address of pool being upkept
     * @param keeper Keeper to be rewarded for upkeeping
     * @param reward Keeper's reward (in settlement tokens)
     */
    event KeeperPaid(address indexed _pool, address indexed keeper, uint256 reward);

    /**
     * @notice Creates a notification when a keeper's payment for upkeeping a pool failed
     * @param _pool Address of pool being upkept
     * @param keeper Keeper to be rewarded for upkeeping
     * @param expectedReward Keeper's expected reward (in settlement tokens); not actually transferred
     */
    event KeeperPaymentError(address indexed _pool, address indexed keeper, uint256 expectedReward);

    /**
     * @notice Creates a notification of a failed pool update
     * @param pool The pool that failed to update
     * @param reason The reason for the error
     */
    event PoolUpkeepError(address indexed pool, string reason);

    // #### Functions
    function newPool(address _poolAddress) external;

    function setFactory(address _factory) external;

    function checkUpkeepSinglePool(address pool) external view returns (bool);

    function checkUpkeepMultiplePools(address[] calldata pools) external view returns (bool);

    function performUpkeepSinglePool(address pool) external;

    function performUpkeepMultiplePools(address[] calldata pools) external;
}

File 3 of 15 : IOracleWrapper.sol
//SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity 0.8.7;

/// @title The oracle wrapper contract interface
interface IOracleWrapper {
    function oracle() external view returns (address);

    // #### Functions
    /**
     * @notice Sets the oracle for a given market
     * @dev Should be secured, ideally only allowing the PoolKeeper to access
     * @param _oracle The oracle to set for the market
     */
    function setOracle(address _oracle) external;

    /**
     * @notice Returns the current price for the asset in question
     * @return The latest price
     */
    function getPrice() external view returns (int256);

    /**
     * @return _price The latest round data price
     * @return _data The metadata. Implementations can choose what data to return here
     */
    function getPriceAndMetadata() external view returns (int256 _price, bytes memory _data);

    /**
     * @notice Converts from a WAD to normal value
     * @return Converted non-WAD value
     */
    function fromWad(int256 wad) external view returns (int256);
}

File 4 of 15 : IPoolFactory.sol
//SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity 0.8.7;

/// @title The contract factory for the keeper and pool contracts. Utilizes minimal clones to keep gas costs low
interface IPoolFactory {
    struct PoolDeployment {
        string poolName; // The name to identify a pool by
        uint32 frontRunningInterval; // The minimum number of seconds that must elapse before a commit can be executed. Must be smaller than or equal to the update interval to prevent deadlock
        uint32 updateInterval; // The minimum number of seconds that must elapse before a price change
        uint16 leverageAmount; // The amount of exposure to price movements for the pool
        address quoteToken; // The digital asset that the pool accepts
        address oracleWrapper; // The IOracleWrapper implementation for fetching price feed data
        address settlementEthOracle; // The oracle to fetch the price of Ether in terms of the settlement token
        uint128 minimumCommitSize; // The minimum amount (in settlement tokens) that a user can commit in a single commitment
        uint128 maximumCommitQueueLength; // The maximum number of commitments that can be made for a given updateInterval
    }

    // #### Events
    /**
     * @notice Creates a notification when a pool is deployed
     * @param pool Address of the new pool
     * @param ticker Ticker of the neew pool
     */
    event DeployPool(address indexed pool, string ticker);

    // #### Getters for Globals
    function pools(uint256 id) external view returns (address);

    function numPools() external view returns (uint256);

    function isValidPool(address _pool) external view returns (bool);

    // #### Functions
    function deployPool(PoolDeployment calldata deploymentParameters) external returns (address);

    function getOwner() external returns (address);

    function setPoolKeeper(address _poolKeeper) external;

    function setMaxLeverage(uint16 newMaxLeverage) external;

    function setFeeReceiver(address _feeReceiver) external;

    function setFee(uint256 _fee) external;

    function setPoolCommitterDeployer(address _poolCommitterDeployer) external;
}

File 5 of 15 : ILeveragedPool.sol
//SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity 0.8.7;

/// @title The pool controller contract interface
interface ILeveragedPool {
    // Initialisation parameters for new market
    struct Initialization {
        address _owner; // Owner of the contract
        address _keeper; // The address of the PoolKeeper contract
        address _oracleWrapper; // The oracle wrapper for the derivative price feed
        address _settlementEthOracle; // The oracle wrapper for the SettlementToken/ETH price feed
        address _longToken; // Address of the long pool token
        address _shortToken; // Address of the short pool token
        address _poolCommitter; // Address of the PoolCommitter contract
        string _poolName; // The pool identification name
        uint32 _frontRunningInterval; // The minimum number of seconds that must elapse before a commit is forced to wait until the next interval
        uint32 _updateInterval; // The minimum number of seconds that must elapse before a commit can be executed
        uint256 _fee; // The fund movement fee. This amount is extracted from the deposited asset with every update and sent to the fee address
        uint16 _leverageAmount; // The amount of exposure to price movements for the pool
        address _feeAddress; // The address that the fund movement fee is sent to
        address _quoteToken; //  The digital asset that the pool accepts. Must have a decimals() function
    }

    // #### Events
    /**
     * @notice Creates a notification when the pool is setup and ready for use
     * @param longToken The address of the LONG pair token
     * @param shortToken The address of the SHORT pair token
     * @param quoteToken The address of the digital asset that the pool accepts
     * @param poolName The pool code for the pool
     */
    event PoolInitialized(address indexed longToken, address indexed shortToken, address quoteToken, string poolName);

    /**
     * @notice Creates a notification when the pool is rebalanced
     * @param shortBalanceChange The change of funds in the short side
     * @param longBalanceChange The change of funds in the long side
     */
    event PoolRebalance(int256 shortBalanceChange, int256 longBalanceChange);

    /**
     * @notice Creates a notification when the pool's price execution fails
     * @param startPrice Price prior to price change execution
     * @param endPrice Price during price change execution
     */
    event PriceChangeError(int256 indexed startPrice, int256 indexed endPrice);

    /**
     * @notice Represents change in fee receiver's address
     * @param oldAddress Previous address
     * @param newAddress Address after change
     */
    event FeeAddressUpdated(address indexed oldAddress, address indexed newAddress);

    /**
     * @notice Represents change in keeper's address
     * @param oldAddress Previous address
     * @param newAddress Address after change
     */
    event KeeperAddressChanged(address indexed oldAddress, address indexed newAddress);

    /**
     * @notice Represents proposed change in governance address
     * @param newAddress Proposed address
     */
    event ProvisionalGovernanceChanged(address indexed newAddress);

    /**
     * @notice Represents change in governance address
     * @param oldAddress Previous address
     * @param newAddress Address after change
     */
    event GovernanceAddressChanged(address indexed oldAddress, address indexed newAddress);

    function leverageAmount() external view returns (bytes16);

    function poolCommitter() external view returns (address);

    function quoteToken() external view returns (address);

    function oracleWrapper() external view returns (address);

    function lastPriceTimestamp() external view returns (uint256);

    function poolName() external view returns (string calldata);

    function updateInterval() external view returns (uint32);

    function shortBalance() external view returns (uint256);

    function longBalance() external view returns (uint256);

    function frontRunningInterval() external view returns (uint32);

    function poolTokens() external view returns (address[2] memory);

    function settlementEthOracle() external view returns (address);

    // #### Functions
    /**
     * @notice Configures the pool on deployment. The pools are EIP 1167 clones.
     * @dev This should only be able to be run once to prevent abuse of the pool. Use of Openzeppelin Initializable or similar is recommended
     * @param initialization The struct Initialization containing initialization data
     */
    function initialize(Initialization calldata initialization) external;

    function poolUpkeep(int256 _oldPrice, int256 _newPrice) external;

    function quoteTokenTransferFrom(
        address from,
        address to,
        uint256 amount
    ) external;

    function payKeeperFromBalances(address to, uint256 amount) external returns (bool);

    function quoteTokenTransfer(address to, uint256 amount) external;

    function setNewPoolBalances(uint256 _longBalance, uint256 _shortBalance) external;

    /**
     * @return _latestPrice The oracle price
     * @return _data The oracleWrapper's metadata. Implementations can choose what data to return here
     * @return _lastPriceTimestamp The timestamp of the last upkeep
     * @return _updateInterval The update frequency for this pool
     * @dev To save gas so PoolKeeper does not have to make three external calls
     */
    function getUpkeepInformation()
        external
        view
        returns (
            int256 _latestPrice,
            bytes memory _data,
            uint256 _lastPriceTimestamp,
            uint256 _updateInterval
        );

    function getOraclePrice() external view returns (int256);

    function intervalPassed() external view returns (bool);

    function balances() external view returns (uint256 _shortBalance, uint256 _longBalance);

    function setKeeper(address _keeper) external;

    function transferGovernance(address _governance) external;

    function claimGovernance() external;

    function updateFeeAddress(address account) external;

    function mintTokens(
        uint256 token,
        uint256 amount,
        address burner
    ) external;

    function burnTokens(
        uint256 token,
        uint256 amount,
        address burner
    ) external;
}

File 6 of 15 : IERC20DecimalsWrapper.sol
//SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity 0.8.7;

/// @title The decimals interface for extending the ERC20 interface
interface IERC20DecimalsWrapper {
    function decimals() external view returns (uint8);
}

File 7 of 15 : PoolSwapLibrary.sol
//SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity 0.8.7;

import "abdk-libraries-solidity/ABDKMathQuad.sol";

/// @title Library for various useful (mostly) mathematical functions
library PoolSwapLibrary {
    bytes16 public constant one = 0x3fff0000000000000000000000000000;
    bytes16 public constant zero = 0x00000000000000000000000000000000;

    /* ABDKMathQuad defines this but it's private */
    bytes16 private constant NEGATIVE_ZERO = 0x80000000000000000000000000000000;
    uint256 public constant MAX_DECIMALS = 18;

    uint256 public constant WAD_PRECISION = 10**18;

    struct PriceChangeData {
        int256 oldPrice;
        int256 newPrice;
        uint256 longBalance;
        uint256 shortBalance;
        bytes16 leverageAmount;
        bytes16 fee;
    }

    /**
     * @notice Calculates the ratio between two numbers
     * @dev Rounds any overflow towards 0. If either parameter is zero, the ratio is 0
     * @param _numerator The "parts per" side of the equation. If this is zero, the ratio is zero
     * @param _denominator The "per part" side of the equation. If this is zero, the ratio is zero
     * @return the ratio, as an ABDKMathQuad number (IEEE 754 quadruple precision floating point)
     */
    function getRatio(uint256 _numerator, uint256 _denominator) public pure returns (bytes16) {
        // Catch the divide by zero error.
        if (_denominator == 0) {
            return 0;
        }
        return ABDKMathQuad.div(ABDKMathQuad.fromUInt(_numerator), ABDKMathQuad.fromUInt(_denominator));
    }

    /**
     * @notice Gets the short and long balances after the keeper rewards have been paid out
     *         Keeper rewards are paid proportionally to the short and long pool
     * @dev Assumes shortBalance + longBalance >= reward
     * @param reward Amount of keeper reward
     * @param shortBalance Short balance of the pool
     * @param longBalance Long balance of the pool
     * @return shortBalanceAfterFees Short balance of the pool after the keeper reward has been paid
     * @return longBalanceAfterFees Long balance of the pool after the keeper reward has been paid
     */
    function getBalancesAfterFees(
        uint256 reward,
        uint256 shortBalance,
        uint256 longBalance
    ) external pure returns (uint256, uint256) {
        bytes16 ratioShort = getRatio(shortBalance, shortBalance + longBalance);

        uint256 shortFees = convertDecimalToUInt(multiplyDecimalByUInt(ratioShort, reward));

        uint256 shortBalanceAfterFees = shortBalance - shortFees;
        uint256 longBalanceAfterFees = longBalance - (reward - shortFees);

        // Return shortBalance and longBalance after rewards are paid out
        return (shortBalanceAfterFees, longBalanceAfterFees);
    }

    /**
     * @notice Compares two decimal numbers
     * @param x The first number to compare
     * @param y The second number to compare
     * @return -1 if x < y, 0 if x = y, or 1 if x > y
     */
    function compareDecimals(bytes16 x, bytes16 y) public pure returns (int8) {
        return ABDKMathQuad.cmp(x, y);
    }

    /**
     * @notice Converts an integer value to a compatible decimal value
     * @param amount The amount to convert
     * @return The amount as a IEEE754 quadruple precision number
     */
    function convertUIntToDecimal(uint256 amount) external pure returns (bytes16) {
        return ABDKMathQuad.fromUInt(amount);
    }

    /**
     * @notice Converts a raw decimal value to a more readable uint256 value
     * @param ratio The value to convert
     * @return The converted value
     */
    function convertDecimalToUInt(bytes16 ratio) public pure returns (uint256) {
        return ABDKMathQuad.toUInt(ratio);
    }

    /**
     * @notice Multiplies a decimal and an unsigned integer
     * @param a The first term
     * @param b The second term
     * @return The product of a*b as a decimal
     */
    function multiplyDecimalByUInt(bytes16 a, uint256 b) public pure returns (bytes16) {
        return ABDKMathQuad.mul(a, ABDKMathQuad.fromUInt(b));
    }

    /**
     * @notice Divides two integers
     * @param a The dividend
     * @param b The divisor
     * @return The quotient
     */
    function divInt(int256 a, int256 b) public pure returns (bytes16) {
        return ABDKMathQuad.div(ABDKMathQuad.fromInt(a), ABDKMathQuad.fromInt(b));
    }

    /**
     * @notice Calculates the loss multiplier to apply to the losing pool. Includes the power leverage
     * @param ratio The ratio of new price to old price
     * @param direction The direction of the change. -1 if it's decreased, 0 if it hasn't changed, and 1 if it's increased
     * @param leverage The amount of leverage to apply
     * @return The multiplier
     */
    function getLossMultiplier(
        bytes16 ratio,
        int8 direction,
        bytes16 leverage
    ) public pure returns (bytes16) {
        // If decreased:  2 ^ (leverage * log2[(1 * new/old) + [(0 * 1) / new/old]])
        //              = 2 ^ (leverage * log2[(new/old)])
        // If increased:  2 ^ (leverage * log2[(0 * new/old) + [(1 * 1) / new/old]])
        //              = 2 ^ (leverage * log2([1 / new/old]))
        //              = 2 ^ (leverage * log2([old/new]))
        return
            ABDKMathQuad.pow_2(
                ABDKMathQuad.mul(leverage, ABDKMathQuad.log_2(direction < 0 ? ratio : ABDKMathQuad.div(one, ratio)))
            );
    }

    /**
     * @notice Calculates the amount to take from the losing pool
     * @param lossMultiplier The multiplier to use
     * @param balance The balance of the losing pool
     */
    function getLossAmount(bytes16 lossMultiplier, uint256 balance) public pure returns (uint256) {
        return
            ABDKMathQuad.toUInt(
                ABDKMathQuad.mul(ABDKMathQuad.sub(one, lossMultiplier), ABDKMathQuad.fromUInt(balance))
            );
    }

    /**
     * @notice Calculates the effect of a price change. This involves calculating how many funds to transfer from the losing pool to the other.
     * @dev This function should be called by the LeveragedPool.
     * @param priceChange The struct containing necessary data to calculate price change
     */
    function calculatePriceChange(PriceChangeData memory priceChange)
        external
        pure
        returns (
            uint256,
            uint256,
            uint256
        )
    {
        uint256 shortBalance = priceChange.shortBalance;
        uint256 longBalance = priceChange.longBalance;
        bytes16 leverageAmount = priceChange.leverageAmount;
        int256 oldPrice = priceChange.oldPrice;
        int256 newPrice = priceChange.newPrice;
        bytes16 fee = priceChange.fee;

        // Calculate fees from long and short sides
        uint256 longFeeAmount = convertDecimalToUInt(multiplyDecimalByUInt(fee, longBalance)) /
            PoolSwapLibrary.WAD_PRECISION;
        uint256 shortFeeAmount = convertDecimalToUInt(multiplyDecimalByUInt(fee, shortBalance)) /
            PoolSwapLibrary.WAD_PRECISION;

        shortBalance = shortBalance - shortFeeAmount;
        longBalance = longBalance - longFeeAmount;
        uint256 totalFeeAmount = shortFeeAmount + longFeeAmount;

        // Use the ratio to determine if the price increased or decreased and therefore which direction
        // the funds should be transferred towards.

        bytes16 ratio = divInt(newPrice, oldPrice);
        int8 direction = compareDecimals(ratio, PoolSwapLibrary.one);
        // Take into account the leverage
        bytes16 lossMultiplier = getLossMultiplier(ratio, direction, leverageAmount);

        if (direction >= 0 && shortBalance > 0) {
            // Move funds from short to long pair
            uint256 lossAmount = getLossAmount(lossMultiplier, shortBalance);
            shortBalance = shortBalance - lossAmount;
            longBalance = longBalance + lossAmount;
        } else if (direction < 0 && longBalance > 0) {
            // Move funds from long to short pair
            uint256 lossAmount = getLossAmount(lossMultiplier, longBalance);
            shortBalance = shortBalance + lossAmount;
            longBalance = longBalance - lossAmount;
        }

        return (longBalance, shortBalance, totalFeeAmount);
    }

    /**
     * @notice Returns true if the given timestamp is BEFORE the frontRunningInterval starts,
     *         which is allowed for uncommitment.
     * @dev If you try to uncommit AFTER the frontRunningInterval, it should revert.
     * @param subjectTime The timestamp for which you want to calculate if it was beforeFrontRunningInterval
     * @param lastPriceTimestamp The timestamp of the last price update
     * @param updateInterval The interval between price updates
     * @param frontRunningInterval The window of time before a price udpate users can not uncommit or have their commit executed from
     */
    function isBeforeFrontRunningInterval(
        uint256 subjectTime,
        uint256 lastPriceTimestamp,
        uint256 updateInterval,
        uint256 frontRunningInterval
    ) external pure returns (bool) {
        return lastPriceTimestamp + updateInterval - frontRunningInterval > subjectTime;
    }

    /**
     * @notice Gets the number of settlement tokens to be withdrawn based on a pool token burn amount
     * @dev Calculates as `balance * amountIn / (tokenSupply + shadowBalance)
     * @param tokenSupply Total supply of pool tokens
     * @param amountIn Commitment amount of collateral tokens going into the pool
     * @param balance Balance of the pool (no. of underlying collateral tokens in pool)
     * @param shadowBalance Balance the shadow pool at time of mint
     * @return Number of settlement tokens to be withdrawn on a burn
     */
    function getWithdrawAmountOnBurn(
        uint256 tokenSupply,
        uint256 amountIn,
        uint256 balance,
        uint256 shadowBalance
    ) external pure returns (uint256) {
        require(amountIn > 0, "Invalid amount");

        // Catch the divide by zero error.
        if (balance == 0 || tokenSupply + shadowBalance == 0) {
            return amountIn;
        }
        bytes16 numerator = ABDKMathQuad.mul(ABDKMathQuad.fromUInt(balance), ABDKMathQuad.fromUInt(amountIn));
        return ABDKMathQuad.toUInt(ABDKMathQuad.div(numerator, ABDKMathQuad.fromUInt(tokenSupply + shadowBalance)));
    }

    /**
     * @notice Gets the number of pool tokens to be minted based on existing tokens
     * @dev Calculated as (tokenSupply + shadowBalance) * amountIn / balance
     * @param tokenSupply Total supply of pool tokens
     * @param amountIn Commitment amount of collateral tokens going into the pool
     * @param balance Balance of the pool (no. of underlying collateral tokens in pool)
     * @param shadowBalance Balance the shadow pool at time of mint
     * @return Number of pool tokens to be minted
     */
    function getMintAmount(
        uint256 tokenSupply,
        uint256 amountIn,
        uint256 balance,
        uint256 shadowBalance
    ) external pure returns (uint256) {
        require(amountIn > 0, "Invalid amount");

        // Catch the divide by zero error.
        if (balance == 0 || tokenSupply + shadowBalance == 0) {
            return amountIn;
        }

        bytes16 numerator = ABDKMathQuad.mul(
            ABDKMathQuad.fromUInt(tokenSupply + shadowBalance),
            ABDKMathQuad.fromUInt(amountIn)
        );
        return ABDKMathQuad.toUInt(ABDKMathQuad.div(numerator, ABDKMathQuad.fromUInt(balance)));
    }

    /**
     * @notice Converts from a WAD to normal value
     * @return Converted non-WAD value
     */
    function fromWad(uint256 _wadValue, uint256 _decimals) external pure returns (uint256) {
        uint256 scaler = 10**(MAX_DECIMALS - _decimals);
        return _wadValue / scaler;
    }
}

File 8 of 15 : Ownable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _setOwner(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _setOwner(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 9 of 15 : Clones.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create(0, ptr, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create2(0, ptr, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
            mstore(add(ptr, 0x38), shl(0x60, deployer))
            mstore(add(ptr, 0x4c), salt)
            mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
            predicted := keccak256(add(ptr, 0x37), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address implementation, bytes32 salt)
        internal
        view
        returns (address predicted)
    {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 11 of 15 : ABDKMathQuad.sol
// SPDX-License-Identifier: BSD-4-Clause
/*
 * ABDK Math Quad 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 IEEE 754
 * quadruple-precision binary floating-point numbers (quadruple precision
 * numbers).  As long as quadruple precision numbers are 16-bytes long, they are
 * represented by bytes16 type.
 */
library ABDKMathQuad {
  /*
   * 0.
   */
  bytes16 private constant POSITIVE_ZERO = 0x00000000000000000000000000000000;

  /*
   * -0.
   */
  bytes16 private constant NEGATIVE_ZERO = 0x80000000000000000000000000000000;

  /*
   * +Infinity.
   */
  bytes16 private constant POSITIVE_INFINITY = 0x7FFF0000000000000000000000000000;

  /*
   * -Infinity.
   */
  bytes16 private constant NEGATIVE_INFINITY = 0xFFFF0000000000000000000000000000;

  /*
   * Canonical NaN value.
   */
  bytes16 private constant NaN = 0x7FFF8000000000000000000000000000;

  /**
   * Convert signed 256-bit integer number into quadruple precision number.
   *
   * @param x signed 256-bit integer number
   * @return quadruple precision number
   */
  function fromInt (int256 x) internal pure returns (bytes16) {
    unchecked {
      if (x == 0) return bytes16 (0);
      else {
        // We rely on overflow behavior here
        uint256 result = uint256 (x > 0 ? x : -x);

        uint256 msb = mostSignificantBit (result);
        if (msb < 112) result <<= 112 - msb;
        else if (msb > 112) result >>= msb - 112;

        result = result & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF | 16383 + msb << 112;
        if (x < 0) result |= 0x80000000000000000000000000000000;

        return bytes16 (uint128 (result));
      }
    }
  }

  /**
   * Convert quadruple precision number into signed 256-bit integer number
   * rounding towards zero.  Revert on overflow.
   *
   * @param x quadruple precision number
   * @return signed 256-bit integer number
   */
  function toInt (bytes16 x) internal pure returns (int256) {
    unchecked {
      uint256 exponent = uint128 (x) >> 112 & 0x7FFF;

      require (exponent <= 16638); // Overflow
      if (exponent < 16383) return 0; // Underflow

      uint256 result = uint256 (uint128 (x)) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF |
        0x10000000000000000000000000000;

      if (exponent < 16495) result >>= 16495 - exponent;
      else if (exponent > 16495) result <<= exponent - 16495;

      if (uint128 (x) >= 0x80000000000000000000000000000000) { // Negative
        require (result <= 0x8000000000000000000000000000000000000000000000000000000000000000);
        return -int256 (result); // We rely on overflow behavior here
      } else {
        require (result <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
        return int256 (result);
      }
    }
  }

  /**
   * Convert unsigned 256-bit integer number into quadruple precision number.
   *
   * @param x unsigned 256-bit integer number
   * @return quadruple precision number
   */
  function fromUInt (uint256 x) internal pure returns (bytes16) {
    unchecked {
      if (x == 0) return bytes16 (0);
      else {
        uint256 result = x;

        uint256 msb = mostSignificantBit (result);
        if (msb < 112) result <<= 112 - msb;
        else if (msb > 112) result >>= msb - 112;

        result = result & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF | 16383 + msb << 112;

        return bytes16 (uint128 (result));
      }
    }
  }

  /**
   * Convert quadruple precision number into unsigned 256-bit integer number
   * rounding towards zero.  Revert on underflow.  Note, that negative floating
   * point numbers in range (-1.0 .. 0.0) may be converted to unsigned integer
   * without error, because they are rounded to zero.
   *
   * @param x quadruple precision number
   * @return unsigned 256-bit integer number
   */
  function toUInt (bytes16 x) internal pure returns (uint256) {
    unchecked {
      uint256 exponent = uint128 (x) >> 112 & 0x7FFF;

      if (exponent < 16383) return 0; // Underflow

      require (uint128 (x) < 0x80000000000000000000000000000000); // Negative

      require (exponent <= 16638); // Overflow
      uint256 result = uint256 (uint128 (x)) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF |
        0x10000000000000000000000000000;

      if (exponent < 16495) result >>= 16495 - exponent;
      else if (exponent > 16495) result <<= exponent - 16495;

      return result;
    }
  }

  /**
   * Convert signed 128.128 bit fixed point number into quadruple precision
   * number.
   *
   * @param x signed 128.128 bit fixed point number
   * @return quadruple precision number
   */
  function from128x128 (int256 x) internal pure returns (bytes16) {
    unchecked {
      if (x == 0) return bytes16 (0);
      else {
        // We rely on overflow behavior here
        uint256 result = uint256 (x > 0 ? x : -x);

        uint256 msb = mostSignificantBit (result);
        if (msb < 112) result <<= 112 - msb;
        else if (msb > 112) result >>= msb - 112;

        result = result & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF | 16255 + msb << 112;
        if (x < 0) result |= 0x80000000000000000000000000000000;

        return bytes16 (uint128 (result));
      }
    }
  }

  /**
   * Convert quadruple precision number into signed 128.128 bit fixed point
   * number.  Revert on overflow.
   *
   * @param x quadruple precision number
   * @return signed 128.128 bit fixed point number
   */
  function to128x128 (bytes16 x) internal pure returns (int256) {
    unchecked {
      uint256 exponent = uint128 (x) >> 112 & 0x7FFF;

      require (exponent <= 16510); // Overflow
      if (exponent < 16255) return 0; // Underflow

      uint256 result = uint256 (uint128 (x)) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF |
        0x10000000000000000000000000000;

      if (exponent < 16367) result >>= 16367 - exponent;
      else if (exponent > 16367) result <<= exponent - 16367;

      if (uint128 (x) >= 0x80000000000000000000000000000000) { // Negative
        require (result <= 0x8000000000000000000000000000000000000000000000000000000000000000);
        return -int256 (result); // We rely on overflow behavior here
      } else {
        require (result <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
        return int256 (result);
      }
    }
  }

  /**
   * Convert signed 64.64 bit fixed point number into quadruple precision
   * number.
   *
   * @param x signed 64.64 bit fixed point number
   * @return quadruple precision number
   */
  function from64x64 (int128 x) internal pure returns (bytes16) {
    unchecked {
      if (x == 0) return bytes16 (0);
      else {
        // We rely on overflow behavior here
        uint256 result = uint128 (x > 0 ? x : -x);

        uint256 msb = mostSignificantBit (result);
        if (msb < 112) result <<= 112 - msb;
        else if (msb > 112) result >>= msb - 112;

        result = result & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF | 16319 + msb << 112;
        if (x < 0) result |= 0x80000000000000000000000000000000;

        return bytes16 (uint128 (result));
      }
    }
  }

  /**
   * Convert quadruple precision number into signed 64.64 bit fixed point
   * number.  Revert on overflow.
   *
   * @param x quadruple precision number
   * @return signed 64.64 bit fixed point number
   */
  function to64x64 (bytes16 x) internal pure returns (int128) {
    unchecked {
      uint256 exponent = uint128 (x) >> 112 & 0x7FFF;

      require (exponent <= 16446); // Overflow
      if (exponent < 16319) return 0; // Underflow

      uint256 result = uint256 (uint128 (x)) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF |
        0x10000000000000000000000000000;

      if (exponent < 16431) result >>= 16431 - exponent;
      else if (exponent > 16431) result <<= exponent - 16431;

      if (uint128 (x) >= 0x80000000000000000000000000000000) { // Negative
        require (result <= 0x80000000000000000000000000000000);
        return -int128 (int256 (result)); // We rely on overflow behavior here
      } else {
        require (result <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
        return int128 (int256 (result));
      }
    }
  }

  /**
   * Convert octuple precision number into quadruple precision number.
   *
   * @param x octuple precision number
   * @return quadruple precision number
   */
  function fromOctuple (bytes32 x) internal pure returns (bytes16) {
    unchecked {
      bool negative = x & 0x8000000000000000000000000000000000000000000000000000000000000000 > 0;

      uint256 exponent = uint256 (x) >> 236 & 0x7FFFF;
      uint256 significand = uint256 (x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

      if (exponent == 0x7FFFF) {
        if (significand > 0) return NaN;
        else return negative ? NEGATIVE_INFINITY : POSITIVE_INFINITY;
      }

      if (exponent > 278526)
        return negative ? NEGATIVE_INFINITY : POSITIVE_INFINITY;
      else if (exponent < 245649)
        return negative ? NEGATIVE_ZERO : POSITIVE_ZERO;
      else if (exponent < 245761) {
        significand = (significand | 0x100000000000000000000000000000000000000000000000000000000000) >> 245885 - exponent;
        exponent = 0;
      } else {
        significand >>= 124;
        exponent -= 245760;
      }

      uint128 result = uint128 (significand | exponent << 112);
      if (negative) result |= 0x80000000000000000000000000000000;

      return bytes16 (result);
    }
  }

  /**
   * Convert quadruple precision number into octuple precision number.
   *
   * @param x quadruple precision number
   * @return octuple precision number
   */
  function toOctuple (bytes16 x) internal pure returns (bytes32) {
    unchecked {
      uint256 exponent = uint128 (x) >> 112 & 0x7FFF;

      uint256 result = uint128 (x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

      if (exponent == 0x7FFF) exponent = 0x7FFFF; // Infinity or NaN
      else if (exponent == 0) {
        if (result > 0) {
          uint256 msb = mostSignificantBit (result);
          result = result << 236 - msb & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
          exponent = 245649 + msb;
        }
      } else {
        result <<= 124;
        exponent += 245760;
      }

      result |= exponent << 236;
      if (uint128 (x) >= 0x80000000000000000000000000000000)
        result |= 0x8000000000000000000000000000000000000000000000000000000000000000;

      return bytes32 (result);
    }
  }

  /**
   * Convert double precision number into quadruple precision number.
   *
   * @param x double precision number
   * @return quadruple precision number
   */
  function fromDouble (bytes8 x) internal pure returns (bytes16) {
    unchecked {
      uint256 exponent = uint64 (x) >> 52 & 0x7FF;

      uint256 result = uint64 (x) & 0xFFFFFFFFFFFFF;

      if (exponent == 0x7FF) exponent = 0x7FFF; // Infinity or NaN
      else if (exponent == 0) {
        if (result > 0) {
          uint256 msb = mostSignificantBit (result);
          result = result << 112 - msb & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
          exponent = 15309 + msb;
        }
      } else {
        result <<= 60;
        exponent += 15360;
      }

      result |= exponent << 112;
      if (x & 0x8000000000000000 > 0)
        result |= 0x80000000000000000000000000000000;

      return bytes16 (uint128 (result));
    }
  }

  /**
   * Convert quadruple precision number into double precision number.
   *
   * @param x quadruple precision number
   * @return double precision number
   */
  function toDouble (bytes16 x) internal pure returns (bytes8) {
    unchecked {
      bool negative = uint128 (x) >= 0x80000000000000000000000000000000;

      uint256 exponent = uint128 (x) >> 112 & 0x7FFF;
      uint256 significand = uint128 (x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

      if (exponent == 0x7FFF) {
        if (significand > 0) return 0x7FF8000000000000; // NaN
        else return negative ?
            bytes8 (0xFFF0000000000000) : // -Infinity
            bytes8 (0x7FF0000000000000); // Infinity
      }

      if (exponent > 17406)
        return negative ?
            bytes8 (0xFFF0000000000000) : // -Infinity
            bytes8 (0x7FF0000000000000); // Infinity
      else if (exponent < 15309)
        return negative ?
            bytes8 (0x8000000000000000) : // -0
            bytes8 (0x0000000000000000); // 0
      else if (exponent < 15361) {
        significand = (significand | 0x10000000000000000000000000000) >> 15421 - exponent;
        exponent = 0;
      } else {
        significand >>= 60;
        exponent -= 15360;
      }

      uint64 result = uint64 (significand | exponent << 52);
      if (negative) result |= 0x8000000000000000;

      return bytes8 (result);
    }
  }

  /**
   * Test whether given quadruple precision number is NaN.
   *
   * @param x quadruple precision number
   * @return true if x is NaN, false otherwise
   */
  function isNaN (bytes16 x) internal pure returns (bool) {
    unchecked {
      return uint128 (x) & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF >
        0x7FFF0000000000000000000000000000;
    }
  }

  /**
   * Test whether given quadruple precision number is positive or negative
   * infinity.
   *
   * @param x quadruple precision number
   * @return true if x is positive or negative infinity, false otherwise
   */
  function isInfinity (bytes16 x) internal pure returns (bool) {
    unchecked {
      return uint128 (x) & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ==
        0x7FFF0000000000000000000000000000;
    }
  }

  /**
   * Calculate sign of x, i.e. -1 if x is negative, 0 if x if zero, and 1 if x
   * is positive.  Note that sign (-0) is zero.  Revert if x is NaN. 
   *
   * @param x quadruple precision number
   * @return sign of x
   */
  function sign (bytes16 x) internal pure returns (int8) {
    unchecked {
      uint128 absoluteX = uint128 (x) & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

      require (absoluteX <= 0x7FFF0000000000000000000000000000); // Not NaN

      if (absoluteX == 0) return 0;
      else if (uint128 (x) >= 0x80000000000000000000000000000000) return -1;
      else return 1;
    }
  }

  /**
   * Calculate sign (x - y).  Revert if either argument is NaN, or both
   * arguments are infinities of the same sign. 
   *
   * @param x quadruple precision number
   * @param y quadruple precision number
   * @return sign (x - y)
   */
  function cmp (bytes16 x, bytes16 y) internal pure returns (int8) {
    unchecked {
      uint128 absoluteX = uint128 (x) & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

      require (absoluteX <= 0x7FFF0000000000000000000000000000); // Not NaN

      uint128 absoluteY = uint128 (y) & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

      require (absoluteY <= 0x7FFF0000000000000000000000000000); // Not NaN

      // Not infinities of the same sign
      require (x != y || absoluteX < 0x7FFF0000000000000000000000000000);

      if (x == y) return 0;
      else {
        bool negativeX = uint128 (x) >= 0x80000000000000000000000000000000;
        bool negativeY = uint128 (y) >= 0x80000000000000000000000000000000;

        if (negativeX) {
          if (negativeY) return absoluteX > absoluteY ? -1 : int8 (1);
          else return -1; 
        } else {
          if (negativeY) return 1;
          else return absoluteX > absoluteY ? int8 (1) : -1;
        }
      }
    }
  }

  /**
   * Test whether x equals y.  NaN, infinity, and -infinity are not equal to
   * anything. 
   *
   * @param x quadruple precision number
   * @param y quadruple precision number
   * @return true if x equals to y, false otherwise
   */
  function eq (bytes16 x, bytes16 y) internal pure returns (bool) {
    unchecked {
      if (x == y) {
        return uint128 (x) & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF <
          0x7FFF0000000000000000000000000000;
      } else return false;
    }
  }

  /**
   * Calculate x + y.  Special values behave in the following way:
   *
   * NaN + x = NaN for any x.
   * Infinity + x = Infinity for any finite x.
   * -Infinity + x = -Infinity for any finite x.
   * Infinity + Infinity = Infinity.
   * -Infinity + -Infinity = -Infinity.
   * Infinity + -Infinity = -Infinity + Infinity = NaN.
   *
   * @param x quadruple precision number
   * @param y quadruple precision number
   * @return quadruple precision number
   */
  function add (bytes16 x, bytes16 y) internal pure returns (bytes16) {
    unchecked {
      uint256 xExponent = uint128 (x) >> 112 & 0x7FFF;
      uint256 yExponent = uint128 (y) >> 112 & 0x7FFF;

      if (xExponent == 0x7FFF) {
        if (yExponent == 0x7FFF) { 
          if (x == y) return x;
          else return NaN;
        } else return x; 
      } else if (yExponent == 0x7FFF) return y;
      else {
        bool xSign = uint128 (x) >= 0x80000000000000000000000000000000;
        uint256 xSignifier = uint128 (x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
        if (xExponent == 0) xExponent = 1;
        else xSignifier |= 0x10000000000000000000000000000;

        bool ySign = uint128 (y) >= 0x80000000000000000000000000000000;
        uint256 ySignifier = uint128 (y) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
        if (yExponent == 0) yExponent = 1;
        else ySignifier |= 0x10000000000000000000000000000;

        if (xSignifier == 0) return y == NEGATIVE_ZERO ? POSITIVE_ZERO : y;
        else if (ySignifier == 0) return x == NEGATIVE_ZERO ? POSITIVE_ZERO : x;
        else {
          int256 delta = int256 (xExponent) - int256 (yExponent);
  
          if (xSign == ySign) {
            if (delta > 112) return x;
            else if (delta > 0) ySignifier >>= uint256 (delta);
            else if (delta < -112) return y;
            else if (delta < 0) {
              xSignifier >>= uint256 (-delta);
              xExponent = yExponent;
            }
  
            xSignifier += ySignifier;
  
            if (xSignifier >= 0x20000000000000000000000000000) {
              xSignifier >>= 1;
              xExponent += 1;
            }
  
            if (xExponent == 0x7FFF)
              return xSign ? NEGATIVE_INFINITY : POSITIVE_INFINITY;
            else {
              if (xSignifier < 0x10000000000000000000000000000) xExponent = 0;
              else xSignifier &= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
  
              return bytes16 (uint128 (
                  (xSign ? 0x80000000000000000000000000000000 : 0) |
                  (xExponent << 112) |
                  xSignifier)); 
            }
          } else {
            if (delta > 0) {
              xSignifier <<= 1;
              xExponent -= 1;
            } else if (delta < 0) {
              ySignifier <<= 1;
              xExponent = yExponent - 1;
            }

            if (delta > 112) ySignifier = 1;
            else if (delta > 1) ySignifier = (ySignifier - 1 >> uint256 (delta - 1)) + 1;
            else if (delta < -112) xSignifier = 1;
            else if (delta < -1) xSignifier = (xSignifier - 1 >> uint256 (-delta - 1)) + 1;

            if (xSignifier >= ySignifier) xSignifier -= ySignifier;
            else {
              xSignifier = ySignifier - xSignifier;
              xSign = ySign;
            }

            if (xSignifier == 0)
              return POSITIVE_ZERO;

            uint256 msb = mostSignificantBit (xSignifier);

            if (msb == 113) {
              xSignifier = xSignifier >> 1 & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
              xExponent += 1;
            } else if (msb < 112) {
              uint256 shift = 112 - msb;
              if (xExponent > shift) {
                xSignifier = xSignifier << shift & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
                xExponent -= shift;
              } else {
                xSignifier <<= xExponent - 1;
                xExponent = 0;
              }
            } else xSignifier &= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

            if (xExponent == 0x7FFF)
              return xSign ? NEGATIVE_INFINITY : POSITIVE_INFINITY;
            else return bytes16 (uint128 (
                (xSign ? 0x80000000000000000000000000000000 : 0) |
                (xExponent << 112) |
                xSignifier));
          }
        }
      }
    }
  }

  /**
   * Calculate x - y.  Special values behave in the following way:
   *
   * NaN - x = NaN for any x.
   * Infinity - x = Infinity for any finite x.
   * -Infinity - x = -Infinity for any finite x.
   * Infinity - -Infinity = Infinity.
   * -Infinity - Infinity = -Infinity.
   * Infinity - Infinity = -Infinity - -Infinity = NaN.
   *
   * @param x quadruple precision number
   * @param y quadruple precision number
   * @return quadruple precision number
   */
  function sub (bytes16 x, bytes16 y) internal pure returns (bytes16) {
    unchecked {
      return add (x, y ^ 0x80000000000000000000000000000000);
    }
  }

  /**
   * Calculate x * y.  Special values behave in the following way:
   *
   * NaN * x = NaN for any x.
   * Infinity * x = Infinity for any finite positive x.
   * Infinity * x = -Infinity for any finite negative x.
   * -Infinity * x = -Infinity for any finite positive x.
   * -Infinity * x = Infinity for any finite negative x.
   * Infinity * 0 = NaN.
   * -Infinity * 0 = NaN.
   * Infinity * Infinity = Infinity.
   * Infinity * -Infinity = -Infinity.
   * -Infinity * Infinity = -Infinity.
   * -Infinity * -Infinity = Infinity.
   *
   * @param x quadruple precision number
   * @param y quadruple precision number
   * @return quadruple precision number
   */
  function mul (bytes16 x, bytes16 y) internal pure returns (bytes16) {
    unchecked {
      uint256 xExponent = uint128 (x) >> 112 & 0x7FFF;
      uint256 yExponent = uint128 (y) >> 112 & 0x7FFF;

      if (xExponent == 0x7FFF) {
        if (yExponent == 0x7FFF) {
          if (x == y) return x ^ y & 0x80000000000000000000000000000000;
          else if (x ^ y == 0x80000000000000000000000000000000) return x | y;
          else return NaN;
        } else {
          if (y & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0) return NaN;
          else return x ^ y & 0x80000000000000000000000000000000;
        }
      } else if (yExponent == 0x7FFF) {
          if (x & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0) return NaN;
          else return y ^ x & 0x80000000000000000000000000000000;
      } else {
        uint256 xSignifier = uint128 (x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
        if (xExponent == 0) xExponent = 1;
        else xSignifier |= 0x10000000000000000000000000000;

        uint256 ySignifier = uint128 (y) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
        if (yExponent == 0) yExponent = 1;
        else ySignifier |= 0x10000000000000000000000000000;

        xSignifier *= ySignifier;
        if (xSignifier == 0)
          return (x ^ y) & 0x80000000000000000000000000000000 > 0 ?
              NEGATIVE_ZERO : POSITIVE_ZERO;

        xExponent += yExponent;

        uint256 msb =
          xSignifier >= 0x200000000000000000000000000000000000000000000000000000000 ? 225 :
          xSignifier >= 0x100000000000000000000000000000000000000000000000000000000 ? 224 :
          mostSignificantBit (xSignifier);

        if (xExponent + msb < 16496) { // Underflow
          xExponent = 0;
          xSignifier = 0;
        } else if (xExponent + msb < 16608) { // Subnormal
          if (xExponent < 16496)
            xSignifier >>= 16496 - xExponent;
          else if (xExponent > 16496)
            xSignifier <<= xExponent - 16496;
          xExponent = 0;
        } else if (xExponent + msb > 49373) {
          xExponent = 0x7FFF;
          xSignifier = 0;
        } else {
          if (msb > 112)
            xSignifier >>= msb - 112;
          else if (msb < 112)
            xSignifier <<= 112 - msb;

          xSignifier &= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

          xExponent = xExponent + msb - 16607;
        }

        return bytes16 (uint128 (uint128 ((x ^ y) & 0x80000000000000000000000000000000) |
            xExponent << 112 | xSignifier));
      }
    }
  }

  /**
   * Calculate x / y.  Special values behave in the following way:
   *
   * NaN / x = NaN for any x.
   * x / NaN = NaN for any x.
   * Infinity / x = Infinity for any finite non-negative x.
   * Infinity / x = -Infinity for any finite negative x including -0.
   * -Infinity / x = -Infinity for any finite non-negative x.
   * -Infinity / x = Infinity for any finite negative x including -0.
   * x / Infinity = 0 for any finite non-negative x.
   * x / -Infinity = -0 for any finite non-negative x.
   * x / Infinity = -0 for any finite non-negative x including -0.
   * x / -Infinity = 0 for any finite non-negative x including -0.
   * 
   * Infinity / Infinity = NaN.
   * Infinity / -Infinity = -NaN.
   * -Infinity / Infinity = -NaN.
   * -Infinity / -Infinity = NaN.
   *
   * Division by zero behaves in the following way:
   *
   * x / 0 = Infinity for any finite positive x.
   * x / -0 = -Infinity for any finite positive x.
   * x / 0 = -Infinity for any finite negative x.
   * x / -0 = Infinity for any finite negative x.
   * 0 / 0 = NaN.
   * 0 / -0 = NaN.
   * -0 / 0 = NaN.
   * -0 / -0 = NaN.
   *
   * @param x quadruple precision number
   * @param y quadruple precision number
   * @return quadruple precision number
   */
  function div (bytes16 x, bytes16 y) internal pure returns (bytes16) {
    unchecked {
      uint256 xExponent = uint128 (x) >> 112 & 0x7FFF;
      uint256 yExponent = uint128 (y) >> 112 & 0x7FFF;

      if (xExponent == 0x7FFF) {
        if (yExponent == 0x7FFF) return NaN;
        else return x ^ y & 0x80000000000000000000000000000000;
      } else if (yExponent == 0x7FFF) {
        if (y & 0x0000FFFFFFFFFFFFFFFFFFFFFFFFFFFF != 0) return NaN;
        else return POSITIVE_ZERO | (x ^ y) & 0x80000000000000000000000000000000;
      } else if (y & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0) {
        if (x & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0) return NaN;
        else return POSITIVE_INFINITY | (x ^ y) & 0x80000000000000000000000000000000;
      } else {
        uint256 ySignifier = uint128 (y) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
        if (yExponent == 0) yExponent = 1;
        else ySignifier |= 0x10000000000000000000000000000;

        uint256 xSignifier = uint128 (x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
        if (xExponent == 0) {
          if (xSignifier != 0) {
            uint shift = 226 - mostSignificantBit (xSignifier);

            xSignifier <<= shift;

            xExponent = 1;
            yExponent += shift - 114;
          }
        }
        else {
          xSignifier = (xSignifier | 0x10000000000000000000000000000) << 114;
        }

        xSignifier = xSignifier / ySignifier;
        if (xSignifier == 0)
          return (x ^ y) & 0x80000000000000000000000000000000 > 0 ?
              NEGATIVE_ZERO : POSITIVE_ZERO;

        assert (xSignifier >= 0x1000000000000000000000000000);

        uint256 msb =
          xSignifier >= 0x80000000000000000000000000000 ? mostSignificantBit (xSignifier) :
          xSignifier >= 0x40000000000000000000000000000 ? 114 :
          xSignifier >= 0x20000000000000000000000000000 ? 113 : 112;

        if (xExponent + msb > yExponent + 16497) { // Overflow
          xExponent = 0x7FFF;
          xSignifier = 0;
        } else if (xExponent + msb + 16380  < yExponent) { // Underflow
          xExponent = 0;
          xSignifier = 0;
        } else if (xExponent + msb + 16268  < yExponent) { // Subnormal
          if (xExponent + 16380 > yExponent)
            xSignifier <<= xExponent + 16380 - yExponent;
          else if (xExponent + 16380 < yExponent)
            xSignifier >>= yExponent - xExponent - 16380;

          xExponent = 0;
        } else { // Normal
          if (msb > 112)
            xSignifier >>= msb - 112;

          xSignifier &= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

          xExponent = xExponent + msb + 16269 - yExponent;
        }

        return bytes16 (uint128 (uint128 ((x ^ y) & 0x80000000000000000000000000000000) |
            xExponent << 112 | xSignifier));
      }
    }
  }

  /**
   * Calculate -x.
   *
   * @param x quadruple precision number
   * @return quadruple precision number
   */
  function neg (bytes16 x) internal pure returns (bytes16) {
    unchecked {
      return x ^ 0x80000000000000000000000000000000;
    }
  }

  /**
   * Calculate |x|.
   *
   * @param x quadruple precision number
   * @return quadruple precision number
   */
  function abs (bytes16 x) internal pure returns (bytes16) {
    unchecked {
      return x & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
    }
  }

  /**
   * Calculate square root of x.  Return NaN on negative x excluding -0.
   *
   * @param x quadruple precision number
   * @return quadruple precision number
   */
  function sqrt (bytes16 x) internal pure returns (bytes16) {
    unchecked {
      if (uint128 (x) >  0x80000000000000000000000000000000) return NaN;
      else {
        uint256 xExponent = uint128 (x) >> 112 & 0x7FFF;
        if (xExponent == 0x7FFF) return x;
        else {
          uint256 xSignifier = uint128 (x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
          if (xExponent == 0) xExponent = 1;
          else xSignifier |= 0x10000000000000000000000000000;

          if (xSignifier == 0) return POSITIVE_ZERO;

          bool oddExponent = xExponent & 0x1 == 0;
          xExponent = xExponent + 16383 >> 1;

          if (oddExponent) {
            if (xSignifier >= 0x10000000000000000000000000000)
              xSignifier <<= 113;
            else {
              uint256 msb = mostSignificantBit (xSignifier);
              uint256 shift = (226 - msb) & 0xFE;
              xSignifier <<= shift;
              xExponent -= shift - 112 >> 1;
            }
          } else {
            if (xSignifier >= 0x10000000000000000000000000000)
              xSignifier <<= 112;
            else {
              uint256 msb = mostSignificantBit (xSignifier);
              uint256 shift = (225 - msb) & 0xFE;
              xSignifier <<= shift;
              xExponent -= shift - 112 >> 1;
            }
          }

          uint256 r = 0x10000000000000000000000000000;
          r = (r + xSignifier / r) >> 1;
          r = (r + xSignifier / r) >> 1;
          r = (r + xSignifier / r) >> 1;
          r = (r + xSignifier / r) >> 1;
          r = (r + xSignifier / r) >> 1;
          r = (r + xSignifier / r) >> 1;
          r = (r + xSignifier / r) >> 1; // Seven iterations should be enough
          uint256 r1 = xSignifier / r;
          if (r1 < r) r = r1;

          return bytes16 (uint128 (xExponent << 112 | r & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF));
        }
      }
    }
  }

  /**
   * Calculate binary logarithm of x.  Return NaN on negative x excluding -0.
   *
   * @param x quadruple precision number
   * @return quadruple precision number
   */
  function log_2 (bytes16 x) internal pure returns (bytes16) {
    unchecked {
      if (uint128 (x) > 0x80000000000000000000000000000000) return NaN;
      else if (x == 0x3FFF0000000000000000000000000000) return POSITIVE_ZERO; 
      else {
        uint256 xExponent = uint128 (x) >> 112 & 0x7FFF;
        if (xExponent == 0x7FFF) return x;
        else {
          uint256 xSignifier = uint128 (x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
          if (xExponent == 0) xExponent = 1;
          else xSignifier |= 0x10000000000000000000000000000;

          if (xSignifier == 0) return NEGATIVE_INFINITY;

          bool resultNegative;
          uint256 resultExponent = 16495;
          uint256 resultSignifier;

          if (xExponent >= 0x3FFF) {
            resultNegative = false;
            resultSignifier = xExponent - 0x3FFF;
            xSignifier <<= 15;
          } else {
            resultNegative = true;
            if (xSignifier >= 0x10000000000000000000000000000) {
              resultSignifier = 0x3FFE - xExponent;
              xSignifier <<= 15;
            } else {
              uint256 msb = mostSignificantBit (xSignifier);
              resultSignifier = 16493 - msb;
              xSignifier <<= 127 - msb;
            }
          }

          if (xSignifier == 0x80000000000000000000000000000000) {
            if (resultNegative) resultSignifier += 1;
            uint256 shift = 112 - mostSignificantBit (resultSignifier);
            resultSignifier <<= shift;
            resultExponent -= shift;
          } else {
            uint256 bb = resultNegative ? 1 : 0;
            while (resultSignifier < 0x10000000000000000000000000000) {
              resultSignifier <<= 1;
              resultExponent -= 1;
  
              xSignifier *= xSignifier;
              uint256 b = xSignifier >> 255;
              resultSignifier += b ^ bb;
              xSignifier >>= 127 + b;
            }
          }

          return bytes16 (uint128 ((resultNegative ? 0x80000000000000000000000000000000 : 0) |
              resultExponent << 112 | resultSignifier & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF));
        }
      }
    }
  }

  /**
   * Calculate natural logarithm of x.  Return NaN on negative x excluding -0.
   *
   * @param x quadruple precision number
   * @return quadruple precision number
   */
  function ln (bytes16 x) internal pure returns (bytes16) {
    unchecked {
      return mul (log_2 (x), 0x3FFE62E42FEFA39EF35793C7673007E5);
    }
  }

  /**
   * Calculate 2^x.
   *
   * @param x quadruple precision number
   * @return quadruple precision number
   */
  function pow_2 (bytes16 x) internal pure returns (bytes16) {
    unchecked {
      bool xNegative = uint128 (x) > 0x80000000000000000000000000000000;
      uint256 xExponent = uint128 (x) >> 112 & 0x7FFF;
      uint256 xSignifier = uint128 (x) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

      if (xExponent == 0x7FFF && xSignifier != 0) return NaN;
      else if (xExponent > 16397)
        return xNegative ? POSITIVE_ZERO : POSITIVE_INFINITY;
      else if (xExponent < 16255)
        return 0x3FFF0000000000000000000000000000;
      else {
        if (xExponent == 0) xExponent = 1;
        else xSignifier |= 0x10000000000000000000000000000;

        if (xExponent > 16367)
          xSignifier <<= xExponent - 16367;
        else if (xExponent < 16367)
          xSignifier >>= 16367 - xExponent;

        if (xNegative && xSignifier > 0x406E00000000000000000000000000000000)
          return POSITIVE_ZERO;

        if (!xNegative && xSignifier > 0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
          return POSITIVE_INFINITY;

        uint256 resultExponent = xSignifier >> 128;
        xSignifier &= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
        if (xNegative && xSignifier != 0) {
          xSignifier = ~xSignifier;
          resultExponent += 1;
        }

        uint256 resultSignifier = 0x80000000000000000000000000000000;
        if (xSignifier & 0x80000000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128;
        if (xSignifier & 0x40000000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128;
        if (xSignifier & 0x20000000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128;
        if (xSignifier & 0x10000000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x10B5586CF9890F6298B92B71842A98363 >> 128;
        if (xSignifier & 0x8000000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x1059B0D31585743AE7C548EB68CA417FD >> 128;
        if (xSignifier & 0x4000000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128;
        if (xSignifier & 0x2000000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128;
        if (xSignifier & 0x1000000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128;
        if (xSignifier & 0x800000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128;
        if (xSignifier & 0x400000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128;
        if (xSignifier & 0x200000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x100162F3904051FA128BCA9C55C31E5DF >> 128;
        if (xSignifier & 0x100000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x1000B175EFFDC76BA38E31671CA939725 >> 128;
        if (xSignifier & 0x80000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128;
        if (xSignifier & 0x40000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128;
        if (xSignifier & 0x20000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x1000162E525EE054754457D5995292026 >> 128;
        if (xSignifier & 0x10000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x10000B17255775C040618BF4A4ADE83FC >> 128;
        if (xSignifier & 0x8000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128;
        if (xSignifier & 0x4000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128;
        if (xSignifier & 0x2000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x10000162E43F4F831060E02D839A9D16D >> 128;
        if (xSignifier & 0x1000000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x100000B1721BCFC99D9F890EA06911763 >> 128;
        if (xSignifier & 0x800000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128;
        if (xSignifier & 0x400000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128;
        if (xSignifier & 0x200000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x100000162E430E5A18F6119E3C02282A5 >> 128;
        if (xSignifier & 0x100000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x1000000B1721835514B86E6D96EFD1BFE >> 128;
        if (xSignifier & 0x80000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128;
        if (xSignifier & 0x40000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x10000002C5C8601CC6B9E94213C72737A >> 128;
        if (xSignifier & 0x20000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x1000000162E42FFF037DF38AA2B219F06 >> 128;
        if (xSignifier & 0x10000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x10000000B17217FBA9C739AA5819F44F9 >> 128;
        if (xSignifier & 0x8000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128;
        if (xSignifier & 0x4000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128;
        if (xSignifier & 0x2000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x10000000162E42FF0999CE3541B9FFFCF >> 128;
        if (xSignifier & 0x1000000000000000000000000 > 0) resultSignifier = resultSignifier * 0x100000000B17217F80F4EF5AADDA45554 >> 128;
        if (xSignifier & 0x800000000000000000000000 > 0) resultSignifier = resultSignifier * 0x10000000058B90BFBF8479BD5A81B51AD >> 128;
        if (xSignifier & 0x400000000000000000000000 > 0) resultSignifier = resultSignifier * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128;
        if (xSignifier & 0x200000000000000000000000 > 0) resultSignifier = resultSignifier * 0x100000000162E42FEFB2FED257559BDAA >> 128;
        if (xSignifier & 0x100000000000000000000000 > 0) resultSignifier = resultSignifier * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128;
        if (xSignifier & 0x80000000000000000000000 > 0) resultSignifier = resultSignifier * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128;
        if (xSignifier & 0x40000000000000000000000 > 0) resultSignifier = resultSignifier * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128;
        if (xSignifier & 0x20000000000000000000000 > 0) resultSignifier = resultSignifier * 0x1000000000162E42FEFA494F1478FDE05 >> 128;
        if (xSignifier & 0x10000000000000000000000 > 0) resultSignifier = resultSignifier * 0x10000000000B17217F7D20CF927C8E94C >> 128;
        if (xSignifier & 0x8000000000000000000000 > 0) resultSignifier = resultSignifier * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128;
        if (xSignifier & 0x4000000000000000000000 > 0) resultSignifier = resultSignifier * 0x100000000002C5C85FDF477B662B26945 >> 128;
        if (xSignifier & 0x2000000000000000000000 > 0) resultSignifier = resultSignifier * 0x10000000000162E42FEFA3AE53369388C >> 128;
        if (xSignifier & 0x1000000000000000000000 > 0) resultSignifier = resultSignifier * 0x100000000000B17217F7D1D351A389D40 >> 128;
        if (xSignifier & 0x800000000000000000000 > 0) resultSignifier = resultSignifier * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128;
        if (xSignifier & 0x400000000000000000000 > 0) resultSignifier = resultSignifier * 0x1000000000002C5C85FDF4741BEA6E77E >> 128;
        if (xSignifier & 0x200000000000000000000 > 0) resultSignifier = resultSignifier * 0x100000000000162E42FEFA39FE95583C2 >> 128;
        if (xSignifier & 0x100000000000000000000 > 0) resultSignifier = resultSignifier * 0x1000000000000B17217F7D1CFB72B45E1 >> 128;
        if (xSignifier & 0x80000000000000000000 > 0) resultSignifier = resultSignifier * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128;
        if (xSignifier & 0x40000000000000000000 > 0) resultSignifier = resultSignifier * 0x10000000000002C5C85FDF473E242EA38 >> 128;
        if (xSignifier & 0x20000000000000000000 > 0) resultSignifier = resultSignifier * 0x1000000000000162E42FEFA39F02B772C >> 128;
        if (xSignifier & 0x10000000000000000000 > 0) resultSignifier = resultSignifier * 0x10000000000000B17217F7D1CF7D83C1A >> 128;
        if (xSignifier & 0x8000000000000000000 > 0) resultSignifier = resultSignifier * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128;
        if (xSignifier & 0x4000000000000000000 > 0) resultSignifier = resultSignifier * 0x100000000000002C5C85FDF473DEA871F >> 128;
        if (xSignifier & 0x2000000000000000000 > 0) resultSignifier = resultSignifier * 0x10000000000000162E42FEFA39EF44D91 >> 128;
        if (xSignifier & 0x1000000000000000000 > 0) resultSignifier = resultSignifier * 0x100000000000000B17217F7D1CF79E949 >> 128;
        if (xSignifier & 0x800000000000000000 > 0) resultSignifier = resultSignifier * 0x10000000000000058B90BFBE8E7BCE544 >> 128;
        if (xSignifier & 0x400000000000000000 > 0) resultSignifier = resultSignifier * 0x1000000000000002C5C85FDF473DE6ECA >> 128;
        if (xSignifier & 0x200000000000000000 > 0) resultSignifier = resultSignifier * 0x100000000000000162E42FEFA39EF366F >> 128;
        if (xSignifier & 0x100000000000000000 > 0) resultSignifier = resultSignifier * 0x1000000000000000B17217F7D1CF79AFA >> 128;
        if (xSignifier & 0x80000000000000000 > 0) resultSignifier = resultSignifier * 0x100000000000000058B90BFBE8E7BCD6D >> 128;
        if (xSignifier & 0x40000000000000000 > 0) resultSignifier = resultSignifier * 0x10000000000000002C5C85FDF473DE6B2 >> 128;
        if (xSignifier & 0x20000000000000000 > 0) resultSignifier = resultSignifier * 0x1000000000000000162E42FEFA39EF358 >> 128;
        if (xSignifier & 0x10000000000000000 > 0) resultSignifier = resultSignifier * 0x10000000000000000B17217F7D1CF79AB >> 128;
        if (xSignifier & 0x8000000000000000 > 0) resultSignifier = resultSignifier * 0x1000000000000000058B90BFBE8E7BCD5 >> 128;
        if (xSignifier & 0x4000000000000000 > 0) resultSignifier = resultSignifier * 0x100000000000000002C5C85FDF473DE6A >> 128;
        if (xSignifier & 0x2000000000000000 > 0) resultSignifier = resultSignifier * 0x10000000000000000162E42FEFA39EF34 >> 128;
        if (xSignifier & 0x1000000000000000 > 0) resultSignifier = resultSignifier * 0x100000000000000000B17217F7D1CF799 >> 128;
        if (xSignifier & 0x800000000000000 > 0) resultSignifier = resultSignifier * 0x10000000000000000058B90BFBE8E7BCC >> 128;
        if (xSignifier & 0x400000000000000 > 0) resultSignifier = resultSignifier * 0x1000000000000000002C5C85FDF473DE5 >> 128;
        if (xSignifier & 0x200000000000000 > 0) resultSignifier = resultSignifier * 0x100000000000000000162E42FEFA39EF2 >> 128;
        if (xSignifier & 0x100000000000000 > 0) resultSignifier = resultSignifier * 0x1000000000000000000B17217F7D1CF78 >> 128;
        if (xSignifier & 0x80000000000000 > 0) resultSignifier = resultSignifier * 0x100000000000000000058B90BFBE8E7BB >> 128;
        if (xSignifier & 0x40000000000000 > 0) resultSignifier = resultSignifier * 0x10000000000000000002C5C85FDF473DD >> 128;
        if (xSignifier & 0x20000000000000 > 0) resultSignifier = resultSignifier * 0x1000000000000000000162E42FEFA39EE >> 128;
        if (xSignifier & 0x10000000000000 > 0) resultSignifier = resultSignifier * 0x10000000000000000000B17217F7D1CF6 >> 128;
        if (xSignifier & 0x8000000000000 > 0) resultSignifier = resultSignifier * 0x1000000000000000000058B90BFBE8E7A >> 128;
        if (xSignifier & 0x4000000000000 > 0) resultSignifier = resultSignifier * 0x100000000000000000002C5C85FDF473C >> 128;
        if (xSignifier & 0x2000000000000 > 0) resultSignifier = resultSignifier * 0x10000000000000000000162E42FEFA39D >> 128;
        if (xSignifier & 0x1000000000000 > 0) resultSignifier = resultSignifier * 0x100000000000000000000B17217F7D1CE >> 128;
        if (xSignifier & 0x800000000000 > 0) resultSignifier = resultSignifier * 0x10000000000000000000058B90BFBE8E6 >> 128;
        if (xSignifier & 0x400000000000 > 0) resultSignifier = resultSignifier * 0x1000000000000000000002C5C85FDF472 >> 128;
        if (xSignifier & 0x200000000000 > 0) resultSignifier = resultSignifier * 0x100000000000000000000162E42FEFA38 >> 128;
        if (xSignifier & 0x100000000000 > 0) resultSignifier = resultSignifier * 0x1000000000000000000000B17217F7D1B >> 128;
        if (xSignifier & 0x80000000000 > 0) resultSignifier = resultSignifier * 0x100000000000000000000058B90BFBE8D >> 128;
        if (xSignifier & 0x40000000000 > 0) resultSignifier = resultSignifier * 0x10000000000000000000002C5C85FDF46 >> 128;
        if (xSignifier & 0x20000000000 > 0) resultSignifier = resultSignifier * 0x1000000000000000000000162E42FEFA2 >> 128;
        if (xSignifier & 0x10000000000 > 0) resultSignifier = resultSignifier * 0x10000000000000000000000B17217F7D0 >> 128;
        if (xSignifier & 0x8000000000 > 0) resultSignifier = resultSignifier * 0x1000000000000000000000058B90BFBE7 >> 128;
        if (xSignifier & 0x4000000000 > 0) resultSignifier = resultSignifier * 0x100000000000000000000002C5C85FDF3 >> 128;
        if (xSignifier & 0x2000000000 > 0) resultSignifier = resultSignifier * 0x10000000000000000000000162E42FEF9 >> 128;
        if (xSignifier & 0x1000000000 > 0) resultSignifier = resultSignifier * 0x100000000000000000000000B17217F7C >> 128;
        if (xSignifier & 0x800000000 > 0) resultSignifier = resultSignifier * 0x10000000000000000000000058B90BFBD >> 128;
        if (xSignifier & 0x400000000 > 0) resultSignifier = resultSignifier * 0x1000000000000000000000002C5C85FDE >> 128;
        if (xSignifier & 0x200000000 > 0) resultSignifier = resultSignifier * 0x100000000000000000000000162E42FEE >> 128;
        if (xSignifier & 0x100000000 > 0) resultSignifier = resultSignifier * 0x1000000000000000000000000B17217F6 >> 128;
        if (xSignifier & 0x80000000 > 0) resultSignifier = resultSignifier * 0x100000000000000000000000058B90BFA >> 128;
        if (xSignifier & 0x40000000 > 0) resultSignifier = resultSignifier * 0x10000000000000000000000002C5C85FC >> 128;
        if (xSignifier & 0x20000000 > 0) resultSignifier = resultSignifier * 0x1000000000000000000000000162E42FD >> 128;
        if (xSignifier & 0x10000000 > 0) resultSignifier = resultSignifier * 0x10000000000000000000000000B17217E >> 128;
        if (xSignifier & 0x8000000 > 0) resultSignifier = resultSignifier * 0x1000000000000000000000000058B90BE >> 128;
        if (xSignifier & 0x4000000 > 0) resultSignifier = resultSignifier * 0x100000000000000000000000002C5C85E >> 128;
        if (xSignifier & 0x2000000 > 0) resultSignifier = resultSignifier * 0x10000000000000000000000000162E42E >> 128;
        if (xSignifier & 0x1000000 > 0) resultSignifier = resultSignifier * 0x100000000000000000000000000B17216 >> 128;
        if (xSignifier & 0x800000 > 0) resultSignifier = resultSignifier * 0x10000000000000000000000000058B90A >> 128;
        if (xSignifier & 0x400000 > 0) resultSignifier = resultSignifier * 0x1000000000000000000000000002C5C84 >> 128;
        if (xSignifier & 0x200000 > 0) resultSignifier = resultSignifier * 0x100000000000000000000000000162E41 >> 128;
        if (xSignifier & 0x100000 > 0) resultSignifier = resultSignifier * 0x1000000000000000000000000000B1720 >> 128;
        if (xSignifier & 0x80000 > 0) resultSignifier = resultSignifier * 0x100000000000000000000000000058B8F >> 128;
        if (xSignifier & 0x40000 > 0) resultSignifier = resultSignifier * 0x10000000000000000000000000002C5C7 >> 128;
        if (xSignifier & 0x20000 > 0) resultSignifier = resultSignifier * 0x1000000000000000000000000000162E3 >> 128;
        if (xSignifier & 0x10000 > 0) resultSignifier = resultSignifier * 0x10000000000000000000000000000B171 >> 128;
        if (xSignifier & 0x8000 > 0) resultSignifier = resultSignifier * 0x1000000000000000000000000000058B8 >> 128;
        if (xSignifier & 0x4000 > 0) resultSignifier = resultSignifier * 0x100000000000000000000000000002C5B >> 128;
        if (xSignifier & 0x2000 > 0) resultSignifier = resultSignifier * 0x10000000000000000000000000000162D >> 128;
        if (xSignifier & 0x1000 > 0) resultSignifier = resultSignifier * 0x100000000000000000000000000000B16 >> 128;
        if (xSignifier & 0x800 > 0) resultSignifier = resultSignifier * 0x10000000000000000000000000000058A >> 128;
        if (xSignifier & 0x400 > 0) resultSignifier = resultSignifier * 0x1000000000000000000000000000002C4 >> 128;
        if (xSignifier & 0x200 > 0) resultSignifier = resultSignifier * 0x100000000000000000000000000000161 >> 128;
        if (xSignifier & 0x100 > 0) resultSignifier = resultSignifier * 0x1000000000000000000000000000000B0 >> 128;
        if (xSignifier & 0x80 > 0) resultSignifier = resultSignifier * 0x100000000000000000000000000000057 >> 128;
        if (xSignifier & 0x40 > 0) resultSignifier = resultSignifier * 0x10000000000000000000000000000002B >> 128;
        if (xSignifier & 0x20 > 0) resultSignifier = resultSignifier * 0x100000000000000000000000000000015 >> 128;
        if (xSignifier & 0x10 > 0) resultSignifier = resultSignifier * 0x10000000000000000000000000000000A >> 128;
        if (xSignifier & 0x8 > 0) resultSignifier = resultSignifier * 0x100000000000000000000000000000004 >> 128;
        if (xSignifier & 0x4 > 0) resultSignifier = resultSignifier * 0x100000000000000000000000000000001 >> 128;

        if (!xNegative) {
          resultSignifier = resultSignifier >> 15 & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
          resultExponent += 0x3FFF;
        } else if (resultExponent <= 0x3FFE) {
          resultSignifier = resultSignifier >> 15 & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
          resultExponent = 0x3FFF - resultExponent;
        } else {
          resultSignifier = resultSignifier >> resultExponent - 16367;
          resultExponent = 0;
        }

        return bytes16 (uint128 (resultExponent << 112 | resultSignifier));
      }
    }
  }

  /**
   * Calculate e^x.
   *
   * @param x quadruple precision number
   * @return quadruple precision number
   */
  function exp (bytes16 x) internal pure returns (bytes16) {
    unchecked {
      return pow_2 (mul (x, 0x3FFF71547652B82FE1777D0FFDA0D23A));
    }
  }

  /**
   * Get index of the most significant non-zero bit in binary representation of
   * x.  Reverts if x is zero.
   *
   * @return index of the most significant non-zero bit in binary representation
   *         of x
   */
  function mostSignificantBit (uint256 x) private pure returns (uint256) {
    unchecked {
      require (x > 0);

      uint256 result = 0;

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

      return result;
    }
  }
}

File 12 of 15 : AggregatorV2V3Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./AggregatorInterface.sol";
import "./AggregatorV3Interface.sol";

interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface
{
}

File 13 of 15 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 14 of 15 : AggregatorInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AggregatorInterface {
  function latestAnswer()
    external
    view
    returns (
      int256
    );
  
  function latestTimestamp()
    external
    view
    returns (
      uint256
    );

  function latestRound()
    external
    view
    returns (
      uint256
    );

  function getAnswer(
    uint256 roundId
  )
    external
    view
    returns (
      int256
    );

  function getTimestamp(
    uint256 roundId
  )
    external
    view
    returns (
      uint256
    );

  event AnswerUpdated(
    int256 indexed current,
    uint256 indexed roundId,
    uint256 updatedAt
  );

  event NewRound(
    uint256 indexed roundId,
    address indexed startedBy,
    uint256 startedAt
  );
}

File 15 of 15 : AggregatorV3Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AggregatorV3Interface {

  function decimals()
    external
    view
    returns (
      uint8
    );

  function description()
    external
    view
    returns (
      string memory
    );

  function version()
    external
    view
    returns (
      uint256
    );

  // getRoundData and latestRoundData should both raise "No data present"
  // if they do not have data to report, instead of returning unset values
  // which could be misinterpreted as actual reported values.
  function getRoundData(
    uint80 _roundId
  )
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {
    "contracts/implementation/PoolSwapLibrary.sol": {
      "PoolSwapLibrary": "0x542848e66d8f387a78717be7b39f7259b7782bae"
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_factory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_pool","type":"address"},{"indexed":true,"internalType":"address","name":"keeper","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"KeeperPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_pool","type":"address"},{"indexed":true,"internalType":"address","name":"keeper","type":"address"},{"indexed":false,"internalType":"uint256","name":"expectedReward","type":"uint256"}],"name":"KeeperPaymentError","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"poolAddress","type":"address"},{"indexed":true,"internalType":"int256","name":"firstPrice","type":"int256"}],"name":"PoolAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"string","name":"reason","type":"string"}],"name":"PoolUpkeepError","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":true,"internalType":"int256","name":"startPrice","type":"int256"},{"indexed":true,"internalType":"int256","name":"endPrice","type":"int256"}],"name":"UpkeepSuccessful","type":"event"},{"inputs":[],"name":"BASE_TIP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BLOCK_TIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_DECIMALS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TIP_DELTA_PER_BLOCK","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_pools","type":"address[]"}],"name":"checkUpkeepMultiplePools","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"}],"name":"checkUpkeepSinglePool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"executionPrice","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract IPoolFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gasPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"},{"internalType":"uint256","name":"_gasPrice","type":"uint256"},{"internalType":"uint256","name":"_gasSpent","type":"uint256"}],"name":"keeperGas","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"},{"internalType":"uint256","name":"_gasPrice","type":"uint256"},{"internalType":"uint256","name":"_gasSpent","type":"uint256"},{"internalType":"uint256","name":"_savedPreviousUpdatedTimestamp","type":"uint256"},{"internalType":"uint256","name":"_poolInterval","type":"uint256"}],"name":"keeperReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_savedPreviousUpdatedTimestamp","type":"uint256"},{"internalType":"uint256","name":"_poolInterval","type":"uint256"}],"name":"keeperTip","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_poolAddress","type":"address"}],"name":"newPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"pools","type":"address[]"}],"name":"performUpkeepMultiplePools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"}],"name":"performUpkeepSinglePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_factory","type":"address"}],"name":"setFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"setGasPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040526402540be4006003553480156200001a57600080fd5b50604051620020d5380380620020d58339810160408190526200003d9162000119565b6200004833620000c9565b6001600160a01b038116620000a35760405162461bcd60e51b815260206004820152601b60248201527f466163746f72792063616e6e6f74206265203020616464726573730000000000604482015260640160405180910390fd5b600280546001600160a01b0319166001600160a01b03929092169190911790556200014b565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156200012c57600080fd5b81516001600160a01b03811681146200014457600080fd5b9392505050565b611f7a806200015b6000396000f3fe608060405234801561001057600080fd5b506004361061012c5760003560e01c8063b3fbb7c4116100ad578063ce1dcdfc11610071578063ce1dcdfc1461025e578063f2fde38b14610271578063f930191014610284578063fcab9e981461028c578063fe173b971461029f57600080fd5b8063b3fbb7c4146101f2578063b9c6536514610205578063bf1fe42014610218578063c45a01551461022b578063cdd9e1371461023e57600080fd5b80638da5cb5b116100f45780638da5cb5b1461019f57806391a60782146101c45780639f9a3d95146101d7578063a40d6b80146101df578063aea8acb1146101d757600080fd5b80630417cf8e14610131578063225739b11461014c5780633c5c7fb31461016f5780635bb4780814610184578063715018a614610197575b600080fd5b610139601281565b6040519081526020015b60405180910390f35b61015f61015a366004611a4c565b6102a8565b6040519015158152602001610143565b61018261017d366004611a4c565b6103a9565b005b610182610192366004611a4c565b6105b7565b610182610603565b6000546001600160a01b03165b6040516001600160a01b039091168152602001610143565b6101826101d2366004611a4c565b610639565b610139600581565b6101396101ed366004611a86565b61083b565b610139610200366004611abb565b610988565b610139610213366004611c7a565b610b72565b610182610226366004611c61565b610bce565b6002546101ac906001600160a01b031681565b61013961024c366004611a4c565b60016020526000908152604090205481565b61015f61026c366004611aff565b610bfd565b61018261027f366004611a4c565b610c5e565b610139600d81565b61018261029a366004611aff565b610cf9565b61013960035481565b600254604051635ab78ee160e01b81526001600160a01b0383811660048301526000921690635ab78ee19060240160206040518083038186803b1580156102ee57600080fd5b505afa158015610302573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103269190611b74565b61033257506000919050565b816001600160a01b0316637bbf10336040518163ffffffff1660e01b815260040160206040518083038186803b15801561036b57600080fd5b505afa15801561037f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103a39190611b74565b92915050565b6002546001600160a01b031633146103fd5760405162461bcd60e51b815260206004820152601260248201527143616c6c6572206e6f7420666163746f727960701b60448201526064015b60405180910390fd5b6000816001600160a01b031663b9ed8abf6040518163ffffffff1660e01b815260040160206040518083038186803b15801561043857600080fd5b505afa15801561044c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104709190611a69565b90506000816001600160a01b03166398d5fdca6040518163ffffffff1660e01b815260040160206040518083038186803b1580156104ad57600080fd5b505afa1580156104c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104e59190611b96565b9050600081136105375760405162461bcd60e51b815260206004820152601b60248201527f4669727374207072696365206973206e6f6e2d706f736974697665000000000060448201526064016103f4565b600061055d61055861054884610d45565b668075782dace9d960c71b610dd3565b611050565b905081846001600160a01b03167fe950cb32f7a787627d713d7365623fef2d762ef8fcf813b865a04350852deb9b60405160405180910390a36001600160a01b039093166000908152600160205260409020929092555050565b6000546001600160a01b031633146105e15760405162461bcd60e51b81526004016103f490611cfe565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b0316331461062d5760405162461bcd60e51b81526004016103f490611cfe565b61063760006110f7565b565b60005a9050610647826102a8565b61064f575050565b6000829050600080600080846001600160a01b03166304883c276040518163ffffffff1660e01b815260040160006040518083038186803b15801561069357600080fd5b505afa1580156106a7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106cf9190810190611baf565b6001600160a01b038b81166000908152600160205260409081902080549087905590516303d8ce9d60e61b81526004810182905260248101879052959950939750919550935090919087169063f633a74090604401600060405180830381600087803b15801561073e57600080fd5b505af192505050801561074f575060015b6107ce5761075b611e89565b806308c379a014156107c25750610770611ea5565b8061077b57506107c4565b886001600160a01b03167f8a47468bf96478429d382ed6385d9c77fba8b89540cd88199b1b51d5723b11fa826040516107b49190611ceb565b60405180910390a250610831565b505b3d6000803e3d6000fd5b60005a6107db9089611d8c565b90506107ec89600354838787611147565b85828a6001600160a01b03167f0aa8d2838ad6d4e2a5d7ca0826dfdbc36d922adf8fe5c6d25aee2724b07de074886040516108279190611ceb565b60405180910390a4505b5050505050505050565b600080846001600160a01b0316638226f3966040518163ffffffff1660e01b815260040160206040518083038186803b15801561087757600080fd5b505afa15801561088b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108af9190611a69565b6001600160a01b03166398d5fdca6040518163ffffffff1660e01b815260040160206040518083038186803b1580156108e757600080fd5b505afa1580156108fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061091f9190611b96565b905060008113610933576000915050610981565b60006109476109428587611d6d565b61126c565b905060006109548361126c565b905061097b6109766109668484610dd3565b668075782dace9d960c71b6112d0565b611566565b93505050505b9392505050565b60008061099687878761083b565b905060006109a76109428686610b72565b90506109bc816109b7606461126c565b6112d0565b905060006109e96105586109cf8561126c565b6109e46109666109de8861126c565b87610dd3565b6115e9565b90506000896001600160a01b031663217a4b706040518163ffffffff1660e01b815260040160206040518083038186803b158015610a2657600080fd5b505afa158015610a3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a5e9190611a69565b6001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610a9657600080fd5b505afa158015610aaa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ace9190611c9c565b60405163bf36f0e960e01b81526004810184905260ff9190911660248201819052915060009073542848e66d8f387a78717be7b39f7259b7782bae9063bf36f0e99060440160206040518083038186803b158015610b2b57600080fd5b505af4158015610b3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b639190611b96565b9b9a5050505050505050505050565b600080610b7f8385611d33565b610b899042611d8c565b90506000600d610b9a836005611d6d565b610ba49190611d4b565b610baf906005611d33565b90506064811115610bc5576064925050506103a3565b91506103a39050565b6000546001600160a01b03163314610bf85760405162461bcd60e51b81526004016103f490611cfe565b600355565b6000805b82811015610c5457610c33848483818110610c1e57610c1e611e5d565b905060200201602081019061015a9190611a4c565b15610c425760019150506103a3565b80610c4c81611e00565b915050610c01565b5060009392505050565b6000546001600160a01b03163314610c885760405162461bcd60e51b81526004016103f490611cfe565b6001600160a01b038116610ced5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016103f4565b610cf6816110f7565b50565b60005b81811015610d4057610d2e838383818110610d1957610d19611e5d565b90506020020160208101906101d29190611a4c565b80610d3881611e00565b915050610cfc565b505050565b600081610d5457506000919050565b6000808313610d665782600003610d68565b825b90506000610d75826119a9565b90506070811015610d8e578060700382901b9150610da1565b6070811115610da1576070810382901c91505b6001600160701b03909116613fff820160701b17906000841215610dc9576001607f1b821791505b5060801b92915050565b6000617fff60f084811c8216919084901c811690821415610e835780617fff1415610e51576001600160801b03198581169085161415610e1e57505050600160ff1b811682186103a3565b600160ff1b6001600160801b0319868618161415610e41575050508181176103a3565b5061ffff60ef1b91506103a39050565b600160801b600160ff1b038416610e72575061ffff60ef1b91506103a39050565b505050600160ff1b811682186103a3565b80617fff1415610ebf57600160801b600160ff1b038516610eae575061ffff60ef1b91506103a39050565b505050600160ff1b821681186103a3565b6001600160701b03608086901c1682610edb5760019250610ee2565b600160701b175b6001600160701b03608086901c1682610efe5760019250610f05565b600160701b175b9081029081610f3357600160ff1b87871816610f22576000610f28565b600160ff1b5b9450505050506103a3565b928201926000600160e11b831015610f6657600160e01b831015610f5f57610f5a836119a9565b610f69565b60e0610f69565b60e15b90506140708186011015610f84576000945060009250611021565b6140e08186011015610fc757614070851015610fa957846140700383901c9250610fbe565b614070851115610fbe57614070850383901b92505b60009450611021565b61c0dd8186011115610fe157617fff945060009250611021565b6070811115610ff8576070810383901c925061100b565b607081101561100b578060700383901b92505b6001600160701b03831692506140df8186010394505b82607086901b888a186001607f1b60801b1660801c6001600160801b0316171760801b955050505050506103a3565b6000617fff60f083901c166140fe81111561106a57600080fd5b613fff81101561107d5750600092915050565b600160701b6001600160701b03608085901c161761406f8210156110a75761406f8290031c6110b9565b61406f8211156110b95761406e1982011b5b6001607f1b608085901c106110e357600160ff1b8111156110d957600080fd5b6000039392505050565b6001600160ff1b0381111561098157600080fd5b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006111568686868686610988565b604051636dc2b27160e01b8152336004820152602481018290529091506001600160a01b03871690636dc2b27190604401602060405180830381600087803b1580156111a157600080fd5b505af11580156111b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111d99190611b74565b156112235760405181815233906001600160a01b038816907ff03012dd4aab142682aa01439f94e975e2ec77ef558352323bc05f382f74b0829060200160405180910390a3611264565b60405181815233906001600160a01b038816907f08b35d4c86707ba44cdcc5efaa79b8a307e6c7f789f1eb86ca1ff1e9bde056979060200160405180910390a35b505050505050565b60008161127b57506000919050565b816000611287826119a9565b905060708110156112a0578060700382901b91506112b3565b60708111156112b3576070810382901c91505b613fff0160701b6001600160701b03919091161760801b92915050565b6000617fff60f084811c8216919084901c8116908214156113055780617fff1415610e72575061ffff60ef1b91506103a39050565b80617fff1415611349576dffffffffffffffffffffffffffff60801b841615611338575061ffff60ef1b91506103a39050565b505050808218600160ff1b166103a3565b600160801b600160ff1b03841661139357600160801b600160ff1b03851661137b575061ffff60ef1b91506103a39050565b505050808218600160ff1b16617fff60f01b176103a3565b6001600160701b03608085901c16816113af57600191506113b6565b600160701b175b6001600160701b03608087901c16836113f55780156113f05760006113da826119a9565b6001955060e20393840160711901939190911b90505b6113ff565b600160701b1760721b5b81818161140e5761140e611e47565b0490508061142a57600160ff1b87871816610f22576000610f28565b6001606c1b81101561143e5761143e611e1b565b6000600160731b82101561147d57600160721b82101561147257600160711b82101561146b576070611475565b6071611475565b60725b60ff16611486565b611486826119a9565b9050836140710181860111156114a457617fff945060009150611537565b83818601613ffc0110156114bf576000945060009150611537565b83818601613f8c01101561150c578385613ffc0111156114ea578385613ffc010382901b9150611503565b8385613ffc01101561150357613ffc8585030382901c91505b60009450611537565b607081111561151f576070810382901c91505b6001600160701b038216915083818601613f8d010394505b81607086901b888a186001607f1b60801b1660801c6001600160801b0316171760801b955050505050506103a3565b6000617fff60f083901c16613fff8110156115845750600092915050565b6001607f1b608084901c1061159857600080fd5b6140fe8111156115a757600080fd5b600160701b6001600160701b03608085901c161761406f8210156115d15761406f8290031c610981565b61406f8211156109815761406e1982011b9392505050565b6000617fff60f084811c8216919084901c8116908214156116375780617fff141561162d576001600160801b03198581169085161415610e415784925050506103a3565b84925050506103a3565b80617fff141561164b5783925050506103a3565b6001607f1b608086901c90811015906001600160701b0316836116715760019350611678565b600160701b175b6001607f1b608087901c90811015906001600160701b03168461169e57600194506116a5565b600160701b175b826116d5576001600160801b03198816600160ff1b146116c557876116c8565b60005b96505050505050506103a3565b806116f5576001600160801b03198916600160ff1b146116c557886116c8565b848603841515831515141561180057607081131561171c57899750505050505050506103a3565b600081131561172e5790811c9061175d565b606f1981121561174757889750505050505050506103a3565b600081121561175d578060000384901c93508596505b92810192600160711b8410611778576001968701969390931c925b86617fff14156117aa578461179257617fff60f01b61179c565b6001600160f01b03195b9750505050505050506103a3565b600160701b8410156117bf57600096506117cc565b6001600160701b03841693505b83607088901b866117de5760006117e4565b6001607f1b5b6001600160801b0316171760801b9750505050505050506103a3565b600081131561181b57600184901b9350600187039650611832565b600081121561183257600182901b91506001860396505b60708113156118445760019150611891565b6001811315611861576001810360018303901c6001019150611891565b606f198112156118745760019350611891565b600019811215611891576001816000030360018503901c60010193505b8184106118a25781840393506118ab565b83820393508294505b836118c15750600096506103a395505050505050565b60006118cc856119a9565b905080607114156118f257600185901c6001600160701b03169450600188019750611941565b607081101561193457607081900380891115611921578086901b6001600160701b03169550808903985061192e565b600098600019019590951b945b50611941565b6001600160701b03851694505b87617fff1415611974578561195b57617fff60f01b611965565b6001600160f01b03195b985050505050505050506103a3565b84607089901b8761198657600061198c565b6001607f1b5b6001600160801b0316171760801b985050505050505050506103a3565b60008082116119b757600080fd5b6000600160801b83106119cc57608092831c92015b6801000000000000000083106119e457604092831c92015b64010000000083106119f857602092831c92015b620100008310611a0a57601092831c92015b6101008310611a1b57600892831c92015b60108310611a2b57600492831c92015b60048310611a3b57600292831c92015b600283106103a35760010192915050565b600060208284031215611a5e57600080fd5b813561098181611f2f565b600060208284031215611a7b57600080fd5b815161098181611f2f565b600080600060608486031215611a9b57600080fd5b8335611aa681611f2f565b95602085013595506040909401359392505050565b600080600080600060a08688031215611ad357600080fd5b8535611ade81611f2f565b97602087013597506040870135966060810135965060800135945092505050565b60008060208385031215611b1257600080fd5b823567ffffffffffffffff80821115611b2a57600080fd5b818501915085601f830112611b3e57600080fd5b813581811115611b4d57600080fd5b8660208260051b8501011115611b6257600080fd5b60209290920196919550909350505050565b600060208284031215611b8657600080fd5b8151801515811461098157600080fd5b600060208284031215611ba857600080fd5b5051919050565b60008060008060808587031215611bc557600080fd5b84519350602085015167ffffffffffffffff80821115611be457600080fd5b818701915087601f830112611bf857600080fd5b815181811115611c0a57611c0a611e73565b6040519150611c23601f8201601f191660200183611dd3565b808252886020828501011115611c3857600080fd5b611c49816020840160208601611da3565b50604087015160609097015195989097509350505050565b600060208284031215611c7357600080fd5b5035919050565b60008060408385031215611c8d57600080fd5b50508035926020909101359150565b600060208284031215611cae57600080fd5b815160ff8116811461098157600080fd5b60008151808452611cd7816020860160208601611da3565b601f01601f19169290920160200192915050565b6020815260006109816020830184611cbf565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60008219821115611d4657611d46611e31565b500190565b600082611d6857634e487b7160e01b600052601260045260246000fd5b500490565b6000816000190483118215151615611d8757611d87611e31565b500290565b600082821015611d9e57611d9e611e31565b500390565b60005b83811015611dbe578181015183820152602001611da6565b83811115611dcd576000848401525b50505050565b601f8201601f1916810167ffffffffffffffff81118282101715611df957611df9611e73565b6040525050565b6000600019821415611e1457611e14611e31565b5060010190565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b600060033d1115611ea25760046000803e5060005160e01c5b90565b600060443d1015611eb35790565b6040516003193d81016004833e81513d67ffffffffffffffff8160248401118184111715611ee357505050505090565b8285019150815181811115611efb5750505050505090565b843d8701016020828501011115611f155750505050505090565b611f2460208286010187611dd3565b509095945050505050565b6001600160a01b0381168114610cf657600080fdfea26469706673582212202e1a80ff10a4c06d30d3b609f5014ec1b01c0a9cbd27f0aab71c3a5b7c2a996264736f6c6343000807003300000000000000000000000098c58c1ceb01e198f8356763d5cba8eb7b11e4e2

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061012c5760003560e01c8063b3fbb7c4116100ad578063ce1dcdfc11610071578063ce1dcdfc1461025e578063f2fde38b14610271578063f930191014610284578063fcab9e981461028c578063fe173b971461029f57600080fd5b8063b3fbb7c4146101f2578063b9c6536514610205578063bf1fe42014610218578063c45a01551461022b578063cdd9e1371461023e57600080fd5b80638da5cb5b116100f45780638da5cb5b1461019f57806391a60782146101c45780639f9a3d95146101d7578063a40d6b80146101df578063aea8acb1146101d757600080fd5b80630417cf8e14610131578063225739b11461014c5780633c5c7fb31461016f5780635bb4780814610184578063715018a614610197575b600080fd5b610139601281565b6040519081526020015b60405180910390f35b61015f61015a366004611a4c565b6102a8565b6040519015158152602001610143565b61018261017d366004611a4c565b6103a9565b005b610182610192366004611a4c565b6105b7565b610182610603565b6000546001600160a01b03165b6040516001600160a01b039091168152602001610143565b6101826101d2366004611a4c565b610639565b610139600581565b6101396101ed366004611a86565b61083b565b610139610200366004611abb565b610988565b610139610213366004611c7a565b610b72565b610182610226366004611c61565b610bce565b6002546101ac906001600160a01b031681565b61013961024c366004611a4c565b60016020526000908152604090205481565b61015f61026c366004611aff565b610bfd565b61018261027f366004611a4c565b610c5e565b610139600d81565b61018261029a366004611aff565b610cf9565b61013960035481565b600254604051635ab78ee160e01b81526001600160a01b0383811660048301526000921690635ab78ee19060240160206040518083038186803b1580156102ee57600080fd5b505afa158015610302573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103269190611b74565b61033257506000919050565b816001600160a01b0316637bbf10336040518163ffffffff1660e01b815260040160206040518083038186803b15801561036b57600080fd5b505afa15801561037f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103a39190611b74565b92915050565b6002546001600160a01b031633146103fd5760405162461bcd60e51b815260206004820152601260248201527143616c6c6572206e6f7420666163746f727960701b60448201526064015b60405180910390fd5b6000816001600160a01b031663b9ed8abf6040518163ffffffff1660e01b815260040160206040518083038186803b15801561043857600080fd5b505afa15801561044c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104709190611a69565b90506000816001600160a01b03166398d5fdca6040518163ffffffff1660e01b815260040160206040518083038186803b1580156104ad57600080fd5b505afa1580156104c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104e59190611b96565b9050600081136105375760405162461bcd60e51b815260206004820152601b60248201527f4669727374207072696365206973206e6f6e2d706f736974697665000000000060448201526064016103f4565b600061055d61055861054884610d45565b668075782dace9d960c71b610dd3565b611050565b905081846001600160a01b03167fe950cb32f7a787627d713d7365623fef2d762ef8fcf813b865a04350852deb9b60405160405180910390a36001600160a01b039093166000908152600160205260409020929092555050565b6000546001600160a01b031633146105e15760405162461bcd60e51b81526004016103f490611cfe565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b0316331461062d5760405162461bcd60e51b81526004016103f490611cfe565b61063760006110f7565b565b60005a9050610647826102a8565b61064f575050565b6000829050600080600080846001600160a01b03166304883c276040518163ffffffff1660e01b815260040160006040518083038186803b15801561069357600080fd5b505afa1580156106a7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106cf9190810190611baf565b6001600160a01b038b81166000908152600160205260409081902080549087905590516303d8ce9d60e61b81526004810182905260248101879052959950939750919550935090919087169063f633a74090604401600060405180830381600087803b15801561073e57600080fd5b505af192505050801561074f575060015b6107ce5761075b611e89565b806308c379a014156107c25750610770611ea5565b8061077b57506107c4565b886001600160a01b03167f8a47468bf96478429d382ed6385d9c77fba8b89540cd88199b1b51d5723b11fa826040516107b49190611ceb565b60405180910390a250610831565b505b3d6000803e3d6000fd5b60005a6107db9089611d8c565b90506107ec89600354838787611147565b85828a6001600160a01b03167f0aa8d2838ad6d4e2a5d7ca0826dfdbc36d922adf8fe5c6d25aee2724b07de074886040516108279190611ceb565b60405180910390a4505b5050505050505050565b600080846001600160a01b0316638226f3966040518163ffffffff1660e01b815260040160206040518083038186803b15801561087757600080fd5b505afa15801561088b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108af9190611a69565b6001600160a01b03166398d5fdca6040518163ffffffff1660e01b815260040160206040518083038186803b1580156108e757600080fd5b505afa1580156108fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061091f9190611b96565b905060008113610933576000915050610981565b60006109476109428587611d6d565b61126c565b905060006109548361126c565b905061097b6109766109668484610dd3565b668075782dace9d960c71b6112d0565b611566565b93505050505b9392505050565b60008061099687878761083b565b905060006109a76109428686610b72565b90506109bc816109b7606461126c565b6112d0565b905060006109e96105586109cf8561126c565b6109e46109666109de8861126c565b87610dd3565b6115e9565b90506000896001600160a01b031663217a4b706040518163ffffffff1660e01b815260040160206040518083038186803b158015610a2657600080fd5b505afa158015610a3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a5e9190611a69565b6001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610a9657600080fd5b505afa158015610aaa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ace9190611c9c565b60405163bf36f0e960e01b81526004810184905260ff9190911660248201819052915060009073542848e66d8f387a78717be7b39f7259b7782bae9063bf36f0e99060440160206040518083038186803b158015610b2b57600080fd5b505af4158015610b3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b639190611b96565b9b9a5050505050505050505050565b600080610b7f8385611d33565b610b899042611d8c565b90506000600d610b9a836005611d6d565b610ba49190611d4b565b610baf906005611d33565b90506064811115610bc5576064925050506103a3565b91506103a39050565b6000546001600160a01b03163314610bf85760405162461bcd60e51b81526004016103f490611cfe565b600355565b6000805b82811015610c5457610c33848483818110610c1e57610c1e611e5d565b905060200201602081019061015a9190611a4c565b15610c425760019150506103a3565b80610c4c81611e00565b915050610c01565b5060009392505050565b6000546001600160a01b03163314610c885760405162461bcd60e51b81526004016103f490611cfe565b6001600160a01b038116610ced5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016103f4565b610cf6816110f7565b50565b60005b81811015610d4057610d2e838383818110610d1957610d19611e5d565b90506020020160208101906101d29190611a4c565b80610d3881611e00565b915050610cfc565b505050565b600081610d5457506000919050565b6000808313610d665782600003610d68565b825b90506000610d75826119a9565b90506070811015610d8e578060700382901b9150610da1565b6070811115610da1576070810382901c91505b6001600160701b03909116613fff820160701b17906000841215610dc9576001607f1b821791505b5060801b92915050565b6000617fff60f084811c8216919084901c811690821415610e835780617fff1415610e51576001600160801b03198581169085161415610e1e57505050600160ff1b811682186103a3565b600160ff1b6001600160801b0319868618161415610e41575050508181176103a3565b5061ffff60ef1b91506103a39050565b600160801b600160ff1b038416610e72575061ffff60ef1b91506103a39050565b505050600160ff1b811682186103a3565b80617fff1415610ebf57600160801b600160ff1b038516610eae575061ffff60ef1b91506103a39050565b505050600160ff1b821681186103a3565b6001600160701b03608086901c1682610edb5760019250610ee2565b600160701b175b6001600160701b03608086901c1682610efe5760019250610f05565b600160701b175b9081029081610f3357600160ff1b87871816610f22576000610f28565b600160ff1b5b9450505050506103a3565b928201926000600160e11b831015610f6657600160e01b831015610f5f57610f5a836119a9565b610f69565b60e0610f69565b60e15b90506140708186011015610f84576000945060009250611021565b6140e08186011015610fc757614070851015610fa957846140700383901c9250610fbe565b614070851115610fbe57614070850383901b92505b60009450611021565b61c0dd8186011115610fe157617fff945060009250611021565b6070811115610ff8576070810383901c925061100b565b607081101561100b578060700383901b92505b6001600160701b03831692506140df8186010394505b82607086901b888a186001607f1b60801b1660801c6001600160801b0316171760801b955050505050506103a3565b6000617fff60f083901c166140fe81111561106a57600080fd5b613fff81101561107d5750600092915050565b600160701b6001600160701b03608085901c161761406f8210156110a75761406f8290031c6110b9565b61406f8211156110b95761406e1982011b5b6001607f1b608085901c106110e357600160ff1b8111156110d957600080fd5b6000039392505050565b6001600160ff1b0381111561098157600080fd5b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006111568686868686610988565b604051636dc2b27160e01b8152336004820152602481018290529091506001600160a01b03871690636dc2b27190604401602060405180830381600087803b1580156111a157600080fd5b505af11580156111b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111d99190611b74565b156112235760405181815233906001600160a01b038816907ff03012dd4aab142682aa01439f94e975e2ec77ef558352323bc05f382f74b0829060200160405180910390a3611264565b60405181815233906001600160a01b038816907f08b35d4c86707ba44cdcc5efaa79b8a307e6c7f789f1eb86ca1ff1e9bde056979060200160405180910390a35b505050505050565b60008161127b57506000919050565b816000611287826119a9565b905060708110156112a0578060700382901b91506112b3565b60708111156112b3576070810382901c91505b613fff0160701b6001600160701b03919091161760801b92915050565b6000617fff60f084811c8216919084901c8116908214156113055780617fff1415610e72575061ffff60ef1b91506103a39050565b80617fff1415611349576dffffffffffffffffffffffffffff60801b841615611338575061ffff60ef1b91506103a39050565b505050808218600160ff1b166103a3565b600160801b600160ff1b03841661139357600160801b600160ff1b03851661137b575061ffff60ef1b91506103a39050565b505050808218600160ff1b16617fff60f01b176103a3565b6001600160701b03608085901c16816113af57600191506113b6565b600160701b175b6001600160701b03608087901c16836113f55780156113f05760006113da826119a9565b6001955060e20393840160711901939190911b90505b6113ff565b600160701b1760721b5b81818161140e5761140e611e47565b0490508061142a57600160ff1b87871816610f22576000610f28565b6001606c1b81101561143e5761143e611e1b565b6000600160731b82101561147d57600160721b82101561147257600160711b82101561146b576070611475565b6071611475565b60725b60ff16611486565b611486826119a9565b9050836140710181860111156114a457617fff945060009150611537565b83818601613ffc0110156114bf576000945060009150611537565b83818601613f8c01101561150c578385613ffc0111156114ea578385613ffc010382901b9150611503565b8385613ffc01101561150357613ffc8585030382901c91505b60009450611537565b607081111561151f576070810382901c91505b6001600160701b038216915083818601613f8d010394505b81607086901b888a186001607f1b60801b1660801c6001600160801b0316171760801b955050505050506103a3565b6000617fff60f083901c16613fff8110156115845750600092915050565b6001607f1b608084901c1061159857600080fd5b6140fe8111156115a757600080fd5b600160701b6001600160701b03608085901c161761406f8210156115d15761406f8290031c610981565b61406f8211156109815761406e1982011b9392505050565b6000617fff60f084811c8216919084901c8116908214156116375780617fff141561162d576001600160801b03198581169085161415610e415784925050506103a3565b84925050506103a3565b80617fff141561164b5783925050506103a3565b6001607f1b608086901c90811015906001600160701b0316836116715760019350611678565b600160701b175b6001607f1b608087901c90811015906001600160701b03168461169e57600194506116a5565b600160701b175b826116d5576001600160801b03198816600160ff1b146116c557876116c8565b60005b96505050505050506103a3565b806116f5576001600160801b03198916600160ff1b146116c557886116c8565b848603841515831515141561180057607081131561171c57899750505050505050506103a3565b600081131561172e5790811c9061175d565b606f1981121561174757889750505050505050506103a3565b600081121561175d578060000384901c93508596505b92810192600160711b8410611778576001968701969390931c925b86617fff14156117aa578461179257617fff60f01b61179c565b6001600160f01b03195b9750505050505050506103a3565b600160701b8410156117bf57600096506117cc565b6001600160701b03841693505b83607088901b866117de5760006117e4565b6001607f1b5b6001600160801b0316171760801b9750505050505050506103a3565b600081131561181b57600184901b9350600187039650611832565b600081121561183257600182901b91506001860396505b60708113156118445760019150611891565b6001811315611861576001810360018303901c6001019150611891565b606f198112156118745760019350611891565b600019811215611891576001816000030360018503901c60010193505b8184106118a25781840393506118ab565b83820393508294505b836118c15750600096506103a395505050505050565b60006118cc856119a9565b905080607114156118f257600185901c6001600160701b03169450600188019750611941565b607081101561193457607081900380891115611921578086901b6001600160701b03169550808903985061192e565b600098600019019590951b945b50611941565b6001600160701b03851694505b87617fff1415611974578561195b57617fff60f01b611965565b6001600160f01b03195b985050505050505050506103a3565b84607089901b8761198657600061198c565b6001607f1b5b6001600160801b0316171760801b985050505050505050506103a3565b60008082116119b757600080fd5b6000600160801b83106119cc57608092831c92015b6801000000000000000083106119e457604092831c92015b64010000000083106119f857602092831c92015b620100008310611a0a57601092831c92015b6101008310611a1b57600892831c92015b60108310611a2b57600492831c92015b60048310611a3b57600292831c92015b600283106103a35760010192915050565b600060208284031215611a5e57600080fd5b813561098181611f2f565b600060208284031215611a7b57600080fd5b815161098181611f2f565b600080600060608486031215611a9b57600080fd5b8335611aa681611f2f565b95602085013595506040909401359392505050565b600080600080600060a08688031215611ad357600080fd5b8535611ade81611f2f565b97602087013597506040870135966060810135965060800135945092505050565b60008060208385031215611b1257600080fd5b823567ffffffffffffffff80821115611b2a57600080fd5b818501915085601f830112611b3e57600080fd5b813581811115611b4d57600080fd5b8660208260051b8501011115611b6257600080fd5b60209290920196919550909350505050565b600060208284031215611b8657600080fd5b8151801515811461098157600080fd5b600060208284031215611ba857600080fd5b5051919050565b60008060008060808587031215611bc557600080fd5b84519350602085015167ffffffffffffffff80821115611be457600080fd5b818701915087601f830112611bf857600080fd5b815181811115611c0a57611c0a611e73565b6040519150611c23601f8201601f191660200183611dd3565b808252886020828501011115611c3857600080fd5b611c49816020840160208601611da3565b50604087015160609097015195989097509350505050565b600060208284031215611c7357600080fd5b5035919050565b60008060408385031215611c8d57600080fd5b50508035926020909101359150565b600060208284031215611cae57600080fd5b815160ff8116811461098157600080fd5b60008151808452611cd7816020860160208601611da3565b601f01601f19169290920160200192915050565b6020815260006109816020830184611cbf565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60008219821115611d4657611d46611e31565b500190565b600082611d6857634e487b7160e01b600052601260045260246000fd5b500490565b6000816000190483118215151615611d8757611d87611e31565b500290565b600082821015611d9e57611d9e611e31565b500390565b60005b83811015611dbe578181015183820152602001611da6565b83811115611dcd576000848401525b50505050565b601f8201601f1916810167ffffffffffffffff81118282101715611df957611df9611e73565b6040525050565b6000600019821415611e1457611e14611e31565b5060010190565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b600060033d1115611ea25760046000803e5060005160e01c5b90565b600060443d1015611eb35790565b6040516003193d81016004833e81513d67ffffffffffffffff8160248401118184111715611ee357505050505090565b8285019150815181811115611efb5750505050505090565b843d8701016020828501011115611f155750505050505090565b611f2460208286010187611dd3565b509095945050505050565b6001600160a01b0381168114610cf657600080fdfea26469706673582212202e1a80ff10a4c06d30d3b609f5014ec1b01c0a9cbd27f0aab71c3a5b7c2a996264736f6c63430008070033

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

00000000000000000000000098c58c1ceb01e198f8356763d5cba8eb7b11e4e2

-----Decoded View---------------
Arg [0] : _factory (address): 0x98C58c1cEb01E198F8356763d5CbA8EB7b11e4E2

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000098c58c1ceb01e198f8356763d5cba8eb7b11e4e2


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
[ Download: CSV Export  ]
[ 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.