Contract 0x3Feafee6b12C8d2E58c5B118e54C09F9273c6124 1

 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x08f264ebaced29866bb5636fc0c0b77f1d44d6237b66bab8d6b383c672a6bed6Transfer Governa...175172812022-07-13 2:21:40137 days 17 hrs agoMycelium: Deployer IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.000153382796 ETH
0xd0608c7b10a6a8256156b4d8a5d22e11db849238ca37dbe9cb07471876071110Transfer Governa...175171642022-07-13 2:18:58137 days 17 hrs agoMycelium: Deployer IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.000169457419 ETH
0x674af215b33a132023e27faaaecf73affff281f6877643dc53f9367d30a3bc34Deploy Pool154618322022-06-23 4:09:53157 days 15 hrs agoMycelium: Deployer IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.000990669399 ETH
0xb6a4aac0455058314b863866359f2ad0f70dccdbd8ae6b77e3c89ba99017e230Deploy Pool154613492022-06-23 4:04:19157 days 15 hrs agoMycelium: Deployer IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.00098807704 ETH
0x895e130637617a3b2ace7e09182854cca9cf80c0e4c3d67693b32318534ce28aDeploy Pool152135712022-06-21 3:00:07159 days 16 hrs ago0x28f2ed74d19454d766873ba30a4c124367409858 IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.000768212798 ETH
0x90b3e19256a48ce9b015d9e065570834b8b6b023d823e64fb7c01b0b46c2d2b6Deploy Pool149596862022-06-18 4:43:46162 days 15 hrs ago0x6bd2b339294c6696a2ba908e64c9787dce92dd19 IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.000996197344 ETH
0xdbc6e3ab1150a233857b1bd48da42499efca5d6164f3820013f4cc2c0d998e22Deploy Pool147664532022-06-16 5:04:52164 days 14 hrs ago0x97f31b64c9b4ce4ab96e6e550026b122485ee18e IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.001777156581 ETH
0x260aafc45e177e4e57f4027db0117665d482fd7d9c87f6639c46621790e0120cDeploy Pool141568022022-06-09 13:39:49171 days 6 hrs ago0x4d1271bf27901dfcb3fe8d67c52c907b2bb7afca IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.001481481706 ETH
0x296413ae9a301661bf7e7bd9f59629a0f77b84160decc43e55cb0ef2b7c1d2aeDeploy Pool133879312022-06-01 1:28:21179 days 18 hrs agoMycelium: Deployer IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.00164766229 ETH
0x1440365bc8b06ce9943a37cc1644213d0be9600ab0446fa99fc9f398d0ba1714Deploy Pool133879222022-06-01 1:28:06179 days 18 hrs agoMycelium: Deployer IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.001647728073 ETH
0x945a16c93911a7b0108deeba71f92ce6b2267f7c1e81f7a3a56bfcff435a3428Deploy Pool133879102022-06-01 1:28:06179 days 18 hrs agoMycelium: Deployer IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.001678095784 ETH
0xd924bf7a3193c9761569d10410ee70a7b0971bd746e56f60b9ce2e12e8d21723Set Invariant Ch...133876462022-06-01 1:24:33179 days 18 hrs agoMycelium: Deployer IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.00031746121 ETH
0x6f4b5cf86f590daca7894b9a0933752ecdea8600418d24320a2da1bd24f83167Set Fee133876392022-06-01 1:24:33179 days 18 hrs agoMycelium: Deployer IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.000305437128 ETH
0xcddff63ef3030fc4698e17647dce979f34a33f20b650d5c2e1ab18f60ac9eb5cSet Deployment F...133876332022-06-01 1:24:33179 days 18 hrs agoMycelium: Deployer IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.000421984151 ETH
0xc013050ff9f681f069936664dada35cc7eaca8e95464f1215b4e60cf110395eaSet Auto Claim133876272022-06-01 1:24:33179 days 18 hrs agoMycelium: Deployer IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.000317455004 ETH
0x781a17f878ead15ac0f5aab076a05bb69eda4e787e10cc2edc03f8d7c0d91313Set Pool Keeper133876212022-06-01 1:23:06179 days 18 hrs agoMycelium: Deployer IN  0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH0.000317494102 ETH
0x385529327b588b8120af9cc3815fe5e2290d62fec8afb6edf72104f2d32fc5e00x60e06040133875222022-06-01 1:22:08179 days 18 hrs agoMycelium: Deployer IN  Create: PoolFactory0 ETH0.063315369911 ETH
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x908c2a2e6928d94a23cffb84a3cee308c2bb60ec372861d5ea17c7028f68137c417018862022-11-27 19:27:5326 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0x908c2a2e6928d94a23cffb84a3cee308c2bb60ec372861d5ea17c7028f68137c417018862022-11-27 19:27:5326 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0x908c2a2e6928d94a23cffb84a3cee308c2bb60ec372861d5ea17c7028f68137c417018862022-11-27 19:27:5326 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0x908c2a2e6928d94a23cffb84a3cee308c2bb60ec372861d5ea17c7028f68137c417018862022-11-27 19:27:5326 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0xa120b7a99f1b97a802c7d65ee562f3944c8369a6b5756fd67fdfb31f37834697417018852022-11-27 19:27:5226 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0xa120b7a99f1b97a802c7d65ee562f3944c8369a6b5756fd67fdfb31f37834697417018852022-11-27 19:27:5226 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0xa120b7a99f1b97a802c7d65ee562f3944c8369a6b5756fd67fdfb31f37834697417018852022-11-27 19:27:5226 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0xa120b7a99f1b97a802c7d65ee562f3944c8369a6b5756fd67fdfb31f37834697417018852022-11-27 19:27:5226 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0x40fd4a5af1bf55aa60628c047a6e46b3a53653ca077a7aa92e3d068c80621987417018842022-11-27 19:27:5226 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0x40fd4a5af1bf55aa60628c047a6e46b3a53653ca077a7aa92e3d068c80621987417018842022-11-27 19:27:5226 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0x40fd4a5af1bf55aa60628c047a6e46b3a53653ca077a7aa92e3d068c80621987417018842022-11-27 19:27:5226 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0x40fd4a5af1bf55aa60628c047a6e46b3a53653ca077a7aa92e3d068c80621987417018842022-11-27 19:27:5226 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0xed42a8eedf73ddc3e98fdf6a505f61b26e7391aeb2ac97cbe687e43a13ced010416915932022-11-27 18:27:531 hr 26 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0xed42a8eedf73ddc3e98fdf6a505f61b26e7391aeb2ac97cbe687e43a13ced010416915932022-11-27 18:27:531 hr 26 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0xed42a8eedf73ddc3e98fdf6a505f61b26e7391aeb2ac97cbe687e43a13ced010416915932022-11-27 18:27:531 hr 26 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0xed42a8eedf73ddc3e98fdf6a505f61b26e7391aeb2ac97cbe687e43a13ced010416915932022-11-27 18:27:531 hr 26 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0xd8f0785d18f59c91c667bd5d06e618862a3eb2d423fa35fa6e7fb1004e850b1e416915922022-11-27 18:27:531 hr 26 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0xd8f0785d18f59c91c667bd5d06e618862a3eb2d423fa35fa6e7fb1004e850b1e416915922022-11-27 18:27:531 hr 26 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0xd8f0785d18f59c91c667bd5d06e618862a3eb2d423fa35fa6e7fb1004e850b1e416915922022-11-27 18:27:531 hr 26 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0xd8f0785d18f59c91c667bd5d06e618862a3eb2d423fa35fa6e7fb1004e850b1e416915922022-11-27 18:27:531 hr 26 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0x777f91ae4e57a6809ba65c99238960a980996066da47e17c047afff94303faca416915912022-11-27 18:27:521 hr 26 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0x777f91ae4e57a6809ba65c99238960a980996066da47e17c047afff94303faca416915912022-11-27 18:27:521 hr 26 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0x777f91ae4e57a6809ba65c99238960a980996066da47e17c047afff94303faca416915912022-11-27 18:27:521 hr 26 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0x777f91ae4e57a6809ba65c99238960a980996066da47e17c047afff94303faca416915912022-11-27 18:27:521 hr 26 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
0x1bb5ec424dc89184278a5a58da6be926d63e4b74e66bb5c0f83aa1097691e053416857802022-11-27 17:56:241 hr 57 mins ago 0x051afd0b39acf4cc52c76a479ad802d0b82a8249 0x3feafee6b12c8d2e58c5b118e54c09f9273c61240 ETH
[ Download CSV Export 
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.

Contract Source Code Verified (Exact Match)

Contract Name:
PoolFactory

Compiler Version
v0.8.7+commit.e28d00a7

Optimization Enabled:
Yes with 200 runs

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

import "../interfaces/IPoolFactory.sol";
import "../interfaces/ILeveragedPool.sol";
import "../interfaces/IPoolCommitter.sol";
import "../interfaces/IERC20DecimalsWrapper.sol";
import "../interfaces/IAutoClaim.sol";
import "../interfaces/ITwoStepGovernance.sol";
import "./LeveragedPool.sol";
import "./PoolToken.sol";
import "./PoolKeeper.sol";
import "./PoolCommitter.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title The pool factory contract
contract PoolFactory is IPoolFactory, ITwoStepGovernance {
    // #### Globals
    address public immutable pairTokenBaseAddress;
    address public immutable poolBaseAddress;
    IPoolKeeper public poolKeeper;
    address public immutable poolCommitterBaseAddress;

    address public autoClaim;
    address public invariantCheck;

    // Contract address which has governance permissions
    address public override governance;
    bool public override governanceTransferInProgress;
    address public override provisionalGovernance;
    // Default fee, annualised; Fee value as a decimal multiplied by 10^18. For example, 50% is represented as 0.5 * 10^18
    uint256 public fee;
    // Percent of fees that go to secondary fee address if applicable.
    uint256 public secondaryFeeSplitPercent = 10;

    // Deployment fee variables
    address public deploymentFeeToken;
    uint256 public deploymentFee;
    address public deploymentFeeReceiver;

    // This is required because we must pass along *some* value for decimal
    // precision to the base pool tokens as we use the Cloneable pattern
    uint8 constant DEFAULT_NUM_DECIMALS = 18;
    uint8 constant MAX_DECIMALS = DEFAULT_NUM_DECIMALS;
    // Considering leap year thus using 365.2425 days per year
    uint32 constant SECONDS_PER_LEAP_YEAR = 365.2425 days;
    // Contract address to receive protocol fees
    address public feeReceiver;

    /**
     * @notice Format: Pool counter => pool address
     */
    mapping(uint256 => address) public override pools;
    uint256 public override numPools;

    /**
     * @notice Format: Pool address => validity
     */
    mapping(address => bool) public override isValidPool;

    /**
     * @notice Format: PoolCommitter address => validity
     */
    mapping(address => bool) public override isValidPoolCommitter;

    // #### Modifiers
    modifier onlyGov() {
        require(msg.sender == governance, "msg.sender not governance");
        _;
    }

    // #### Functions
    constructor(address _feeReceiver, address _governance) {
        require(_feeReceiver != address(0), "Fee receiver cannot be null");
        require(_governance != address(0), "Governance cannot be null");

        governance = _governance;

        // Deploy base contracts
        PoolToken pairTokenBase = new PoolToken(DEFAULT_NUM_DECIMALS);
        pairTokenBaseAddress = address(pairTokenBase);
        LeveragedPool poolBase = new LeveragedPool();
        poolBaseAddress = address(poolBase);
        PoolCommitter poolCommitterBase = new PoolCommitter();
        poolCommitterBaseAddress = address(poolCommitterBase);

        feeReceiver = _feeReceiver;

        /* initialise base PoolToken template (with dummy values) */
        pairTokenBase.initialize(address(poolBase), "base", "BASE", 8);

        /* initialise base LeveragedPool template (with dummy values) */
        ILeveragedPool.Initialization memory dummyInitialization = ILeveragedPool.Initialization({
            _owner: address(this),
            _keeper: address(this),
            _oracleWrapper: address(this),
            _settlementEthOracle: address(this),
            _longToken: address(pairTokenBase),
            _shortToken: address(pairTokenBase),
            _poolCommitter: address(poolCommitterBase),
            _invariantCheck: address(this),
            _poolName: "base",
            _frontRunningInterval: 0,
            _updateInterval: 1,
            _fee: 0,
            _leverageAmount: 1,
            _feeAddress: address(this),
            _secondaryFeeAddress: address(this),
            _settlementToken: address(this),
            _secondaryFeeSplitPercent: 0
        });
        poolBase.initialize(dummyInitialization);
        /* initialise base PoolCommitter template (with dummy values) */
        poolCommitterBase.initialize(address(this), address(this), address(this), governance, governance, 0, 0, 0);
    }

    /**
     * @notice Deploy a leveraged pool and its committer/pool tokens with given parameters
     * @notice Rebasing tokens are not supported and will break functionality
     * @param deploymentParameters Deployment parameters of the market. Some may be reconfigurable.
     * @return Address of the created pool
     * @dev Throws if pool keeper is null
     * @dev Throws if deployer does not own the oracle wrapper
     * @dev Throws if leverage amount is invalid
     * @dev Throws if decimal precision is too high (i.e., greater than `MAX_DECIMALS`)
     * @dev The IOracleWrapper declares a `deployer` variable, this is used here to confirm that the pool which uses said oracle wrapper is indeed
     *      the intended address. This is to prevent a griefing attack in which someone uses the same oracle wrapper with the same parameters *before* the genuine deployer.
     */
    function deployPool(PoolDeployment calldata deploymentParameters) external override returns (address) {
        address _poolKeeper = address(poolKeeper);
        require(_poolKeeper != address(0), "PoolKeeper not set");
        require(autoClaim != address(0), "AutoClaim not set");
        require(invariantCheck != address(0), "InvariantCheck not set");
        require(
            IOracleWrapper(deploymentParameters.oracleWrapper).deployer() == msg.sender,
            "Deployer must be oracle wrapper owner"
        );
        require(deploymentParameters.leverageAmount != 0, "Leveraged amount cannot equal 0");
        require(
            IERC20DecimalsWrapper(deploymentParameters.settlementToken).decimals() <= MAX_DECIMALS,
            "Decimal precision too high"
        );

        if (deploymentFee != 0) {
            IERC20(deploymentFeeToken).transferFrom(msg.sender, deploymentFeeReceiver, deploymentFee);
        }

        bytes32 uniquePoolHash = keccak256(
            abi.encode(
                deploymentParameters.frontRunningInterval,
                deploymentParameters.updateInterval,
                deploymentParameters.leverageAmount,
                deploymentParameters.settlementToken,
                deploymentParameters.oracleWrapper
            )
        );

        PoolCommitter poolCommitter = PoolCommitter(
            Clones.cloneDeterministic(poolCommitterBaseAddress, uniquePoolHash)
        );

        address poolCommitterAddress = address(poolCommitter);
        poolCommitter.initialize(
            address(this),
            autoClaim,
            governance,
            deploymentParameters.feeController,
            invariantCheck,
            deploymentParameters.mintingFee,
            deploymentParameters.burningFee,
            deploymentParameters.changeInterval
        );

        LeveragedPool pool = LeveragedPool(Clones.cloneDeterministic(poolBaseAddress, uniquePoolHash));
        address _pool = address(pool);
        emit DeployPool(_pool, address(poolCommitter), deploymentParameters.poolName);

        string memory leverage = Strings.toString(deploymentParameters.leverageAmount);

        ILeveragedPool.Initialization memory initialization = ILeveragedPool.Initialization({
            _owner: governance, // governance is the owner of pools -- if this changes, `onlyGov` breaks
            _keeper: _poolKeeper,
            _oracleWrapper: deploymentParameters.oracleWrapper,
            _settlementEthOracle: deploymentParameters.settlementEthOracle,
            _longToken: deployPairToken(_pool, leverage, deploymentParameters, "L-"),
            _shortToken: deployPairToken(_pool, leverage, deploymentParameters, "S-"),
            _poolCommitter: poolCommitterAddress,
            _invariantCheck: invariantCheck,
            _poolName: string(abi.encodePacked(leverage, "-", deploymentParameters.poolName)),
            _frontRunningInterval: deploymentParameters.frontRunningInterval,
            _updateInterval: deploymentParameters.updateInterval,
            _fee: (fee * deploymentParameters.updateInterval) / (SECONDS_PER_LEAP_YEAR),
            _leverageAmount: deploymentParameters.leverageAmount,
            _feeAddress: feeReceiver,
            _secondaryFeeAddress: msg.sender,
            _settlementToken: deploymentParameters.settlementToken,
            _secondaryFeeSplitPercent: secondaryFeeSplitPercent
        });

        // approve the settlement token on the pool committer to finalise linking
        // this also stores the pool address in the committer
        // finalise pool setup
        pool.initialize(initialization);
        IPoolCommitter(poolCommitterAddress).setPool(_pool);
        emit DeployCommitter(
            poolCommitterAddress,
            deploymentParameters.settlementToken,
            _pool,
            deploymentParameters.changeInterval,
            deploymentParameters.feeController
        );

        poolKeeper.newPool(_pool);
        pools[numPools] = _pool;
        // numPools overflowing would require an unrealistic number of markets
        unchecked {
            numPools++;
        }
        isValidPool[_pool] = true;
        isValidPoolCommitter[address(poolCommitter)] = true;
        return _pool;
    }

    /**
     * @notice Deploy a contract for pool tokens
     * @param pool The pool address, owner of the Pool Token
     * @param leverage Amount of leverage for pool
     * @param deploymentParameters Deployment parameters for parent function
     * @param direction Long or short token, L- or S-
     * @return Address of the pool token
     */
    function deployPairToken(
        address pool,
        string memory leverage,
        PoolDeployment memory deploymentParameters,
        string memory direction
    ) internal returns (address) {
        string memory poolNameAndSymbol = string(abi.encodePacked(leverage, direction, deploymentParameters.poolName));
        uint8 settlementDecimals = IERC20DecimalsWrapper(deploymentParameters.settlementToken).decimals();
        bytes32 uniqueTokenHash = keccak256(
            abi.encode(
                deploymentParameters.leverageAmount,
                deploymentParameters.settlementToken,
                deploymentParameters.oracleWrapper,
                direction
            )
        );

        PoolToken pairToken = PoolToken(Clones.cloneDeterministic(pairTokenBaseAddress, uniqueTokenHash));
        pairToken.initialize(pool, poolNameAndSymbol, poolNameAndSymbol, settlementDecimals);
        return address(pairToken);
    }

    /**
     * @notice Sets the address of the associated `PoolKeeper` contract
     * @param _poolKeeper Address of the `PoolKeeper`
     * @dev Throws if provided address is null
     * @dev Only callable by the owner
     * @dev Emits a `PoolKeeperChanged` event on success
     */
    function setPoolKeeper(address _poolKeeper) external override onlyGov {
        require(_poolKeeper != address(0), "cannot be null");
        poolKeeper = IPoolKeeper(_poolKeeper);
        emit PoolKeeperChanged(_poolKeeper);
    }

    /**
     * @notice Gets the address of the associated `PoolKeeper` contract
     * @return Address of the associated `PoolKeeper` contract
     */
    function getPoolKeeper() external view override returns (address) {
        return address(poolKeeper);
    }

    /**
     * @notice Sets the address of the associated `AutoClaim` contract
     * @param _autoClaim Address of the `AutoClaim`
     * @dev Throws if provided address is null
     * @dev Only callable by the owner
     */
    function setAutoClaim(address _autoClaim) external override onlyGov {
        require(_autoClaim != address(0), "cannot be null");
        autoClaim = _autoClaim;
        emit AutoClaimChanged(_autoClaim);
    }

    /**
     * @notice Sets the address of the associated `InvariantCheck` contract
     * @param _invariantCheck Address of the `InvariantCheck`
     * @dev Throws if provided address is null
     * @dev Only callable by the owner
     */
    function setInvariantCheck(address _invariantCheck) external override onlyGov {
        require(_invariantCheck != address(0), "cannot be null");
        invariantCheck = _invariantCheck;
        emit InvariantCheckChanged(_invariantCheck);
    }

    /**
     * @notice Sets the primary fee receiver of deployed Leveraged pools.
     * @param _feeReceiver address of fee receiver
     * @dev Only callable by the owner of this contract
     * @dev This fuction does not change anything for already deployed pools, only pools deployed after the change
     * @dev Emits a `FeeReceiverChanged` event on success
     */
    function setFeeReceiver(address _feeReceiver) external override onlyGov {
        require(_feeReceiver != address(0), "Fee receiver cannot be null");
        feeReceiver = _feeReceiver;
        emit FeeReceiverChanged(_feeReceiver);
    }

    /**
     * @notice Sets the proportion of fees to be split to the nominated secondary fees recipient
     * @param newFeePercent Proportion of fees to split
     * @dev Only callable by the owner of this contract
     * @dev Throws if `newFeePercent` exceeds 100
     * @dev Emits a `SecondaryFeeSplitChanged` event on success
     */
    function setSecondaryFeeSplitPercent(uint256 newFeePercent) external override onlyGov {
        require(newFeePercent <= 100, "Secondary fee split cannot exceed 100%");
        secondaryFeeSplitPercent = newFeePercent;
        emit SecondaryFeeSplitChanged(newFeePercent);
    }

    /**
     * @notice Set the yearly fee amount. The max yearly fee is 10%
     * @dev This is a percentage in WAD; multiplied by 10^18 e.g. 5% is 0.05 * 10^18
     * @param _fee The fee amount as a percentage
     * @dev Throws if fee is greater than 10%
     * @dev Emits a `FeeChanged` event on success
     */
    function setFee(uint256 _fee) external override onlyGov {
        require(_fee <= 0.1e18, "Fee cannot be > 10%");
        fee = _fee;
        emit FeeChanged(_fee);
    }

    /**
     * @notice Set the deployment fee
     * @dev Only callable by the owner of this contract
     * @dev Emits a `DeploymentFeeChanged` event on success
     */
    function setDeploymentFee(
        address _token,
        uint256 _fee,
        address _receiver
    ) external override onlyGov {
        require(_token != address(0), "Token cannot be null");
        deploymentFeeToken = _token;
        deploymentFee = _fee;
        deploymentFeeReceiver = _receiver;
        emit DeploymentFeeChanged(_token, _fee, _receiver);
    }

    /**
     * @notice Starts to transfer governance of the pool. The new governance
     *          address must call `claimGovernance` in order for this to take
     *          effect. Until this occurs, the existing governance address
     *          remains in control of the pool.
     * @param _governance New address of the governance of the pool
     * @dev First step of the two-step governance transfer process
     * @dev Sets the governance transfer flag to true
     * @dev See `claimGovernance`
     */
    function transferGovernance(address _governance) external override onlyGov {
        require(_governance != governance, "New governance address cannot be same as old governance address");
        require(_governance != address(0), "Governance cannot be null");
        provisionalGovernance = _governance;
        governanceTransferInProgress = true;
        emit ProvisionalGovernanceChanged(_governance);
    }

    /**
     * @notice Completes transfer of governance by actually changing permissions
     *          over the pool.
     * @dev Second and final step of the two-step governance transfer process
     * @dev See `transferGovernance`
     * @dev Sets the governance transfer flag to false
     * @dev After a successful call to this function, the actual governance
     *      address and the provisional governance address MUST be equal.
     */
    function claimGovernance() external override {
        require(governanceTransferInProgress, "No governance change active");
        address _provisionalGovernance = provisionalGovernance;
        require(msg.sender == _provisionalGovernance, "Not provisional governor");
        emit GovernanceAddressChanged(governance, _provisionalGovernance);
        governance = _provisionalGovernance;
        delete provisionalGovernance;
        governanceTransferInProgress = false;
    }
}

File 2 of 31 : 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 settlementToken; // 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
        address feeController;
        // The fee taken for each mint and burn. Fee value as a decimal multiplied by 10^18. For example, 50% is represented as 0.5 * 10^18
        uint256 mintingFee; // The fee amount for mints
        uint256 changeInterval; // The interval at which the mintingFee in a market either increases or decreases, as per the logic in `PoolCommitter::updateMintingFee`
        uint256 burningFee; // The fee amount for burns
    }

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

    /**
     * @notice Indicates that the InvariantCheck contract has changed
     * @param invariantCheck New InvariantCheck contract
     */
    event InvariantCheckChanged(address indexed invariantCheck);

    /**
     * @notice Creates a notification when a PoolCommitter is deployed
     * @param poolCommitterAddress Address of new PoolCommitter
     * @param settlementToken Address of new settlementToken
     * @param pool Address of the pool associated with this PoolCommitter
     * @param changeInterval The amount that the `mintingFee` will change each update interval, based on `updateMintingFee`, given as a decimal * 10 ^ 18 (same format as `_mintingFee`)
     * @param feeController The address that has control over fee parameters
     */
    event DeployCommitter(
        address poolCommitterAddress,
        address settlementToken,
        address pool,
        uint256 changeInterval,
        address feeController
    );

    /**
     * @notice Creates a notification when the pool keeper changes
     * @param _poolKeeper Address of the new pool keeper
     */
    event PoolKeeperChanged(address _poolKeeper);

    /**
     * @notice Indicates that the maximum allowed leverage has changed
     * @param leverage New maximum allowed leverage value
     */
    event MaxLeverageChanged(uint256 indexed leverage);

    /**
     * @notice Indicates that the receipient of fees has changed
     * @param receiver Address of the new receipient of fees
     */
    event FeeReceiverChanged(address indexed receiver);

    /**
     * @notice Indicates that the receipient of fees has changed
     * @param fee Address of the new receipient of fees
     */
    event SecondaryFeeSplitChanged(uint256 indexed fee);

    /**
     * @notice Indicates that the trading fee has changed
     * @param fee New trading fee
     */
    event FeeChanged(uint256 indexed fee);

    /**
     * @notice Indicates that the AutoClaim contract has changed
     * @param autoClaim New AutoClaim contract
     */
    event AutoClaimChanged(address indexed autoClaim);

    /**
     * @notice Indicates that the minting and burning fees have changed
     * @param mint Minting fee
     * @param burn Burning fee
     */
    event MintAndBurnFeesChanged(uint256 indexed mint, uint256 indexed burn);

    /**
     * @notice Indicates that the deployment fee has changed
     * @param fee New deployment fee
     */
    event DeploymentFeeChanged(address _token, uint256 fee, address _receiver);

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

    function isValidPoolCommitter(address _poolCommitter) external view returns (bool);

    function getPoolKeeper() external view returns (address);

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

    function setPoolKeeper(address _poolKeeper) external;

    function setAutoClaim(address _autoClaim) external;

    function setInvariantCheck(address _invariantCheck) external;

    function setFeeReceiver(address _feeReceiver) external;

    function setFee(uint256 _fee) external;

    function setDeploymentFee(
        address _token,
        uint256 _fee,
        address _receiver
    ) external;

    function setSecondaryFeeSplitPercent(uint256 newFeePercent) external;
}

File 3 of 31 : 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
        address _invariantCheck; // Address of the InvariantCheck 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
        uint16 _leverageAmount; // The amount of exposure to price movements for the pool
        uint256 _fee; // The fund movement fee. This amount is extracted from the deposited asset with every update and sent to the fee address. Given as the decimal * 10 ^ 18. For example, 60% fee is 0.6 * 10 ^ 18
        address _feeAddress; // The address that the fund movement fee is sent to
        address _secondaryFeeAddress; // The address of fee recieved by third party deployers
        address _settlementToken; //  The digital asset that the pool accepts. Must have a decimals() function
        uint256 _secondaryFeeSplitPercent; // Percent of fees that go to secondary fee address if it exists
    }

    // #### 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 settlementToken The address of the digital asset that the pool accepts
     * @param poolName The identification name of the pool
     */
    event PoolInitialized(
        address indexed longToken,
        address indexed shortToken,
        address settlementToken,
        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
     * @param shortFeeAmount Proportional fee taken from short side
     * @param longFeeAmount Proportional fee taken from long side
     */
    event PoolRebalance(
        int256 shortBalanceChange,
        int256 longBalanceChange,
        uint256 shortFeeAmount,
        uint256 longFeeAmount
    );

    /**
     * @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 secondary fee receiver's address
     * @param oldAddress Previous address
     * @param newAddress Address after change
     */
    event SecondaryFeeAddressUpdated(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 Indicates a payment of fees to the secondary fee address
     * @param secondaryFeeAddress The address that got fees paid to it
     * @param amount Amount of settlement token paid
     */
    event SecondaryFeesPaid(address indexed secondaryFeeAddress, uint256 amount);

    /**
     * @notice Indicates a payment of fees to the primary fee address
     * @param feeAddress The address that got fees paid to it
     * @param amount Amount of settlement token paid
     */
    event PrimaryFeesPaid(address indexed feeAddress, uint256 amount);

    /**
     * @notice Indicates settlement assets have been withdrawn from the system
     * @param to Receipient
     * @param quantity Quantity of settlement tokens withdrawn
     */
    event SettlementWithdrawn(address indexed to, uint256 indexed quantity);

    /**
     * @notice Indicates that the balance of pool tokens on issue for the pool
     *          changed
     * @param long New quantity of long pool tokens
     * @param short New quantity of short pool tokens
     */
    event PoolBalancesChanged(uint256 indexed long, uint256 indexed short);

    function leverageAmount() external view returns (bytes16);

    function poolCommitter() external view returns (address);

    function settlementToken() external view returns (address);

    function primaryFees() external view returns (uint256);

    function secondaryFees() external view returns (uint256);

    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 settlementTokenTransferFrom(
        address from,
        address to,
        uint256 amount
    ) external;

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

    function settlementTokenTransfer(address to, uint256 amount) external;

    function claimPrimaryFees() external;

    function claimSecondaryFees() external;

    /**
     * @notice Transfer pool tokens from pool to user
     * @param isLongToken True if transferring long pool token; False if transferring short pool token
     * @param to Address of account to transfer to
     * @param amount Amount of pool tokens being transferred
     * @dev Only callable by the associated `PoolCommitter` contract
     * @dev Only callable when the market is *not* paused
     */
    function poolTokenTransfer(
        bool isLongToken,
        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 updateFeeAddress(address account) external;

    function updateSecondaryFeeAddress(address account) external;

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

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

/// @title The interface for the contract that handles pool commitments
interface IPoolCommitter {
    /// Type of commit
    enum CommitType {
        ShortMint, // Mint short tokens
        ShortBurn, // Burn short tokens
        LongMint, // Mint long tokens
        LongBurn, // Burn long tokens
        LongBurnShortMint, // Burn Long tokens, then instantly mint in same upkeep
        ShortBurnLongMint // Burn Short tokens, then instantly mint in same upkeep
    }

    function isMint(CommitType t) external pure returns (bool);

    function isBurn(CommitType t) external pure returns (bool);

    function isLong(CommitType t) external pure returns (bool);

    function isShort(CommitType t) external pure returns (bool);

    // Pool balances and supplies
    struct BalancesAndSupplies {
        uint256 newShortBalance;
        uint256 newLongBalance;
        uint256 longMintPoolTokens;
        uint256 shortMintPoolTokens;
        uint256 longBurnInstantMintSettlement;
        uint256 shortBurnInstantMintSettlement;
        uint256 totalLongBurnPoolTokens;
        uint256 totalShortBurnPoolTokens;
    }

    // User aggregate balance
    struct Balance {
        uint256 longTokens;
        uint256 shortTokens;
        uint256 settlementTokens;
    }

    // Token Prices
    struct Prices {
        bytes16 longPrice;
        bytes16 shortPrice;
    }

    // Commit information
    struct Commit {
        uint256 amount;
        CommitType commitType;
        uint40 created;
        address owner;
    }

    // Commit information
    struct TotalCommitment {
        uint256 longMintSettlement;
        uint256 longBurnPoolTokens;
        uint256 shortMintSettlement;
        uint256 shortBurnPoolTokens;
        uint256 shortBurnLongMintPoolTokens;
        uint256 longBurnShortMintPoolTokens;
    }

    // User updated aggregate balance
    struct BalanceUpdate {
        uint256 _updateIntervalId;
        uint256 _newLongTokensSum;
        uint256 _newShortTokensSum;
        uint256 _newSettlementTokensSum;
        uint256 _longSettlementFee;
        uint256 _shortSettlementFee;
        uint8 _maxIterations;
    }

    // Track how much of a user's commitments are being done from their aggregate balance
    struct UserCommitment {
        uint256 longMintSettlement;
        uint256 longBurnPoolTokens;
        uint256 shortMintSettlement;
        uint256 shortBurnPoolTokens;
        uint256 shortBurnLongMintPoolTokens;
        uint256 longBurnShortMintPoolTokens;
        uint256 updateIntervalId;
    }

    // Track the relevant data when executing a range of update interval's commitments (stack too deep)
    struct CommitmentExecutionTracking {
        uint256 longTotalSupply;
        uint256 shortTotalSupply;
        uint256 longTotalSupplyBefore;
        uint256 shortTotalSupplyBefore;
        uint256 _updateIntervalId;
    }

    /**
     * @notice Creates a notification when a commit is created
     * @param user The user making the commitment
     * @param amount Amount of the commit
     * @param commitType Type of the commit (Short v Long, Mint v Burn)
     * @param appropriateUpdateIntervalId Id of update interval where this commit can be executed as part of upkeep
     * @param fromAggregateBalance whether or not to commit from aggregate (unclaimed) balance
     * @param payForClaim whether or not to request this commit be claimed automatically
     * @param mintingFee Minting fee at time of commit creation
     */
    event CreateCommit(
        address indexed user,
        uint256 indexed amount,
        CommitType indexed commitType,
        uint256 appropriateUpdateIntervalId,
        bool fromAggregateBalance,
        bool payForClaim,
        bytes16 mintingFee
    );

    /**
     * @notice Creates a notification when a user's aggregate balance is updated
     */
    event AggregateBalanceUpdated(address indexed user);

    /**
     * @notice Creates a notification when the PoolCommitter's leveragedPool address has been updated.
     * @param newPool the address of the new leveraged pool
     */
    event PoolChanged(address indexed newPool);

    /**
     * @notice Creates a notification when commits for a given update interval are executed
     * @param updateIntervalId Unique identifier for the relevant update interval
     * @param burningFee Burning fee at the time of commit execution
     */
    event ExecutedCommitsForInterval(uint256 indexed updateIntervalId, bytes16 burningFee);

    /**
     * @notice Creates a notification when a claim is made, depositing pool tokens in user's wallet
     */
    event Claim(address indexed user);

    /*
     * @notice Creates a notification when the burningFee is updated
     */
    event BurningFeeSet(uint256 indexed _burningFee);

    /**
     * @notice Creates a notification when the mintingFee is updated
     */
    event MintingFeeSet(uint256 indexed _mintingFee);

    /**
     * @notice Creates a notification when the changeInterval is updated
     */
    event ChangeIntervalSet(uint256 indexed _changeInterval);

    /**
     * @notice Creates a notification when the feeController is updated
     */
    event FeeControllerSet(address indexed _feeController);

    // #### Functions

    function initialize(
        address _factory,
        address _autoClaim,
        address _factoryOwner,
        address _feeController,
        address _invariantCheck,
        uint256 mintingFee,
        uint256 burningFee,
        uint256 _changeInterval
    ) external;

    function commit(bytes32 args) external payable;

    function updateIntervalId() external view returns (uint128);

    function pendingMintSettlementAmount() external view returns (uint256);

    function pendingShortBurnPoolTokens() external view returns (uint256);

    function pendingLongBurnPoolTokens() external view returns (uint256);

    function claim(address user) external;

    function executeCommitments(
        uint256 lastPriceTimestamp,
        uint256 updateInterval,
        uint256 longBalance,
        uint256 shortBalance
    )
        external
        returns (
            uint256,
            uint256,
            uint256,
            uint256,
            uint256
        );

    function updateAggregateBalance(address user) external;

    function getAggregateBalance(address user) external view returns (Balance memory _balance);

    function getAppropriateUpdateIntervalId() external view returns (uint128);

    function setPool(address _leveragedPool) external;

    function setBurningFee(uint256 _burningFee) external;

    function setMintingFee(uint256 _mintingFee) external;

    function setChangeInterval(uint256 _changeInterval) external;

    function setFeeController(address _feeController) external;
}

File 5 of 31 : 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 6 of 31 : IAutoClaim.sol
//SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity 0.8.7;

interface IAutoClaim {
    /**
     * @notice Creates a notification when an auto-claim request is updated. i.e. When another commit is added and reward is incremented.
     * @param user The user whose request got updated
     * @param poolCommitter The PoolCommitter instance in which the commits were made
     * @param updateIntervalId The update interval ID that the corresponding commitment was allocated for
     * @param newReward The new total reward for the auto-claim
     */
    event PaidClaimRequestUpdate(
        address indexed user,
        address indexed poolCommitter,
        uint256 indexed updateIntervalId,
        uint256 newReward
    );

    /**
     * @notice Creates a notification when an auto-claim request is executed
     * @param user The user whose request got executed
     * @param poolCommitter The PoolCommitter instance in which the original commit was made
     * @param reward The reward for the auto-claim
     */
    event PaidRequestExecution(address indexed user, address indexed poolCommitter, uint256 indexed reward);

    /**
     * @notice Creates a notification when an auto-claim request is withdrawn
     * @param user The user whose request got withdrawn
     * @param poolCommitter The PoolCommitter instance in which the original commit was made
     */
    event RequestWithdrawn(address indexed user, address indexed poolCommitter);

    struct ClaimRequest {
        uint128 updateIntervalId; // The update interval during which a user requested a claim.
        uint256 reward; // The amount of ETH in wei that was given by the user to pay for upkeep
    }

    /**
     * @notice Pay for your commit to be claimed. This means that a willing participant can claim on `user`'s behalf when the current update interval ends.
     * @dev Only callable by this contract's associated PoolCommitter instance. This prevents griefing. Consider a permissionless function, where a user can claim that somebody else wants to auto claim when they do not.
     * @param user The user who wants to autoclaim.
     */
    function makePaidClaimRequest(address user) external payable;

    /**
     * @notice Claim on the behalf of a user who has requests to have their commit automatically claimed by a keeper.
     * @param user The user who requested an autoclaim.
     * @param poolCommitterAddress The PoolCommitter address within which the user's claim will be executed
     */
    function paidClaim(address user, address poolCommitterAddress) external;

    function multiPaidClaimMultiplePoolCommitters(bytes memory args1, bytes memory args2) external;

    /**
     * @notice Call `paidClaim` for multiple users, in a single PoolCommitter.
     * @param args Arguments for the function packed into a bytes array. Generated with L2Encoder.encode
     * -------------------------------------------------------------------------------------------------------------------------
     * |          20 bytes          |          20 bytes         |          20 bytes          |          20 bytes         | ... |
     * |      0th user address      |     1st user address      |      3rd user address      |      4th user address     | ... |
     * -------------------------------------------------------------------------------------------------------------------------
     * @param poolCommitterAddress The PoolCommitter address within which you would like to claim for the respective user
     * @dev poolCommitterAddress should be the PoolCommitter where the all supplied user addresses requested an auto claim
     */
    function multiPaidClaimSinglePoolCommitter(bytes calldata args, address poolCommitterAddress) external;

    /**
     * @notice If a user's claim request never gets executed (due to not high enough of a reward), or they change their minds, enable them to withdraw their request.
     * @param poolCommitter The PoolCommitter for which the user's commit claim is to be withdrawn.
     */
    function withdrawClaimRequest(address poolCommitter) external;

    /**
     * @notice When the user claims themself through poolCommitter, you want the user to be able to withdraw their request through the poolCommitter as msg.sender
     * @param user The user who will have their claim request withdrawn.
     */
    function withdrawUserClaimRequest(address user) external;

    /**
     * @notice Check the validity of a user's claim request for a given pool committer.
     * @return true if the claim request can be executed.
     * @param user The user whose claim request will be checked.
     * @param poolCommitter The pool committer in which to look for a user's claim request.
     */
    function checkUserClaim(address user, address poolCommitter) external view returns (bool);

    /**
     * @return true if the given claim request can be executed.
     * @dev A claim request can be executed only if one exists and is from an update interval that has passed.
     * @param request The ClaimRequest object to be checked.
     * @param currentUpdateIntervalId The current update interval. Used to compare to the update interval of the ClaimRequest.
     */
    function checkClaim(ClaimRequest memory request, uint256 currentUpdateIntervalId) external pure returns (bool);
}

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

interface ITwoStepGovernance {
    /**
     * @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 governance() external returns (address);

    function provisionalGovernance() external returns (address);

    function governanceTransferInProgress() external returns (bool);

    function transferGovernance(address _governance) external;

    function claimGovernance() external;
}

File 8 of 31 : LeveragedPool.sol
//SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity 0.8.7;

import "../interfaces/ILeveragedPool.sol";
import "../interfaces/IPoolCommitter.sol";
import "../interfaces/IPoolToken.sol";
import "../interfaces/IPoolKeeper.sol";
import "../interfaces/IInvariantCheck.sol";
import "../interfaces/IPausable.sol";
import "../interfaces/ITwoStepGovernance.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import "../libraries/PoolSwapLibrary.sol";
import "../interfaces/IOracleWrapper.sol";

/// @title The pool contract itself
contract LeveragedPool is ILeveragedPool, Initializable, IPausable, ITwoStepGovernance {
    using SafeERC20 for IERC20;
    // #### Globals

    // Each balance is the amount of settlement tokens in the pair
    uint256 public override shortBalance;
    uint256 public override longBalance;
    uint256 public constant LONG_INDEX = 0;
    uint256 public constant SHORT_INDEX = 1;

    address public override governance;
    address public invariantCheck;
    uint32 public override frontRunningInterval;
    uint32 public override updateInterval;
    bytes16 public fee;

    bytes16 public override leverageAmount;
    address public override provisionalGovernance;
    bool public override paused;
    bool public override governanceTransferInProgress;
    address public keeper;
    // When feeAddress changes, all prior fees are assigned to the new address
    address public feeAddress;
    address public secondaryFeeAddress;
    uint256 public secondaryFeeSplitPercent; // Split to secondary fee address as a percentage.
    // Amount of fees assigned to either feeAddress (primaryFees), or secondaryFeeAddress (secondaryFees)
    uint256 public override primaryFees;
    uint256 public override secondaryFees;
    address public override settlementToken;
    address public override poolCommitter;
    address public override oracleWrapper;
    address public override settlementEthOracle;
    address[2] public tokens;
    uint256 public override lastPriceTimestamp; // The last time the pool was upkept

    string public override poolName;

    // #### Modifiers

    modifier onlyKeeper() {
        require(msg.sender == keeper, "msg.sender not keeper");
        _;
    }

    modifier onlyKeeperRewards() {
        require(msg.sender == IPoolKeeper(keeper).keeperRewards(), "msg.sender not keeperRewards");
        _;
    }

    modifier onlyPoolCommitter() {
        require(msg.sender == poolCommitter, "msg.sender not poolCommitter");
        _;
    }

    modifier onlyGov() {
        require(msg.sender == governance, "msg.sender not governance");
        _;
    }

    modifier onlyInvariantCheckContract() {
        require(msg.sender == invariantCheck, "msg.sender not invariantCheck");
        _;
    }

    modifier onlyUnpaused() {
        require(!paused, "Pool is paused");
        _;
    }

    // #### Functions

    function initialize(ILeveragedPool.Initialization calldata initialization) external override initializer {
        require(initialization._feeAddress != address(0), "Fee cannot be null");
        require(initialization._settlementToken != address(0), "Settlement token cannot be null");
        require(initialization._oracleWrapper != address(0), "Oracle wrapper cannot be null");
        require(initialization._settlementEthOracle != address(0), "Keeper oracle cannot be null");
        require(initialization._owner != address(0), "Owner cannot be null");
        require(initialization._keeper != address(0), "Keeper cannot be null");
        require(initialization._longToken != address(0), "Long token cannot be null");
        require(initialization._shortToken != address(0), "Short token cannot be null");
        require(initialization._poolCommitter != address(0), "PoolCommitter cannot be null");
        require(initialization._invariantCheck != address(0), "InvariantCheck cannot be null");
        require(initialization._fee < PoolSwapLibrary.WAD_PRECISION, "Fee >= 100%");
        require(initialization._secondaryFeeSplitPercent <= 100, "Secondary fee split cannot exceed 100%");
        require(initialization._updateInterval != 0, "Update interval cannot be 0");

        // set the owner of the pool. This is governance when deployed from the factory
        governance = initialization._owner;

        // Setup variables
        keeper = initialization._keeper;
        oracleWrapper = initialization._oracleWrapper;
        settlementEthOracle = initialization._settlementEthOracle;
        settlementToken = initialization._settlementToken;
        invariantCheck = initialization._invariantCheck;
        frontRunningInterval = initialization._frontRunningInterval;
        updateInterval = initialization._updateInterval;
        fee = PoolSwapLibrary.convertUIntToDecimal(initialization._fee);
        leverageAmount = PoolSwapLibrary.convertUIntToDecimal(initialization._leverageAmount);
        feeAddress = initialization._feeAddress;
        secondaryFeeAddress = initialization._secondaryFeeAddress;
        secondaryFeeSplitPercent = initialization._secondaryFeeSplitPercent;
        lastPriceTimestamp = block.timestamp;
        poolName = initialization._poolName;
        tokens[LONG_INDEX] = initialization._longToken;
        tokens[SHORT_INDEX] = initialization._shortToken;
        poolCommitter = initialization._poolCommitter;
        emit PoolInitialized(
            initialization._longToken,
            initialization._shortToken,
            initialization._settlementToken,
            initialization._poolName
        );
    }

    /**
     * @notice Execute a price change
     * @param _oldPrice Previous price of the underlying asset
     * @param _newPrice New price of the underlying asset
     * @dev Throws if at least one update interval has not elapsed since last price update
     * @dev This is the entry point to upkeep a market
     * @dev Only callable by the associated `PoolKeeper` contract
     * @dev Only callable if the market is *not* paused
     */
    function poolUpkeep(int256 _oldPrice, int256 _newPrice) external override onlyKeeper onlyUnpaused {
        require(intervalPassed(), "Update interval hasn't passed");
        // We update `lastPriceTimestamp` to prevent a reentrancy.
        // We want to guarantee on any potential reentrancy, `intervalPassed` will fail. To do this, we temporarily set `lastPriceTimestamp` to `block.timestamp`.
        // It can then be set to the appropriate value as a result of executeCommitments afterwards.
        uint256 oldPriceTimestamp = lastPriceTimestamp;
        lastPriceTimestamp = block.timestamp;
        // perform price change and update pool balances
        executePriceChange(_oldPrice, _newPrice);
        (
            uint256 longMintAmount,
            uint256 shortMintAmount,
            uint256 newLongBalance,
            uint256 newShortBalance,
            uint256 newLastPriceTimestamp
        ) = IPoolCommitter(poolCommitter).executeCommitments(
                oldPriceTimestamp,
                updateInterval,
                longBalance,
                shortBalance
            );
        lastPriceTimestamp = newLastPriceTimestamp;
        longBalance = newLongBalance;
        shortBalance = newShortBalance;
        if (longMintAmount > 0) {
            IPoolToken(tokens[LONG_INDEX]).mint(address(this), longMintAmount);
        }
        if (shortMintAmount > 0) {
            IPoolToken(tokens[SHORT_INDEX]).mint(address(this), shortMintAmount);
        }
    }

    /**
     * @notice Pay keeper some amount in the settlement token for the perpetual pools market
     * @param to Address of the pool keeper to pay
     * @param amount Amount to pay the pool keeper
     * @return Whether the keeper is going to be paid; false if the amount exceeds the balances of the
     *         long and short pool, and true if the keeper can successfully be paid out
     * @dev Only callable by the associated `PoolKeeper` contract
     * @dev Only callable when the market is *not* paused
     */
    function payKeeperFromBalances(address to, uint256 amount)
        external
        override
        onlyKeeperRewards
        onlyUnpaused
        returns (bool)
    {
        uint256 _shortBalance = shortBalance;
        uint256 _longBalance = longBalance;

        // If the rewards are greater than the or equal to balances of the pool, the keeper does not get paid.
        // shortBalance and longBalance can not sum to 0, because getBalanceAfterFees will revert.
        if (amount >= _shortBalance + _longBalance) {
            return false;
        }

        (shortBalance, longBalance) = PoolSwapLibrary.getBalancesAfterFees(amount, _shortBalance, _longBalance);

        // Pay keeper
        IERC20(settlementToken).safeTransfer(to, amount);

        return true;
    }

    /**
     * @notice Transfer settlement tokens from pool to user
     * @param to Address of account to transfer to
     * @param amount Amount of settlement tokens being transferred
     * @dev Only callable by the associated `PoolCommitter` contract
     * @dev Only callable when the market is *not* paused
     */
    function settlementTokenTransfer(address to, uint256 amount) external override onlyPoolCommitter onlyUnpaused {
        IERC20(settlementToken).safeTransfer(to, amount);
    }

    /**
     * @notice Transfer pool tokens from pool to user
     * @param isLongToken True if transferring long pool token; False if transferring short pool token
     * @param to Address of account to transfer to
     * @param amount Amount of pool tokens being transferred
     * @dev Only callable by the associated `PoolCommitter` contract
     * @dev Only callable when the market is *not* paused
     */
    function poolTokenTransfer(
        bool isLongToken,
        address to,
        uint256 amount
    ) external override onlyPoolCommitter onlyUnpaused {
        if (isLongToken) {
            IERC20(tokens[LONG_INDEX]).safeTransfer(to, amount);
        } else {
            IERC20(tokens[SHORT_INDEX]).safeTransfer(to, amount);
        }
    }

    /**
     * @notice Transfer tokens from user to account
     * @param from The account that's transferring settlement tokens
     * @param to Address of account to transfer to
     * @param amount Amount of settlement tokens being transferred
     * @dev Only callable by the associated `PoolCommitter` contract
     * @dev Only callable when the market is *not* paused
     */
    function settlementTokenTransferFrom(
        address from,
        address to,
        uint256 amount
    ) external override onlyPoolCommitter onlyUnpaused {
        IERC20(settlementToken).safeTransferFrom(from, to, amount);
    }

    /**
     * @notice Execute the price change once the interval period ticks over, updating the long & short
     *         balances based on the change of the feed (upwards or downwards) and paying fees
     * @param _oldPrice Old price from the oracle
     * @param _newPrice New price from the oracle
     * @dev Can only be called by poolUpkeep
     * @dev Only callable when the market is *not* paused
     * @dev Emits `PoolRebalance` if execution succeeds
     * @dev Emits `PriceChangeError` if execution does not take place
     */
    function executePriceChange(int256 _oldPrice, int256 _newPrice) internal {
        // prevent a division by 0 in computing the price change
        // prevent negative pricing
        if (_oldPrice <= 0 || _newPrice <= 0) {
            emit PriceChangeError(_oldPrice, _newPrice);
        } else {
            uint256 _shortBalance = shortBalance;
            uint256 _longBalance = longBalance;
            (
                uint256 newLongBalance,
                uint256 newShortBalance,
                uint256 longFeeAmount,
                uint256 shortFeeAmount
            ) = PoolSwapLibrary.calculateValueTransfer(
                    _longBalance,
                    _shortBalance,
                    leverageAmount,
                    _oldPrice,
                    _newPrice,
                    fee
                );

            unchecked {
                emit PoolRebalance(
                    int256(newShortBalance) - int256(_shortBalance),
                    int256(newLongBalance) - int256(_longBalance),
                    shortFeeAmount,
                    longFeeAmount
                );
            }
            // Update pool balances
            longBalance = newLongBalance;
            shortBalance = newShortBalance;
            // Pay the fee
            feeTransfer(longFeeAmount + shortFeeAmount);
        }
    }

    /**
     * @notice Transfer primary fees to the primary fee address
     * @dev Calls ERC20.safeTransfer on the settlement token
     * @dev Emits a PrimaryFeesPaid event
     */
    function claimPrimaryFees() external override {
        uint256 tempPrimaryFees = primaryFees;
        primaryFees = 0;
        IERC20(settlementToken).safeTransfer(feeAddress, tempPrimaryFees);
        emit PrimaryFeesPaid(feeAddress, tempPrimaryFees);
    }

    /**
     * @notice Transfer secondary fees to the secondary fee address
     * @dev Calls ERC20.safeTransfer on the settlement token
     * @dev Emits a SecondaryFeesPaid event
     */
    function claimSecondaryFees() external override {
        uint256 tempSecondaryFees = secondaryFees;
        secondaryFees = 0;
        IERC20(settlementToken).safeTransfer(secondaryFeeAddress, tempSecondaryFees);
        emit SecondaryFeesPaid(secondaryFeeAddress, tempSecondaryFees);
    }

    /**
     * @notice Increment fee amounts. Allows primary or secondary fees to be claimed with either `claimPrimaryFees` or `claimSecondaryFees` respectively.
     *         If the DAO is the fee deployer, secondary fee address should be address(0) and all fees go to DAO.
     * @param totalFeeAmount total amount of fees paid
     */
    function feeTransfer(uint256 totalFeeAmount) internal {
        if (secondaryFeeAddress == address(0)) {
            unchecked {
                // Overflow would require more than settlement's entire total supply
                primaryFees += totalFeeAmount;
            }
        } else {
            uint256 secondaryFeeAmount = PoolSwapLibrary.mulFraction(totalFeeAmount, secondaryFeeSplitPercent, 100);
            uint256 remainder;
            unchecked {
                // secondaryFee is calculated as totalFeeAmount * secondaryFeeSplitPercent / 100
                // secondaryFeeSplitPercent <= 100 and therefore secondaryFee <= totalFeeAmount - The following line can not underflow
                remainder = totalFeeAmount - secondaryFeeAmount;
                // Overflow would require more than settlement's entire total supply
                secondaryFees += secondaryFeeAmount;
                primaryFees += remainder;
            }
        }
    }

    /**
     * @notice Sets the long and short balances of the pools
     * @param _longBalance New balance of the long pool
     * @param _shortBalance New balance of the short pool
     * @dev Only callable by the associated `PoolCommitter` contract
     * @dev Only callable when the market is *not* paused
     * @dev Emits a `PoolBalancesChanged` event on success
     */
    function setNewPoolBalances(uint256 _longBalance, uint256 _shortBalance)
        external
        override
        onlyPoolCommitter
        onlyUnpaused
    {
        longBalance = _longBalance;
        shortBalance = _shortBalance;
        emit PoolBalancesChanged(_longBalance, _shortBalance);
    }

    /**
     * @notice Burn tokens by a user
     * @dev Can only be called by & used by the pool committer
     * @param tokenType LONG_INDEX (0) or SHORT_INDEX (1) for either burning the long or short  token respectively
     * @param amount Amount of tokens to burn
     * @param burner Address of user/burner
     * @dev Only callable by the associated `PoolCommitter` contract
     * @dev Only callable when the market is *not* paused
     */
    function burnTokens(
        uint256 tokenType,
        uint256 amount,
        address burner
    ) external override onlyPoolCommitter onlyUnpaused {
        IPoolToken(tokens[tokenType]).burn(burner, amount);
    }

    /**
     * @notice Indicates whether the price was last updated more than `updateInterval` seconds ago
     * @return Whether the price was last updated more than `updateInterval` seconds ago
     * @dev Unchecked
     */
    function intervalPassed() public view override returns (bool) {
        unchecked {
            return block.timestamp >= lastPriceTimestamp + updateInterval;
        }
    }

    /**
     * @notice Updates the fee address of the pool
     * @param account New address of the fee address/receiver
     * @dev Only callable by governance
     * @dev Only callable when the market is *not* paused
     * @dev Emits `FeeAddressUpdated` event on success
     */
    function updateFeeAddress(address account) external override onlyGov onlyUnpaused {
        require(account != address(0), "Account cannot be null");
        emit FeeAddressUpdated(feeAddress, account);
        feeAddress = account;
    }

    /**
     * @notice Updates the secondary fee address of the pool
     * @param account New address of the fee address/receiver
     */
    function updateSecondaryFeeAddress(address account) external override {
        require(msg.sender == secondaryFeeAddress);
        emit SecondaryFeeAddressUpdated(secondaryFeeAddress, account);
        secondaryFeeAddress = account;
    }

    /**
     * @notice Updates the keeper contract of the pool
     * @param _keeper New address of the keeper contract
     */
    function setKeeper(address _keeper) external override onlyGov {
        require(_keeper != address(0), "Keeper cannot be null");
        emit KeeperAddressChanged(keeper, _keeper);
        keeper = _keeper;
    }

    /**
     * @notice Starts to transfer governance of the pool. The new governance
     *          address must call `claimGovernance` in order for this to take
     *          effect. Until this occurs, the existing governance address
     *          remains in control of the pool.
     * @param _governance New address of the governance of the pool
     * @dev First step of the two-step governance transfer process
     * @dev Sets the governance transfer flag to true
     * @dev See `claimGovernance`
     */
    function transferGovernance(address _governance) external override onlyGov {
        require(_governance != governance, "New governance address cannot be same as old governance address");
        require(_governance != address(0), "Governance cannot be null");
        provisionalGovernance = _governance;
        governanceTransferInProgress = true;
        emit ProvisionalGovernanceChanged(_governance);
    }

    /**
     * @notice Completes transfer of governance by actually changing permissions
     *          over the pool.
     * @dev Second and final step of the two-step governance transfer process
     * @dev See `transferGovernance`
     * @dev Sets the governance transfer flag to false
     * @dev After a successful call to this function, the actual governance
     *      address and the provisional governance address MUST be equal.
     */
    function claimGovernance() external override {
        require(governanceTransferInProgress, "No governance change active");
        address _provisionalGovernance = provisionalGovernance;
        require(msg.sender == _provisionalGovernance, "Not provisional governor");
        emit GovernanceAddressChanged(governance, _provisionalGovernance);
        governance = _provisionalGovernance;
        delete provisionalGovernance;
        governanceTransferInProgress = false;
    }

    /**
     * @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
        override
        returns (
            int256,
            bytes memory,
            uint256,
            uint256
        )
    {
        (int256 _latestPrice, bytes memory _data) = IOracleWrapper(oracleWrapper).getPriceAndMetadata();
        return (_latestPrice, _data, lastPriceTimestamp, updateInterval);
    }

    /**
     * @return The price of the pool's feed oracle
     */
    function getOraclePrice() external view override returns (int256) {
        return IOracleWrapper(oracleWrapper).getPrice();
    }

    /**
     * @return The pools management fee
     */
    function getFee() external view returns (uint256) {
        return PoolSwapLibrary.convertDecimalToUInt(fee);
    }

    /**
     * @return The pools leverage as a readable uint
     */
    function getLeverage() external view returns (uint256) {
        return PoolSwapLibrary.convertDecimalToUInt(leverageAmount);
    }

    /**
     * @return Addresses of the pool tokens for this pool (long and short,
     *          respectively)
     */
    function poolTokens() external view override returns (address[2] memory) {
        return tokens;
    }

    /**
     * @return Quantities of pool tokens for this pool (short and long,
     *          respectively)
     */
    function balances() external view override returns (uint256, uint256) {
        return (shortBalance, longBalance);
    }

    /**
     * @notice Withdraws all available settlement asset from the pool
     * @dev Pool must be paused
     * @dev ERC20 transfer
     * @dev Only callable by governance
     */
    function withdrawSettlement() external onlyGov {
        require(paused, "Pool is live");
        IERC20 settlementERC = IERC20(settlementToken);
        uint256 balance = settlementERC.balanceOf(address(this));
        IERC20(settlementToken).safeTransfer(msg.sender, balance);
        emit SettlementWithdrawn(msg.sender, balance);
    }

    /**
     * @notice Pauses the pool
     * @dev Prevents all state updates until unpaused
     */
    function pause() external override onlyInvariantCheckContract {
        paused = true;
        emit Paused();
    }

    /**
     * @notice Unpauses the pool
     * @dev Prevents all state updates until unpaused
     */
    function unpause() external override onlyGov {
        paused = false;
        emit Unpaused();
    }
}

File 9 of 31 : PoolToken.sol
//SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity 0.8.7;

import "../vendors/ERC20_Cloneable.sol";
import "../interfaces/IPoolToken.sol";

/// @title The pool token; used for ownership/shares of the underlying tokens of the long/short pool
/// @dev ERC_20_Cloneable contains onlyOwner code implemented for use with the cloneable setup
contract PoolToken is ERC20_Cloneable, IPoolToken {
    // #### Functions
    constructor(uint8 _decimals) ERC20_Cloneable("BASE_TOKEN", "BASE", _decimals) {}

    /**
     * @notice Mints pool tokens
     * @param account Account to mint pool tokens to
     * @param amount Pool tokens to mint
     */
    function mint(address account, uint256 amount) external override onlyOwner {
        _mint(account, amount);
    }

    /**
     * @notice Burns pool tokens
     * @param account Account to burn pool tokens from
     * @param amount Pool tokens to burn
     */
    function burn(address account, uint256 amount) external override onlyOwner {
        _burn(account, amount);
    }
}

File 10 of 31 : 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/IKeeperRewards.sol";

import "../libraries/CalldataLogic.sol";

import "@openzeppelin/contracts/access/Ownable.sol";
import "abdk-libraries-solidity/ABDKMathQuad.sol";

/// @title The manager contract for multiple markets and the pools in them
/// @dev Currently, this contract estimates the best keeper rewards in a way that is best suited for Ethereum L1.
/// @dev It assumes an approximate block time of 13 seconds, and an Ethereum-like gas system.
/// @dev This code was also written with Arbitrum deployment in mind, meaning there exists no `block.basefee`, and no arbitrum gas price oracle.
/// @dev It has another large drawback in that it is not possible to calculate the cost of the current transaction Arbitrum, given that the cost is largely determined by L1 calldata cost.
/// @dev Because of this, the reward calculation is an rough "good enough" estimation.
contract PoolKeeper is IPoolKeeper, Ownable {
    // #### Global variables
    /**
     * @notice Format: Pool address => last executionPrice
     */
    mapping(address => int256) public executionPrice;

    IPoolFactory public immutable factory;
    // The KeeperRewards contract permissioned to pay out pool upkeep rewards
    address public override keeperRewards;

    uint256 public gasPrice = 10 gwei;

    /**
     * @notice Ensures that the caller is the associated `PoolFactory` contract
     */
    modifier onlyFactory() {
        require(msg.sender == address(factory), "Caller not factory");
        _;
    }

    // #### Functions
    constructor(address _factory) {
        require(_factory != address(0), "Factory cannot be null");
        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
     * @dev Only callable by the associated `PoolFactory` contract
     */
    function newPool(address _poolAddress) external override onlyFactory {
        IOracleWrapper(ILeveragedPool(_poolAddress).oracleWrapper()).poll();
        int256 firstPrice = ILeveragedPool(_poolAddress).getOraclePrice();
        require(firstPrice > 0, "First price is non-positive");
        emit PoolAdded(_poolAddress, firstPrice);
        executionPrice[_poolAddress] = firstPrice;
    }

    /**
     * @notice Check if upkeep is required
     * @param _pool The address of the pool to upkeep
     * @return Whether or not upkeep is needed for this single pool
     */
    function isUpkeepRequiredSinglePool(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 Array of pools to check
     * @return Whether or not at least one pool needs upkeeping
     * @dev Iterates over the provided array of pool addresses
     */
    function checkUpkeepMultiplePools(address[] calldata _pools) external view override returns (bool) {
        uint256 poolsLength = _pools.length;
        for (uint256 i = 0; i < poolsLength; i = unchecked_inc(i)) {
            if (isUpkeepRequiredSinglePool(_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 Address of the pool to be upkept
     * @dev Tracks gas usage via `gasleft` accounting and uses this to inform
     *          keeper payment
     * @dev Catches any failure of the underlying `pool.poolUpkeep` call
     * @dev Emits a `KeeperPaid` event if the underlying call to `pool.payKeeperFromBalances` succeeds
     * @dev Emits a `KeeperPaymentError` event otherwise
     */
    function performUpkeepSinglePool(address _pool) public override {
        uint256 startGas = gasleft();

        // validate the pool, check that the interval time has passed
        if (!isUpkeepRequiredSinglePool(_pool)) {
            return;
        }

        /* update SMA oracle, does nothing for spot oracles */
        IOracleWrapper poolOracleWrapper = IOracleWrapper(ILeveragedPool(_pool).oracleWrapper());

        try poolOracleWrapper.poll() {} catch Error(string memory reason) {
            emit PoolUpkeepError(_pool, reason);
        }

        (
            int256 latestPrice,
            bytes memory data,
            uint256 savedPreviousUpdatedTimestamp,
            uint256 updateInterval
        ) = ILeveragedPool(_pool).getUpkeepInformation();

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

        /* This allows us to still batch multiple calls to
         * executePriceChange, even if some are invalid
         * without reverting the entire transaction */
        try ILeveragedPool(_pool).poolUpkeep(lastExecutionPrice, latestPrice) {
            executionPrice[_pool] = latestPrice;
            // If poolUpkeep is successful, refund the keeper for their gas costs
            emit UpkeepSuccessful(_pool, data, lastExecutionPrice, latestPrice);
        } catch Error(string memory reason) {
            // If poolUpkeep fails for any other reason, emit event
            emit PoolUpkeepError(_pool, reason);
        }

        uint256 gasSpent = startGas - gasleft();
        uint256 reward = IKeeperRewards(keeperRewards).payKeeper(
            msg.sender,
            _pool,
            gasPrice,
            gasSpent,
            savedPreviousUpdatedTimestamp,
            updateInterval
        );
        // Emit events depending on whether or not the reward was actually paid
        if (reward > 0) {
            emit KeeperPaid(_pool, msg.sender, reward);
        } else {
            emit KeeperPaymentError(_pool, msg.sender, reward);
        }
    }

    /**
     * @notice Called by keepers to perform an update on multiple pools
     * @param pools Addresses of each pool to upkeep
     * @dev Iterates over the provided array
     * @dev Essentially wraps calls to `performUpkeepSinglePool`
     */
    function performUpkeepMultiplePools(address[] calldata pools) external override {
        uint256 poolsLength = pools.length;
        for (uint256 i = 0; i < poolsLength; i = unchecked_inc(i)) {
            performUpkeepSinglePool(pools[i]);
        }
    }

    /**
     * @notice Changes the KeeperRewards contract, used for calculating and executing rewards for calls to upkeep functions
     * @param _keeperRewards The new KeeperRewards contract
     * @dev Only callable by the contract owner
     * @dev emits KeeperRewardsSet when the addresss is successfuly changed
     */
    function setKeeperRewards(address _keeperRewards) external override onlyOwner {
        require(_keeperRewards != address(0), "KeeperRewards cannot be 0 address");
        address oldKeeperRewards = keeperRewards;
        keeperRewards = _keeperRewards;
        emit KeeperRewardsSet(oldKeeperRewards, _keeperRewards);
    }

    /**
     * @notice Called by keepers to perform an update on multiple pools
     * @param pools A tightly packed bytes array of LeveragedPool addresses to be upkept
     *  __________________________________________________
     * |   20 bytes       20 bytes       20 bytes     ... |
     * | pool address | pool address | pool address | ... |
     *  ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
     * @dev Arguments can be encoded with `L2Encoder.encodeAddressArray`
     * @dev Will revert if the bytes array is a correct length (some multiple of 20 bytes)
     */
    function performUpkeepMultiplePoolsPacked(bytes calldata pools) external override {
        require(pools.length % CalldataLogic.ADDRESS_LENGTH == 0, "Data must only include addresses");
        uint256 numPools = pools.length / CalldataLogic.ADDRESS_LENGTH;
        uint256 offset;
        assembly {
            offset := pools.offset
        }
        for (uint256 i = 0; i < numPools; ) {
            performUpkeepSinglePool(CalldataLogic.getAddressAtOffset(offset));
            unchecked {
                offset += CalldataLogic.ADDRESS_LENGTH;
                ++i;
            }
        }
    }

    /**
     * @notice Sets the gas price to be used in compensating keepers for successful upkeep
     * @param _price Price (in ETH) per unit gas
     * @dev Only callable by the owner
     * @dev This function is only necessary due to the L2 deployment of Pools -- in reality, it should be `BASEFEE`
     * @dev Emits a `GasPriceChanged` event on success
     */
    function setGasPrice(uint256 _price) external override onlyOwner {
        gasPrice = _price;
        emit GasPriceChanged(_price);
    }

    function unchecked_inc(uint256 i) private pure returns (uint256) {
        unchecked {
            return ++i;
        }
    }
}

File 11 of 31 : PoolCommitter.sol
//SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity 0.8.7;

import "../interfaces/IPoolCommitter.sol";
import "../interfaces/ILeveragedPool.sol";
import "../interfaces/IPoolFactory.sol";
import "../interfaces/IAutoClaim.sol";
import "../interfaces/IPausable.sol";
import "../interfaces/IInvariantCheck.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import "../libraries/PoolSwapLibrary.sol";
import "../libraries/CalldataLogic.sol";

/// @title This contract is responsible for handling commitment logic
contract PoolCommitter is IPoolCommitter, IPausable, Initializable {
    // #### Globals
    uint128 public constant LONG_INDEX = 0;
    uint128 public constant SHORT_INDEX = 1;
    // Set max minting fee to 100%. This is a ABDKQuad representation of 1 * 10 ** 18
    bytes16 public constant MAX_MINTING_FEE = 0x403abc16d674ec800000000000000000;
    // Set max burning fee to 10%. This is a ABDKQuad representation of 0.1 * 10 ** 18
    bytes16 public constant MAX_BURNING_FEE = 0x40376345785d8a000000000000000000;
    // Maximum changeInterval is the theoretical maximum change to the minting fee in one update interval.
    bytes16 public constant MAX_CHANGE_INTERVAL = MAX_MINTING_FEE;

    // 15 was chosen because it will definitely fit in a block on Arbitrum which can be tricky to ascertain definitive computation cap without trial and error, while it is also a reasonable number of upkeeps to get executed in one transaction
    uint8 public constant MAX_ITERATIONS = 15;
    IAutoClaim public autoClaim;
    uint128 public override updateIntervalId = 1;
    // The amount that is extracted from each mint and burn, being left in the pool. Given as the decimal * 10 ^ 18. For example, 60% fee is 0.6 * 10 ^ 18
    // Fees can be 0.
    bytes16 public mintingFee;
    bytes16 public burningFee;
    // The amount that the `mintingFee` will change each update interval, based on `updateMintingFee`, given as a decimal * 10 ^ 18 (same format as `_mintingFee`)
    bytes16 public changeInterval;

    // Index 0 is the LONG token, index 1 is the SHORT token.
    // Fetched from the LeveragedPool when leveragedPool is set
    address[2] public tokens;

    mapping(uint256 => Prices) public priceHistory; // updateIntervalId => tokenPrice
    mapping(uint256 => bytes16) public burnFeeHistory; // updateIntervalId => burn fee. We need to store this historically because people can claim at any time after the update interval, but we want them to pay the fee from the update interval in which they committed.
    mapping(address => Balance) public userAggregateBalance;

    // The total amount of settlement that has been committed to mints that are not yet executed
    uint256 public override pendingMintSettlementAmount;
    // The total amount of short pool tokens that have been burnt that are not yet executed on
    uint256 public override pendingShortBurnPoolTokens;
    // The total amount of long pool tokens that have been burnt that are not yet executed on
    uint256 public override pendingLongBurnPoolTokens;
    // Update interval ID => TotalCommitment
    mapping(uint256 => TotalCommitment) public totalPoolCommitments;
    // Address => Update interval ID => UserCommitment
    mapping(address => mapping(uint256 => UserCommitment)) public userCommitments;
    // The last interval ID for which a given user's balance was updated
    mapping(address => uint256) public lastUpdatedIntervalId;
    // An array for all update intervals in which a user committed
    mapping(address => uint256[]) public unAggregatedCommitments;
    // Used to create a dynamic array that is used to copy the new unAggregatedCommitments array into the mapping after updating balance
    uint256[] private storageArrayPlaceHolder;

    address public factory;
    address public governance;
    address public feeController;
    address public leveragedPool;
    address public invariantCheck;
    bool public override paused;

    modifier onlyFeeController() {
        require(msg.sender == feeController, "msg.sender not fee controller");
        _;
    }

    modifier onlyUnpaused() {
        require(!paused, "Pool is paused");
        _;
    }

    modifier onlyGov() {
        require(msg.sender == governance, "msg.sender not governance");
        _;
    }

    /**
     * @notice Asserts that the caller is the associated `PoolFactory` contract
     */
    modifier onlyFactory() {
        require(msg.sender == factory, "Committer: not factory");
        _;
    }

    /**
     * @notice Asserts that the caller is the associated `LeveragedPool` contract
     */
    modifier onlyPool() {
        require(msg.sender == leveragedPool, "msg.sender not leveragedPool");
        _;
    }

    modifier onlyInvariantCheckContract() {
        require(msg.sender == invariantCheck, "msg.sender not invariantCheck");
        _;
    }

    modifier onlyAutoClaimOrCommitter(address user) {
        require(msg.sender == user || msg.sender == address(autoClaim), "msg.sender not committer or AutoClaim");
        _;
    }

    /**
     * @notice Determines whether the provided commitment type represents a
     *          mint
     * @return Boolean indicating if `t` is mint
     */
    function isMint(CommitType t) external pure override returns (bool) {
        return t == CommitType.LongMint || t == CommitType.ShortMint;
    }

    /**
     * @notice Determines whether the provided commitment type represents a
     *          burn
     * @return Boolean indicating if `t` is burn
     */
    function isBurn(CommitType t) external pure override returns (bool) {
        return t == CommitType.LongBurn || t == CommitType.ShortBurn;
    }

    /**
     * @notice Determines whether the provided commitment type represents a
     *          long
     * @return Boolean indicating if `t` is long
     */
    function isLong(CommitType t) external pure override returns (bool) {
        return t == CommitType.LongMint || t == CommitType.LongBurn;
    }

    /**
     * @notice Determines whether the provided commitment type represents a
     *          short
     * @return Boolean indicating if `t` is short
     */
    function isShort(CommitType t) external pure override returns (bool) {
        return t == CommitType.ShortMint || t == CommitType.ShortBurn;
    }

    /**
     * @notice Initialises the contract
     * @param _factory Address of the associated `PoolFactory` contract
     * @param _autoClaim Address of the associated `AutoClaim` contract
     * @param _factoryOwner Address of the owner of the `PoolFactory`
     * @param _invariantCheck Address of the `InvariantCheck` contract
     * @param _mintingFee The percentage that is taken from each mint, given as a decimal * 10 ^ 18
     * @param _burningFee The percentage that is taken from each burn, given as a decimal * 10 ^ 18
     * @param _changeInterval The amount that the `mintingFee` will change each update interval, based on `updateMintingFee`, given as a decimal * 10 ^ 18 (same format as `_mintingFee`)
     * @dev Throws if factory contract address is null
     * @dev Throws if autoClaim contract address is null
     * @dev Throws if autoclaim contract address is null
     * @dev Only callable by the associated initializer address
     * @dev Throws if minting fee is over MAX_MINTING_FEE
     * @dev Throws if burning fee is over MAX_BURNING_FEE
     * @dev Throws if changeInterval is over MAX_CHANGE_INTERVAL
     * @dev Emits a `ChangeIntervalSet` event on success
     */
    function initialize(
        address _factory,
        address _autoClaim,
        address _factoryOwner,
        address _feeController,
        address _invariantCheck,
        uint256 _mintingFee,
        uint256 _burningFee,
        uint256 _changeInterval
    ) external override initializer {
        require(_factory != address(0), "Factory cannot be null");
        require(_autoClaim != address(0), "AutoClaim cannot be null");
        require(_feeController != address(0), "fee controller cannot be null");
        require(_invariantCheck != address(0), "invariantCheck cannot be null");
        updateIntervalId = 1;
        factory = _factory;
        invariantCheck = _invariantCheck;
        mintingFee = PoolSwapLibrary.convertUIntToDecimal(_mintingFee);
        burningFee = PoolSwapLibrary.convertUIntToDecimal(_burningFee);
        changeInterval = PoolSwapLibrary.convertUIntToDecimal(_changeInterval);
        require(mintingFee <= MAX_MINTING_FEE, "Minting fee exceeds limit");
        require(burningFee <= MAX_BURNING_FEE, "Burning fee exceeds limit");
        require(changeInterval <= MAX_CHANGE_INTERVAL, "Change Interval exceeds limit");

        feeController = _feeController;
        autoClaim = IAutoClaim(_autoClaim);
        governance = _factoryOwner;
    }

    /**
     * @notice Apply commitment data to storage
     * @param pool The LeveragedPool of this PoolCommitter instance
     * @param commitType The type of commitment being made
     * @param amount The amount of tokens being committed
     * @param fromAggregateBalance If minting, burning, or rebalancing into a delta neutral position,
     *                             will tokens be taken from user's aggregate balance?
     * @param userCommit The appropriate update interval's commitment data for the user
     * @param totalCommit The appropriate update interval's commitment data for the entire pool
     */
    function applyCommitment(
        ILeveragedPool pool,
        CommitType commitType,
        uint256 amount,
        bool fromAggregateBalance,
        UserCommitment storage userCommit,
        TotalCommitment storage totalCommit
    ) private {
        Balance memory balance = userAggregateBalance[msg.sender];
        uint256 feeAmount;

        if (this.isMint(commitType)) {
            // We want to deduct the amount of settlement tokens that will be recorded under the commit by the minting fee
            // and then add it to the correct side of the pool
            feeAmount = PoolSwapLibrary.mintingOrBurningFee(mintingFee, amount);
            amount = amount - feeAmount;
            pendingMintSettlementAmount += amount;
        }

        if (commitType == CommitType.LongMint) {
            (uint256 shortBalance, uint256 longBalance) = pool.balances();
            userCommit.longMintSettlement += amount;
            totalCommit.longMintSettlement += amount;
            // Add the fee to long side. This has been taken from the commit amount.
            pool.setNewPoolBalances(longBalance + feeAmount, shortBalance);
            // If we are minting from balance, this would already have thrown in `commit` if we are minting more than entitled too
        } else if (commitType == CommitType.LongBurn) {
            pendingLongBurnPoolTokens += amount;
            userCommit.longBurnPoolTokens += amount;
            totalCommit.longBurnPoolTokens += amount;
            // long burning: pull in long pool tokens from committer
            if (fromAggregateBalance) {
                // Burning from user's aggregate balance
                require(amount <= balance.longTokens, "Insufficient pool tokens");
                userAggregateBalance[msg.sender].longTokens -= amount;
                // Burn from leveragedPool, because that is the official owner of the tokens before they are claimed
                pool.burnTokens(LONG_INDEX, amount, leveragedPool);
            } else {
                // Burning from user's wallet
                pool.burnTokens(LONG_INDEX, amount, msg.sender);
            }
        } else if (commitType == CommitType.ShortMint) {
            (uint256 shortBalance, uint256 longBalance) = pool.balances();
            userCommit.shortMintSettlement += amount;
            totalCommit.shortMintSettlement += amount;
            // Add the fee to short side. This has been taken from the commit amount.
            pool.setNewPoolBalances(longBalance, shortBalance + feeAmount);
            // If we are minting from balance, this would already have thrown in `commit` if we are minting more than entitled too
        } else if (commitType == CommitType.ShortBurn) {
            pendingShortBurnPoolTokens += amount;
            userCommit.shortBurnPoolTokens += amount;
            totalCommit.shortBurnPoolTokens += amount;
            if (fromAggregateBalance) {
                // Burning from user's aggregate balance
                require(amount <= balance.shortTokens, "Insufficient pool tokens");
                userAggregateBalance[msg.sender].shortTokens -= amount;
                // Burn from leveragedPool, because that is the official owner of the tokens before they are claimed
                pool.burnTokens(SHORT_INDEX, amount, leveragedPool);
            } else {
                // Burning from user's wallet
                pool.burnTokens(SHORT_INDEX, amount, msg.sender);
            }
        } else if (commitType == CommitType.LongBurnShortMint) {
            pendingLongBurnPoolTokens += amount;
            userCommit.longBurnShortMintPoolTokens += amount;
            totalCommit.longBurnShortMintPoolTokens += amount;
            if (fromAggregateBalance) {
                require(amount <= balance.longTokens, "Insufficient pool tokens");
                userAggregateBalance[msg.sender].longTokens -= amount;
                pool.burnTokens(LONG_INDEX, amount, leveragedPool);
            } else {
                pool.burnTokens(LONG_INDEX, amount, msg.sender);
            }
        } else if (commitType == CommitType.ShortBurnLongMint) {
            pendingShortBurnPoolTokens += amount;
            userCommit.shortBurnLongMintPoolTokens += amount;
            totalCommit.shortBurnLongMintPoolTokens += amount;
            if (fromAggregateBalance) {
                require(amount <= balance.shortTokens, "Insufficient pool tokens");
                userAggregateBalance[msg.sender].shortTokens -= amount;
                pool.burnTokens(SHORT_INDEX, amount, leveragedPool);
            } else {
                pool.burnTokens(SHORT_INDEX, amount, msg.sender);
            }
        }
    }

    /**
     * @notice Commit to minting/burning long/short tokens after the next price change
     * @param args Arguments for the commit function packed into one bytes32
     *  _______________________________________________________________________________________
     * |   104 bits  |     8 bits    |        8 bits        |    8 bits    |      128 bits     |
     * |  0-padding  |  payForClaim  | fromAggregateBalance |  commitType  |  shortenedAmount  |
     *  ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
     * @dev Arguments can be encoded with `L2Encoder.encodeCommitParams`
     * @dev bool payForClaim: True if user wants to pay for the commit to be claimed
     * @dev bool fromAggregateBalance: If minting, burning, or rebalancing into a delta neutral position,
     *                                 will tokens be taken from user's aggregate balance?
     * @dev CommitType commitType: Type of commit you're doing (Long vs Short, Mint vs Burn)
     * @dev uint128 shortenedAmount: Amount of settlement tokens you want to commit to minting; OR amount of pool
     *                               tokens you want to burn. Expanded to uint256 at decode time
     * @dev Emits a `CreateCommit` event on success
     */
    function commit(bytes32 args) external payable override {
        (uint256 amount, CommitType commitType, bool fromAggregateBalance, bool payForClaim) = CalldataLogic
            .decodeCommitParams(args);
        require(amount > 0, "Amount must not be zero");
        updateAggregateBalance(msg.sender);
        ILeveragedPool pool = ILeveragedPool(leveragedPool);
        uint256 updateInterval = pool.updateInterval();
        uint256 lastPriceTimestamp = pool.lastPriceTimestamp();
        uint256 frontRunningInterval = pool.frontRunningInterval();

        uint256 appropriateUpdateIntervalId = PoolSwapLibrary.appropriateUpdateIntervalId(
            block.timestamp,
            lastPriceTimestamp,
            frontRunningInterval,
            updateInterval,
            updateIntervalId
        );
        TotalCommitment storage totalCommit = totalPoolCommitments[appropriateUpdateIntervalId];
        UserCommitment storage userCommit = userCommitments[msg.sender][appropriateUpdateIntervalId];

        if (userCommit.updateIntervalId == 0) {
            userCommit.updateIntervalId = appropriateUpdateIntervalId;
        }

        uint256 length = unAggregatedCommitments[msg.sender].length;
        if (length == 0 || unAggregatedCommitments[msg.sender][length - 1] < appropriateUpdateIntervalId) {
            // Push to the array if the most recent commitment was done in a prior update interval
            unAggregatedCommitments[msg.sender].push(appropriateUpdateIntervalId);
        }

        /*
         * Below, we want to follow the "Checks, Effects, Interactions" pattern.
         * `applyCommitment` adheres to the pattern, so we must put our effects before this, and interactions after.
         * Hence, we do the storage change if `fromAggregateBalance == true` before calling `applyCommitment`, and do the interaction if `fromAggregateBalance == false` after.
         * Lastly, we call `AutoClaim::makePaidClaimRequest`, which is an external interaction (albeit with a protocol contract).
         */
        if (this.isMint(commitType) && fromAggregateBalance) {
            // Want to take away from their balance's settlement tokens
            require(amount <= userAggregateBalance[msg.sender].settlementTokens, "Insufficient settlement tokens");
            userAggregateBalance[msg.sender].settlementTokens -= amount;
        }

        applyCommitment(pool, commitType, amount, fromAggregateBalance, userCommit, totalCommit);

        if (this.isMint(commitType) && !fromAggregateBalance) {
            // minting: pull in the settlement token from the committer
            // Do not need to transfer if minting using aggregate balance tokens, since the leveraged pool already owns these tokens.
            pool.settlementTokenTransferFrom(msg.sender, leveragedPool, amount);
        }

        if (payForClaim) {
            require(msg.value != 0, "Must pay for claim");
            autoClaim.makePaidClaimRequest{value: msg.value}(msg.sender);
        } else {
            require(msg.value == 0, "msg.value must be zero");
        }

        emit CreateCommit(
            msg.sender,
            amount,
            commitType,
            appropriateUpdateIntervalId,
            fromAggregateBalance,
            payForClaim,
            mintingFee
        );
    }

    /**
     * @notice Claim user's balance. This can be done either by the user themself or by somebody else on their behalf.
     * @param user Address of the user to claim against
     * @dev Updates aggregate user balances
     * @dev Emits a `Claim` event on success
     */
    function claim(address user) external override onlyAutoClaimOrCommitter(user) onlyUnpaused {
        updateAggregateBalance(user);
        Balance memory balance = userAggregateBalance[user];
        ILeveragedPool pool = ILeveragedPool(leveragedPool);

        /* update bookkeeping *before* external calls! */
        delete userAggregateBalance[user];
        emit Claim(user);

        if (msg.sender == user && autoClaim.checkUserClaim(user, address(this))) {
            // If the committer is claiming for themself and they have a valid pending claim, clear it.
            autoClaim.withdrawUserClaimRequest(user);
        }

        if (balance.settlementTokens > 0) {
            pool.settlementTokenTransfer(user, balance.settlementTokens);
        }
        if (balance.longTokens > 0) {
            pool.poolTokenTransfer(true, user, balance.longTokens);
        }
        if (balance.shortTokens > 0) {
            pool.poolTokenTransfer(false, user, balance.shortTokens);
        }
    }

    /**
     * @notice Retrieves minting fee from each mint being left in the pool
     * @return Minting fee
     */
    function getMintingFee() external view returns (uint256) {
        return PoolSwapLibrary.convertDecimalToUInt(mintingFee);
    }

    /**
     * @notice Retrieves burning fee from each burn being left in the pool
     * @return Burning fee
     */
    function getBurningFee() external view returns (uint256) {
        return PoolSwapLibrary.convertDecimalToUInt(burningFee);
    }

    /**
     * @notice Executes every commitment specified in the list
     * @param _commits Array of `TotalCommitment`s
     * @param executionTracking A struct containing the update interval ID being executed, and the long and short total supplies
     * @param longBalance The amount of settlement tokens in the long side of the pool
     * @param shortBalance The amount of settlement tokens in the short side of the pool
     * @return newLongTotalSupply The total supply of long pool tokens as a result of minting
     * @return newShortTotalSupply The total supply of short pool tokens as a result of minting
     * @return newLongBalance The amount of settlement tokens in the long side of the pool as a result of minting and burning
     * @return newShortBalance The amount of settlement tokens in the short side of the pool as a result of minting and burning
     */
    function executeGivenCommitments(
        TotalCommitment memory _commits,
        CommitmentExecutionTracking memory executionTracking,
        uint256 longBalance,
        uint256 shortBalance
    )
        internal
        returns (
            uint256,
            uint256,
            uint256,
            uint256
        )
    {
        pendingMintSettlementAmount =
            pendingMintSettlementAmount -
            totalPoolCommitments[executionTracking._updateIntervalId].longMintSettlement -
            totalPoolCommitments[executionTracking._updateIntervalId].shortMintSettlement;

        BalancesAndSupplies memory balancesAndSupplies = BalancesAndSupplies({
            newShortBalance: _commits.shortMintSettlement + shortBalance,
            newLongBalance: _commits.longMintSettlement + longBalance,
            longMintPoolTokens: 0,
            shortMintPoolTokens: 0,
            longBurnInstantMintSettlement: 0,
            shortBurnInstantMintSettlement: 0,
            totalLongBurnPoolTokens: _commits.longBurnPoolTokens + _commits.longBurnShortMintPoolTokens,
            totalShortBurnPoolTokens: _commits.shortBurnPoolTokens + _commits.shortBurnLongMintPoolTokens
        });

        bytes16 longPrice = PoolSwapLibrary.getPrice(
            longBalance,
            executionTracking.longTotalSupply + pendingLongBurnPoolTokens
        );
        bytes16 shortPrice = PoolSwapLibrary.getPrice(
            shortBalance,
            executionTracking.shortTotalSupply + pendingShortBurnPoolTokens
        );
        // Update price before values change
        priceHistory[executionTracking._updateIntervalId] = Prices({longPrice: longPrice, shortPrice: shortPrice});

        // Amount of collateral tokens that are generated from the long burn into instant mints
        {
            (uint256 mintSettlement, , ) = PoolSwapLibrary.processBurnInstantMintCommit(
                _commits.longBurnShortMintPoolTokens,
                longPrice,
                burningFee,
                mintingFee
            );
            balancesAndSupplies.longBurnInstantMintSettlement = mintSettlement;
        }

        balancesAndSupplies.newShortBalance += balancesAndSupplies.longBurnInstantMintSettlement;
        // Amount of collateral tokens that are generated from the short burn into instant mints
        {
            (uint256 mintSettlement, , ) = PoolSwapLibrary.processBurnInstantMintCommit(
                _commits.shortBurnLongMintPoolTokens,
                shortPrice,
                burningFee,
                mintingFee
            );
            balancesAndSupplies.shortBurnInstantMintSettlement = mintSettlement;
        }
        balancesAndSupplies.newLongBalance += balancesAndSupplies.shortBurnInstantMintSettlement;

        // Long Mints
        balancesAndSupplies.longMintPoolTokens = PoolSwapLibrary.getMintAmount(
            executionTracking.longTotalSupply, // long token total supply,
            _commits.longMintSettlement + balancesAndSupplies.shortBurnInstantMintSettlement, // Add the settlement tokens that will be generated from burning shorts for instant long mint
            longBalance, // total quote tokens in the long pool
            pendingLongBurnPoolTokens // total pool tokens commited to be burned
        );

        // Long Burns
        balancesAndSupplies.newLongBalance -= PoolSwapLibrary.getWithdrawAmountOnBurn(
            executionTracking.longTotalSupply,
            balancesAndSupplies.totalLongBurnPoolTokens,
            longBalance,
            pendingLongBurnPoolTokens
        );

        // Short Mints
        balancesAndSupplies.shortMintPoolTokens = PoolSwapLibrary.getMintAmount(
            executionTracking.shortTotalSupply, // short token total supply
            _commits.shortMintSettlement + balancesAndSupplies.longBurnInstantMintSettlement, // Add the settlement tokens that will be generated from burning longs for instant short mint
            shortBalance,
            pendingShortBurnPoolTokens
        );

        // Short Burns
        balancesAndSupplies.newShortBalance -= PoolSwapLibrary.getWithdrawAmountOnBurn(
            executionTracking.shortTotalSupply,
            balancesAndSupplies.totalShortBurnPoolTokens,
            shortBalance,
            pendingShortBurnPoolTokens
        );

        pendingLongBurnPoolTokens -= balancesAndSupplies.totalLongBurnPoolTokens;
        pendingShortBurnPoolTokens -= balancesAndSupplies.totalShortBurnPoolTokens;

        return (
            executionTracking.longTotalSupply + balancesAndSupplies.longMintPoolTokens,
            executionTracking.shortTotalSupply + balancesAndSupplies.shortMintPoolTokens,
            balancesAndSupplies.newLongBalance,
            balancesAndSupplies.newShortBalance
        );
    }

    /**
     * @notice Executes all commitments currently queued for the associated `LeveragedPool`
     * @dev Only callable by the associated `LeveragedPool` contract
     * @dev Emits an `ExecutedCommitsForInterval` event for each update interval processed
     * @param lastPriceTimestamp The timestamp when the last price update happened
     * @param updateInterval The number of seconds that must occur between upkeeps
     * @param longBalance The amount of settlement tokens in the long side of the pool
     * @param shortBalance The amount of settlement tokens in the short side of the pool
     * @return longTotalSupplyChange The amount of long pool tokens that have been added to the supply, passed back to LeveragedPool to mint them.
     * @return shortTotalSupplyChange The amount of short pool tokens that have been added to the supply, passed back to LeveragedPool to mint them.
     * @return newLongBalance The updated longBalance
     * @return newShortBalance The updated longBalance
     * @return lastPriceTimestamp The correct price timestamp for LeveragedPool to set. This is in case not all update intervals get upkept, we can track the time of the most recent upkept one.
     */
    function executeCommitments(
        uint256 lastPriceTimestamp,
        uint256 updateInterval,
        uint256 longBalance,
        uint256 shortBalance
    )
        external
        override
        onlyPool
        returns (
            uint256,
            uint256,
            uint256,
            uint256,
            uint256
        )
    {
        uint8 counter = 1;

        /*
         * (old)
         * updateIntervalId
         * |
         * |    updateIntervalId
         * |    |
         * |    |    counter
         * |    |    |
         * |    |    |              (end)
         * |    |    |              |
         * V    V    V              V
         * +----+----+----+~~~~+----+
         * |    |    |    |....|    |
         * +----+----+----+~~~~+----+
         *
         * Iterate over the sequence of possible update periods from the most
         * recent (i.e., the value of `updateIntervalId` as at the entry point
         * of this function) until the end of the queue.
         *
         * At each iteration, execute all of the (total) commitments for the
         * pool for that period and then remove them from the queue.
         *
         * In reality, this should never iterate more than once, since more than one update interval
         * should never be passed without the previous one being upkept.
         */

        CommitmentExecutionTracking memory executionTracking = CommitmentExecutionTracking({
            longTotalSupply: IERC20(tokens[LONG_INDEX]).totalSupply(),
            shortTotalSupply: IERC20(tokens[SHORT_INDEX]).totalSupply(),
            longTotalSupplyBefore: 0,
            shortTotalSupplyBefore: 0,
            _updateIntervalId: 0
        });

        executionTracking.longTotalSupplyBefore = executionTracking.longTotalSupply;
        executionTracking.shortTotalSupplyBefore = executionTracking.shortTotalSupply;

        while (counter <= MAX_ITERATIONS) {
            if (block.timestamp >= lastPriceTimestamp + updateInterval * counter) {
                // Another update interval has passed, so we have to do the nextIntervalCommit as well
                executionTracking._updateIntervalId = updateIntervalId;
                burnFeeHistory[executionTracking._updateIntervalId] = burningFee;
                (
                    executionTracking.longTotalSupply,
                    executionTracking.shortTotalSupply,
                    longBalance,
                    shortBalance
                ) = executeGivenCommitments(
                    totalPoolCommitments[executionTracking._updateIntervalId],
                    executionTracking,
                    longBalance,
                    shortBalance
                );
                emit ExecutedCommitsForInterval(executionTracking._updateIntervalId, burningFee);
                delete totalPoolCommitments[executionTracking._updateIntervalId];

                // update interval realistically won't overflow for the foreseeable future
                unchecked {
                    updateIntervalId += 1;
                }
            } else {
                break;
            }

            // counter overflowing would require an unrealistic number of update intervals to be updated
            // This wouldn't fit in a block, anyway.
            unchecked {
                counter += 1;
            }
        }

        updateMintingFee(
            PoolSwapLibrary.getPrice(longBalance, executionTracking.longTotalSupply),
            PoolSwapLibrary.getPrice(shortBalance, executionTracking.shortTotalSupply)
        );

        // if we maxed out the number of intervals upkept and projected lastPriceTimestamp is >= than `updateInterval` seconds ago
        // it means there are more intervals to upkeep (equal means that another upkeep is due right now but we already ran out of iterations)
        // counter will be MAX_ITERATIONS + 1 if we hit max iterations because of the loop condition `while (counter <= MAX_ITERATIONS)`
        if (
            counter > MAX_ITERATIONS &&
            (block.timestamp - (lastPriceTimestamp + updateInterval * (counter - 1))) >= updateInterval
        ) {
            // shift lastPriceTimestamp so next time the executeCommitments() will continue where it left off
            lastPriceTimestamp = lastPriceTimestamp + updateInterval * (counter - 1);
        } else {
            // Set to current time if finished every update interval
            lastPriceTimestamp = block.timestamp;
        }
        return (
            executionTracking.longTotalSupply - executionTracking.longTotalSupplyBefore,
            executionTracking.shortTotalSupply - executionTracking.shortTotalSupplyBefore,
            longBalance,
            shortBalance,
            lastPriceTimestamp
        );
    }

    function updateMintingFee(bytes16 longTokenPrice, bytes16 shortTokenPrice) private {
        bytes16 multiple = PoolSwapLibrary.multiplyBytes(longTokenPrice, shortTokenPrice);
        if (PoolSwapLibrary.compareDecimals(PoolSwapLibrary.ONE, multiple) == -1) {
            // longTokenPrice * shortTokenPrice > 1
            if (PoolSwapLibrary.compareDecimals(mintingFee, changeInterval) == -1) {
                // mintingFee < changeInterval. Prevent underflow by setting mintingFee to lowest acceptable value (0)
                mintingFee = 0;
            } else {
                mintingFee = PoolSwapLibrary.subtractBytes(mintingFee, changeInterval);
            }
        } else {
            // longTokenPrice * shortTokenPrice <= 1
            mintingFee = PoolSwapLibrary.addBytes(mintingFee, changeInterval);

            if (PoolSwapLibrary.compareDecimals(mintingFee, MAX_MINTING_FEE) == 1) {
                // mintingFee is greater than 1 (100%).
                // We want to cap this at a theoretical max of 100%
                mintingFee = MAX_MINTING_FEE;
            }
        }
    }

    /**
     * @notice Updates the aggregate balance based on the result of application
     *          of the provided (user) commitment
     * @param _commit Commitment to apply
     * @return The PoolSwapLibrary.UpdateResult struct with the data pertaining to the update of user's aggregate balance
     * @dev Wraps two (pure) library functions from `PoolSwapLibrary`
     */
    function getBalanceSingleCommitment(UserCommitment memory _commit)
        internal
        view
        returns (PoolSwapLibrary.UpdateResult memory)
    {
        PoolSwapLibrary.UpdateData memory updateData = PoolSwapLibrary.UpdateData({
            longPrice: priceHistory[_commit.updateIntervalId].longPrice,
            shortPrice: priceHistory[_commit.updateIntervalId].shortPrice,
            currentUpdateIntervalId: updateIntervalId,
            updateIntervalId: _commit.updateIntervalId,
            longMintSettlement: _commit.longMintSettlement,
            longBurnPoolTokens: _commit.longBurnPoolTokens,
            shortMintSettlement: _commit.shortMintSettlement,
            shortBurnPoolTokens: _commit.shortBurnPoolTokens,
            longBurnShortMintPoolTokens: _commit.longBurnShortMintPoolTokens,
            shortBurnLongMintPoolTokens: _commit.shortBurnLongMintPoolTokens,
            burnFee: burnFeeHistory[_commit.updateIntervalId],
            mintingFeeRate: mintingFee
        });

        return PoolSwapLibrary.getUpdatedAggregateBalance(updateData);
    }

    /**
     * @notice Add the result of a user's most recent commit to their aggregated balance
     * @param user Address of the given user
     * @dev Updates the `userAggregateBalance` mapping by applying `BalanceUpdate`s derived from iteration over the entirety of unaggregated commitments associated with the given user
     * @dev Emits an `AggregateBalanceUpdated` event upon successful termination
     */
    function updateAggregateBalance(address user) public override {
        Balance storage balance = userAggregateBalance[user];

        BalanceUpdate memory update = BalanceUpdate({
            _updateIntervalId: updateIntervalId,
            _newLongTokensSum: 0,
            _newShortTokensSum: 0,
            _newSettlementTokensSum: 0,
            _longSettlementFee: 0,
            _shortSettlementFee: 0,
            _maxIterations: 0
        });

        uint256[] memory currentIntervalIds = unAggregatedCommitments[user];
        uint256 unAggregatedLength = currentIntervalIds.length;

        update._maxIterations = unAggregatedLength < MAX_ITERATIONS ? uint8(unAggregatedLength) : MAX_ITERATIONS; // casting to uint8 is safe because we know it is less than MAX_ITERATIONS, a uint8

        // Iterate from the most recent up until the current update interval
        for (uint256 i = 0; i < update._maxIterations; i = unchecked_inc(i)) {
            uint256 id = currentIntervalIds[i];
            if (id == 0) {
                continue;
            }
            UserCommitment memory commitment = userCommitments[user][id];

            if (commitment.updateIntervalId < updateIntervalId) {
                PoolSwapLibrary.UpdateResult memory result = getBalanceSingleCommitment(commitment);
                update._newLongTokensSum += result._newLongTokens;
                update._newShortTokensSum += result._newShortTokens;
                // result._newSettlementTokens has already been decremented by the minting fees from the `LongBurnShortMint` and `ShortBurnLongMint` commits.
                update._newSettlementTokensSum += result._newSettlementTokens;
                update._longSettlementFee += result._longSettlementFee;
                update._shortSettlementFee += result._shortSettlementFee;
                delete userCommitments[user][id];
                uint256[] storage commitmentIds = unAggregatedCommitments[user];
                if (unAggregatedLength > MAX_ITERATIONS && commitmentIds.length > 1 && i < commitmentIds.length - 1) {
                    // We only enter this branch if our iterations are capped (i.e. we do not delete the array after the loop)
                    // Order doesn't actually matter in this array, so we can just put the last element into this index
                    commitmentIds[i] = commitmentIds[commitmentIds.length - 1];
                }
                commitmentIds.pop();
            } else {
                // This commitment wasn't ready to be completely added to the balance, so copy it over into the new ID array
                if (unAggregatedLength <= MAX_ITERATIONS) {
                    storageArrayPlaceHolder.push(currentIntervalIds[i]);
                }
            }
        }

        if (unAggregatedLength <= MAX_ITERATIONS) {
            // We got through all update intervals, so we can replace all unaggregated update interval IDs
            unAggregatedCommitments[user] = storageArrayPlaceHolder;
            delete storageArrayPlaceHolder;
        }

        // Add new tokens minted, and remove the ones that were burnt from this balance
        balance.longTokens += update._newLongTokensSum;
        balance.shortTokens += update._newShortTokensSum;
        balance.settlementTokens += update._newSettlementTokensSum;

        ILeveragedPool pool = ILeveragedPool(leveragedPool);
        (uint256 shortBalance, uint256 longBalance) = pool.balances();
        pool.setNewPoolBalances(longBalance + update._longSettlementFee, shortBalance + update._shortSettlementFee);

        emit AggregateBalanceUpdated(user);
    }

    /**
     * @return which update interval ID a commit would be placed into if made now
     * @dev Calls PoolSwapLibrary::appropriateUpdateIntervalId
     */
    function getAppropriateUpdateIntervalId() external view override returns (uint128) {
        ILeveragedPool pool = ILeveragedPool(leveragedPool);
        return
            uint128(
                PoolSwapLibrary.appropriateUpdateIntervalId(
                    block.timestamp,
                    pool.lastPriceTimestamp(),
                    pool.frontRunningInterval(),
                    pool.updateInterval(),
                    updateIntervalId
                )
            );
    }

    /**
     * @notice A copy of `updateAggregateBalance` that returns the aggregated balance without updating it
     * @param user Address of the given user
     * @return Associated `Balance` for the given user after aggregation
     */
    function getAggregateBalance(address user) external view override returns (Balance memory) {
        Balance memory _balance = userAggregateBalance[user];

        BalanceUpdate memory update = BalanceUpdate({
            _updateIntervalId: updateIntervalId,
            _newLongTokensSum: 0,
            _newShortTokensSum: 0,
            _newSettlementTokensSum: 0,
            _longSettlementFee: 0,
            _shortSettlementFee: 0,
            _maxIterations: 0
        });

        uint256[] memory currentIntervalIds = unAggregatedCommitments[user];
        uint256 unAggregatedLength = currentIntervalIds.length;

        update._maxIterations = unAggregatedLength < MAX_ITERATIONS ? uint8(unAggregatedLength) : MAX_ITERATIONS; // casting to uint8 is safe because we know it is less than MAX_ITERATIONS, a uint8
        // Iterate from the most recent up until the current update interval
        for (uint256 i = 0; i < update._maxIterations; i = unchecked_inc(i)) {
            uint256 id = currentIntervalIds[i];
            if (id == 0) {
                continue;
            }
            UserCommitment memory commitment = userCommitments[user][id];

            /* If the update interval of commitment has not yet passed, we still
            want to deduct burns from the balance from a user's balance.
            Therefore, this should happen outside of the if block below.*/
            if (commitment.updateIntervalId < updateIntervalId) {
                PoolSwapLibrary.UpdateResult memory result = getBalanceSingleCommitment(commitment);
                update._newLongTokensSum += result._newLongTokens;
                update._newShortTokensSum += result._newShortTokens;
                // result._newSettlementTokens has already been decremented by the minting fees from the `LongBurnShortMint` and `ShortBurnLongMint` commits.
                update._newSettlementTokensSum += result._newSettlementTokens;
            }
        }

        // Add new tokens minted, and remove the ones that were burnt from this balance
        _balance.longTokens += update._newLongTokensSum;
        _balance.shortTokens += update._newShortTokensSum;
        _balance.settlementTokens += update._newSettlementTokensSum;

        return _balance;
    }

    /**
     * @notice Sets the settlement token address and the address of the associated `LeveragedPool` contract to the provided values
     * @param _leveragedPool Address of the pool to use
     * @dev Only callable by the associated `PoolFactory` contract
     * @dev Throws if either address are null
     * @dev Emits a `PoolChanged` event on success
     */
    function setPool(address _leveragedPool) external override onlyFactory {
        require(_leveragedPool != address(0), "Leveraged pool cannot be null");

        leveragedPool = _leveragedPool;
        tokens = ILeveragedPool(leveragedPool).poolTokens();
        emit PoolChanged(_leveragedPool);
    }

    /**
     * @notice Sets the burning fee to be applied to future burn commitments indefinitely
     * @param _burningFee The new burning fee
     * @dev Converts `_burningFee` to a `bytes16` to be compatible with arithmetic library
     * @dev Emits a `BurningFeeSet` event on success
     */
    function setBurningFee(uint256 _burningFee) external override onlyFeeController {
        burningFee = PoolSwapLibrary.convertUIntToDecimal(_burningFee);
        require(burningFee < MAX_BURNING_FEE, "Burning fee >= 10%");
        emit BurningFeeSet(_burningFee);
    }

    /**
     * @notice Sets the minting fee to be applied to future burn commitments indefinitely
     * @param _mintingFee The new minting fee
     * @dev Converts `_mintingFee` to a `bytes16` to be compatible with arithmetic library
     * @dev Emits a `MintingFeeSet` event on success
     */
    function setMintingFee(uint256 _mintingFee) external override onlyFeeController {
        mintingFee = PoolSwapLibrary.convertUIntToDecimal(_mintingFee);
        require(mintingFee <= MAX_MINTING_FEE, "Minting fee > 100%");
        emit MintingFeeSet(_mintingFee);
    }

    /**
     * @notice Sets the change interval used to update the minting fee every update interval
     * @param _changeInterval The new change interval
     * @dev Converts `_changeInterval` to a `bytes16` to be compatible with arithmetic library TODO UPDATE
     * @dev Emits a `ChangeIntervalSet` event on success
     */
    function setChangeInterval(uint256 _changeInterval) external override onlyFeeController {
        changeInterval = PoolSwapLibrary.convertUIntToDecimal(_changeInterval);
        require(changeInterval <= MAX_CHANGE_INTERVAL, "Change Interval exceeds limit");
        emit ChangeIntervalSet(_changeInterval);
    }

    function setFeeController(address _feeController) external override {
        require(msg.sender == governance || msg.sender == feeController, "Cannot set feeController");
        feeController = _feeController;
        emit FeeControllerSet(_feeController);
    }

    /**
     * @notice Pauses the pool
     * @dev Prevents all state updates until unpaused
     */
    function pause() external override onlyInvariantCheckContract {
        paused = true;
        emit Paused();
    }

    /**
     * @notice Unpauses the pool
     * @dev Prevents all state updates until unpaused
     */
    function unpause() external override onlyGov {
        paused = false;
        emit Unpaused();
    }

    function unchecked_inc(uint256 i) private pure returns (uint256) {
        unchecked {
            return ++i;
        }
    }
}

File 12 of 31 : 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 13 of 31 : Strings.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}

File 14 of 31 : 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 15 of 31 : IPoolToken.sol
//SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity 0.8.7;

/// @title Interface for the pool tokens
interface IPoolToken {
    function mint(address account, uint256 amount) external;

    function burn(address account, uint256 amount) external;
}

File 16 of 31 : 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);

    /**
     * @notice Indicates that the factory address has changed
     * @param factory Address of the new factory
     */
    event FactoryChanged(address indexed factory);

    /**
     * @notice Indicates that the KeeperRewards contract has
     * @param oldKeeperRewards The previous KeeperRewards contract
     * @param newKeeperRewards The new KeeperRewards contract
     */
    event KeeperRewardsSet(address indexed oldKeeperRewards, address indexed newKeeperRewards);

    /**
     * @notice Indicates that the gas price for keeper rewards changed
     * @param price New gas price
     */
    event GasPriceChanged(uint256 indexed price);

    // #### Variables

    function keeperRewards() external returns (address);

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

    function isUpkeepRequiredSinglePool(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;

    function setKeeperRewards(address _keeperRewards) external;

    function setGasPrice(uint256 _price) external;

    function performUpkeepMultiplePoolsPacked(bytes calldata pools) external;
}

File 17 of 31 : IInvariantCheck.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 IInvariantCheck {
    event InvariantsHold();
    event InvariantsFail(string message);

    /**
     * @notice Checks all invariants, and pauses all contracts if
     *         any invariant does not hold.
     */
    function checkInvariants(address pool) external;
}

File 18 of 31 : IPausable.sol
//SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity 0.8.7;

/// @title The pausable contract
interface IPausable {
    /**
     * @notice Pauses the pool
     * @dev Prevents all state updates until unpaused
     */
    function pause() external;

    /**
     * @notice Unpauses the pool
     * @dev Prevents all state updates until unpaused
     */
    function unpause() external;

    /**
     * @return true if paused
     */
    function paused() external returns (bool);

    event Paused();
    event Unpaused();
}

File 19 of 31 : Initializable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        require(_initializing || !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }
}

File 20 of 31 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 21 of 31 : 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 {
    /// ABDKMathQuad-formatted representation of the number one
    bytes16 public constant ONE = 0x3fff0000000000000000000000000000;

    /// ABDKMathQuad-formatted representation of negative zero
    bytes16 private constant NEGATIVE_ZERO = 0x80000000000000000000000000000000;

    /// Maximum number of decimal places supported by this contract
    /// (ABDKMathQuad defines this but it's private)
    uint256 public constant MAX_DECIMALS = 18;

    /// Maximum precision supportable via wad arithmetic (for this contract)
    uint256 public constant WAD_PRECISION = 10**18;

    /// Information required to update a given user's aggregated balance
    struct UpdateData {
        bytes16 longPrice;
        bytes16 shortPrice;
        bytes16 mintingFeeRate;
        uint256 currentUpdateIntervalId;
        uint256 updateIntervalId;
        uint256 longMintSettlement;
        uint256 longBurnPoolTokens;
        uint256 shortMintSettlement;
        uint256 shortBurnPoolTokens;
        uint256 longBurnShortMintPoolTokens;
        uint256 shortBurnLongMintPoolTokens;
        bytes16 burnFee;
    }

    /// Information about the result of calculating a user's updated aggregate balance
    struct UpdateResult {
        uint256 _newLongTokens; // Quantity of long pool tokens post-application
        uint256 _newShortTokens; // Quantity of short pool tokens post-application
        uint256 _longSettlementFee; // The fee taken from ShortBurnLongMint commits
        uint256 _shortSettlementFee; // The fee taken from ShortBurnLongMint commits
        uint256 _newSettlementTokens; // Quantity of settlement tokens post
    }

    /// Information required to perform a price change (of the underlying asset)
    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 Multiplies two numbers
     * @param x The number to be multiplied by `y`
     * @param y The number to be multiplied by `x`
     */
    function multiplyBytes(bytes16 x, bytes16 y) external pure returns (bytes16) {
        return ABDKMathQuad.mul(x, y);
    }

    /**
     * @notice Performs a subtraction on two bytes16 numbers
     * @param x The number to be subtracted by `y`
     * @param y The number to subtract from `x`
     */
    function subtractBytes(bytes16 x, bytes16 y) external pure returns (bytes16) {
        return ABDKMathQuad.sub(x, y);
    }

    /**
     * @notice Performs an addition on two bytes16 numbers
     * @param x The number to be added with `y`
     * @param y The number to be added with `x`
     */
    function addBytes(bytes16 x, bytes16 y) external pure returns (bytes16) {
        return ABDKMathQuad.add(x, y);
    }

    /**
     * @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 unsigned integers
     * @param a The dividend
     * @param b The divisor
     * @return The quotient
     */
    function divUInt(uint256 a, uint256 b) private pure returns (bytes16) {
        return ABDKMathQuad.div(ABDKMathQuad.fromUInt(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 Multiply an integer by a fraction
     * @notice number * numerator / denominator
     * @param number The number with which the fraction calculated from `numerator` and `denominator` will be multiplied
     * @param numerator The numerator of the fraction being multipled with `number`
     * @param denominator The denominator of the fraction being multipled with `number`
     * @return The result of multiplying number with numerator/denominator, as an integer
     */
    function mulFraction(
        uint256 number,
        uint256 numerator,
        uint256 denominator
    ) public pure returns (uint256) {
        if (denominator == 0) {
            return 0;
        }
        bytes16 multiplyResult = ABDKMathQuad.mul(ABDKMathQuad.fromUInt(number), ABDKMathQuad.fromUInt(numerator));
        bytes16 result = ABDKMathQuad.div(multiplyResult, ABDKMathQuad.fromUInt(denominator));
        return convertDecimalToUInt(result);
    }

    /**
     * @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
     * @dev The value transfer is calculated using a sigmoid function
     * @dev The sigmoid function used is defined as follows:
     *          when newPrice >= oldPrice
     *              losing_pool_multiplier = 2 / (1 + e^(-2 * L * (1 - (newPrice / oldPrice)))) - 1
     *          when newPrice < oldPrice
     *              losing_pool_multiplier = 2 / (1 + e^(-2 * L * (1 - (oldPrice / newPrice)))) - 1
     *          where
     *              e = euler's number
     *              L = leverage
     *              newPrice = the new oracle price
     *              oldPrice = the previous oracle price
     * @param longBalance Settlement token balance on the long side of the pool before the price change
     * @param shortBalance Settlement token balance on the short side of the pool before the price change
     * @param leverageAmount The leverage of the pool
     * @param oldPrice The previous price
     * @param newPrice The new price
     * @param fee The pool's annualised protocol fee
     * @return Resulting long balance
     * @return Resulting short balance
     * @return Resulting fees taken from long balance
     * @return Resulting fees taken from short balance
     */
    function calculateValueTransfer(
        uint256 longBalance,
        uint256 shortBalance,
        bytes16 leverageAmount,
        int256 oldPrice,
        int256 newPrice,
        bytes16 fee
    )
        external
        pure
        returns (
            uint256,
            uint256,
            uint256,
            uint256
        )
    {
        // Copy into a struct (otherwise stack gets too deep)
        PriceChangeData memory priceChangeData = PoolSwapLibrary.PriceChangeData(
            oldPrice,
            newPrice,
            longBalance,
            shortBalance,
            leverageAmount,
            fee
        );
        // Calculate fees from long and short sides
        uint256 longFeeAmount = convertDecimalToUInt(
            multiplyDecimalByUInt(priceChangeData.fee, priceChangeData.longBalance)
        ) / PoolSwapLibrary.WAD_PRECISION;
        uint256 shortFeeAmount = convertDecimalToUInt(
            multiplyDecimalByUInt(priceChangeData.fee, priceChangeData.shortBalance)
        ) / PoolSwapLibrary.WAD_PRECISION;

        priceChangeData.shortBalance -= shortFeeAmount;
        priceChangeData.longBalance -= longFeeAmount;

        uint256 sumBeforePriceChange = priceChangeData.shortBalance + priceChangeData.longBalance;

        if (newPrice >= oldPrice && priceChangeData.shortBalance > 0) {
            // Price increased
            // Using the sigmoid function defined in the function's natspec, move funds from short side to long side
            bytes16 ratio = divInt(priceChangeData.oldPrice, priceChangeData.newPrice);
            bytes16 poolMultiplier = sigmoid(leverageAmount, ratio);

            priceChangeData.longBalance += ABDKMathQuad.toUInt(
                ABDKMathQuad.mul(ABDKMathQuad.fromUInt(priceChangeData.shortBalance), poolMultiplier)
            );
            priceChangeData.shortBalance = ABDKMathQuad.toUInt(
                ABDKMathQuad.mul(
                    ABDKMathQuad.fromUInt(priceChangeData.shortBalance),
                    ABDKMathQuad.sub(ONE, poolMultiplier)
                )
            );
        } else if (newPrice < oldPrice && priceChangeData.longBalance > 0) {
            // Price decreased
            // Using the sigmoid function defined in the function's natspec, move funds from long side to short side
            bytes16 ratio = divInt(priceChangeData.newPrice, priceChangeData.oldPrice);
            bytes16 poolMultiplier = sigmoid(leverageAmount, ratio);

            priceChangeData.shortBalance += ABDKMathQuad.toUInt(
                ABDKMathQuad.mul(ABDKMathQuad.fromUInt(priceChangeData.longBalance), poolMultiplier)
            );
            priceChangeData.longBalance = ABDKMathQuad.toUInt(
                ABDKMathQuad.mul(
                    ABDKMathQuad.fromUInt(priceChangeData.longBalance),
                    ABDKMathQuad.sub(ONE, poolMultiplier)
                )
            );
        }

        if (sumBeforePriceChange > priceChangeData.longBalance + priceChangeData.shortBalance) {
            // Move dust into winning side
            // This is only ever 1 wei (negligible)
            if (newPrice > oldPrice) {
                priceChangeData.longBalance +=
                    sumBeforePriceChange -
                    (priceChangeData.longBalance + priceChangeData.shortBalance);
            } else {
                priceChangeData.shortBalance +=
                    sumBeforePriceChange -
                    (priceChangeData.longBalance + priceChangeData.shortBalance);
            }
        }

        return (priceChangeData.longBalance, priceChangeData.shortBalance, longFeeAmount, shortFeeAmount);
    }

    /**
     * @notice Use a sigmoid function to determine the losing pool multiplier.
     * @return The losing pool multiplier, represented as an ABDKMathQuad IEEE754 quadruple-precision binary floating-point numbers
     * @dev The returned value is used in `calculateValueTransfer` as the portion to move from the losing side into the winning side
     */
    function sigmoid(bytes16 leverage, bytes16 ratio) private pure returns (bytes16) {
        /**
         * denominator = 1 + e ^ (-2 * leverage * (1 - ratio))
         */
        bytes16 denominator = ABDKMathQuad.mul(ABDKMathQuad.fromInt(-2), leverage);
        denominator = ABDKMathQuad.mul(denominator, ABDKMathQuad.sub(ONE, ratio));
        denominator = ABDKMathQuad.add(ONE, ABDKMathQuad.exp(denominator));
        bytes16 numerator = ABDKMathQuad.add(ONE, ONE); // 2
        return ABDKMathQuad.sub((ABDKMathQuad.div(numerator, denominator)), ONE);
    }

    /**
     * @notice Returns true if the given timestamp is BEFORE the frontRunningInterval starts
     * @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 update in which users can have their commit executed from
     */
    function isBeforeFrontRunningInterval(
        uint256 subjectTime,
        uint256 lastPriceTimestamp,
        uint256 updateInterval,
        uint256 frontRunningInterval
    ) public pure returns (bool) {
        return lastPriceTimestamp + updateInterval - frontRunningInterval > subjectTime;
    }

    /**
     * @notice Calculates the update interval ID that a commitment should be placed in.
     * @param timestamp Current block.timestamp
     * @param lastPriceTimestamp The timestamp of the last price update
     * @param frontRunningInterval The frontrunning interval of a pool - The amount of time before an update interval that you must commit to get included in that update
     * @param updateInterval The frequency of a pool's updates
     * @param currentUpdateIntervalId The current update interval's ID
     * @dev Note that the timestamp parameter is required to be >= lastPriceTimestamp
     * @return The update interval ID in which a commit being made at time timestamp should be included
     */
    function appropriateUpdateIntervalId(
        uint256 timestamp,
        uint256 lastPriceTimestamp,
        uint256 frontRunningInterval,
        uint256 updateInterval,
        uint256 currentUpdateIntervalId
    ) external pure returns (uint256) {
        require(lastPriceTimestamp <= timestamp, "timestamp in the past");
        if (frontRunningInterval <= updateInterval) {
            // This is the "simple" case where we either want the current update interval or the next one
            if (isBeforeFrontRunningInterval(timestamp, lastPriceTimestamp, updateInterval, frontRunningInterval)) {
                // We are before the frontRunning interval
                return currentUpdateIntervalId;
            } else {
                // Floor of `timePassed / updateInterval` to get the number of intervals passed
                uint256 updateIntervalsPassed = (timestamp - lastPriceTimestamp) / updateInterval;
                // If 1 update interval has passed, we want to check if we are within the frontrunning interval of currentUpdateIntervalId + 1
                uint256 frontRunningIntervalStart = lastPriceTimestamp +
                    ((updateIntervalsPassed + 1) * updateInterval) -
                    frontRunningInterval;
                if (timestamp >= frontRunningIntervalStart) {
                    // add an extra update interval because the frontrunning interval has passed
                    return currentUpdateIntervalId + updateIntervalsPassed + 1;
                } else {
                    return currentUpdateIntervalId + updateIntervalsPassed;
                }
            }
        } else {
            // frontRunningInterval > updateInterval
            // This is the generalised case, where it could be any number of update intervals in the future
            // Minimum time is the earliest we could possible execute this commitment (i.e. the current time plus frontrunning interval)
            uint256 minimumTime = timestamp + frontRunningInterval;
            // Number of update intervals that would have had to have passed.
            uint256 updateIntervals = (minimumTime - lastPriceTimestamp) / updateInterval;

            return currentUpdateIntervalId + updateIntervals;
        }
    }

    /**
     * @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 pool tokens going into the pool
     * @param balance Balance of the pool (no. of underlying settlement tokens in pool)
     * @param pendingBurnPoolTokens Amount of pool tokens being burnt during this update interval
     * @return Number of settlement tokens to be withdrawn on a burn
     */
    function getWithdrawAmountOnBurn(
        uint256 tokenSupply,
        uint256 amountIn,
        uint256 balance,
        uint256 pendingBurnPoolTokens
    ) external pure returns (uint256) {
        // Catch the divide by zero error, or return 0 if amountIn is 0
        if ((balance == 0) || (tokenSupply + pendingBurnPoolTokens == 0) || (amountIn == 0)) {
            return amountIn;
        }
        return (balance * amountIn) / (tokenSupply + pendingBurnPoolTokens);
    }

    /**
     * @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 settlement tokens going into the pool
     * @param balance Balance of the pool (no. of underlying settlement tokens in pool)
     * @param pendingBurnPoolTokens Amount of pool tokens being burnt during this update interval
     * @return Number of pool tokens to be minted
     */
    function getMintAmount(
        uint256 tokenSupply,
        uint256 amountIn,
        uint256 balance,
        uint256 pendingBurnPoolTokens
    ) external pure returns (uint256) {
        // Catch the divide by zero error, or return 0 if amountIn is 0
        if (balance == 0 || tokenSupply + pendingBurnPoolTokens == 0 || amountIn == 0) {
            return amountIn;
        }

        return ((tokenSupply + pendingBurnPoolTokens) * amountIn) / balance;
    }

    /**
     * @notice Get the Settlement/PoolToken price, in ABDK IEE754 precision
     * @dev Divide the side balance by the pool token's total supply
     * @param sideBalance no. of underlying settlement tokens on that side of the pool
     * @param tokenSupply Total supply of pool tokens
     */
    function getPrice(uint256 sideBalance, uint256 tokenSupply) external pure returns (bytes16) {
        if (tokenSupply == 0) {
            return ONE;
        }
        return ABDKMathQuad.div(ABDKMathQuad.fromUInt(sideBalance), ABDKMathQuad.fromUInt(tokenSupply));
    }

    /**
     * @notice Calculates the number of pool tokens to mint, given some settlement token amount and a price
     * @param price Price of a pool token
     * @param amount Amount of settlement tokens being used to mint
     * @return Quantity of pool tokens to mint
     * @dev Throws if price is zero, or IEEE754 negative zero
     * @dev `getMint()`
     */
    function getMint(bytes16 price, uint256 amount) public pure returns (uint256) {
        require(price != 0, "price == 0");
        require(price != NEGATIVE_ZERO, "price == negative zero");
        return ABDKMathQuad.toUInt(ABDKMathQuad.div(ABDKMathQuad.fromUInt(amount), price));
    }

    /**
     * @notice Calculate the number of settlement tokens to return, based on a price and an amount of pool tokens being burnt
     * @param price Price of a pool token
     * @param amount Amount of pool tokens being used to burn
     * @return Quantity of settlement tokens to return to the user after `amount` pool tokens are burnt.
     * @dev amount * price, where amount is in PoolToken and price is in USD/PoolToken
     * @dev Throws if price is zero, or IEEE754 negative zero
     * @dev `getBurn()`
     */
    function getBurn(bytes16 price, uint256 amount) public pure returns (uint256) {
        require(price != 0, "price == 0");
        require(price != NEGATIVE_ZERO, "price == negative zero");
        return ABDKMathQuad.toUInt(ABDKMathQuad.mul(ABDKMathQuad.fromUInt(amount), price));
    }

    /**
     * @notice Calculate the amount of settlement tokens to take as the minting fee
     * @param feeRate PoolCommitter's mintingFee or burningFee - The amount that is extracted from each mint or burn. Given as the decimal * 10 ^ 18. For example, 60% fee is 0.6 * 10 ^ 18 Fees can be 0.
     * @param amount The amount of settlement tokens being committed to mint
     */
    function mintingOrBurningFee(bytes16 feeRate, uint256 amount) public pure returns (uint256) {
        return ABDKMathQuad.toUInt(multiplyDecimalByUInt(feeRate, amount)) / WAD_PRECISION;
    }

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

    /**
     * @notice Given an amount of pool tokens to flip to the other side of the pool, calculate the amount of settlement tokens generated from the burn, burn fee, and subsequent minting fee
     * @dev Takes out the burn fee before taking out the mint fee.
     * @param amount The amount of pool tokens being flipped
     * @param burnPrice The price of the pool token being burnt
     * @param burningFee Fee rate for pool token burns
     * @param mintingFee Fee rate for mints
     * @return Amount of settlement tokens used to mint.
     * @return The burn fee. This should be given to the side of the pool of the burnt tokens.
     * @return The mint fee. This should be given to the side of the pool that is being minted into.
     */
    function processBurnInstantMintCommit(
        uint256 amount,
        bytes16 burnPrice,
        bytes16 burningFee,
        bytes16 mintingFee
    )
        public
        pure
        returns (
            uint256,
            uint256,
            uint256
        )
    {
        // Settlement tokens earned from burning pool tokens (for instant mint)
        uint256 mintSettlement = getBurn(burnPrice, amount);
        // The burn fee. This should be given to the side of the pool of the burnt tokens.
        uint256 burnFee = mintingOrBurningFee(burningFee, mintSettlement);
        mintSettlement -= burnFee;

        // The mint fee. This should be given to the side of the pool that is being minted into.
        uint256 mintFee = mintingOrBurningFee(mintingFee, mintSettlement);
        mintSettlement -= mintFee;
        return (mintSettlement, burnFee, mintFee);
    }

    /**
     * @notice Calculate the change in a user's balance based on recent commit(s)
     * @param data Information needed for updating the balance including prices and recent commit amounts
     * @return The UpdateResult struct with the data pertaining to the update of user's aggregate balance
     */
    function getUpdatedAggregateBalance(UpdateData calldata data) external pure returns (UpdateResult memory) {
        UpdateResult memory result = UpdateResult(0, 0, 0, 0, 0);
        if (data.updateIntervalId >= data.currentUpdateIntervalId) {
            // Update interval has not passed: No change
            return result;
        }

        /**
         * Start by looking at the "flip" commitments (either LongBurnShortMint, or ShortBurnLongMint), and determine the amount of settlement tokens were generated from them.
         * Then, take the burning fee off them and add that to the relevant side's fee amount. e.g. a ShortBurnLongMint will generate burn fees for the short side.
         * Now, we can calculate how much minting fee should be paid by the user. This should then be added to the side which they are minting on.
         */
        uint256 shortBurnLongMintResult; // Settlement to be included in the long mint
        uint256 longBurnShortMintResult; // Settlement to be included in the short mint
        if (data.shortBurnLongMintPoolTokens > 0) {
            uint256 burnFeeSettlement;
            uint256 mintFeeSettlement;
            (shortBurnLongMintResult, burnFeeSettlement, mintFeeSettlement) = processBurnInstantMintCommit(
                data.shortBurnLongMintPoolTokens,
                data.shortPrice,
                data.burnFee,
                data.mintingFeeRate
            );
            result._shortSettlementFee += burnFeeSettlement;
            result._longSettlementFee += mintFeeSettlement;
        }
        if (data.longBurnShortMintPoolTokens > 0) {
            // Settlement tokens earned from burning long tokens (for instant mint)
            longBurnShortMintResult = getBurn(data.longPrice, data.longBurnShortMintPoolTokens);
            // The burn fee taken from this burn. This should be given to the long side.
            uint256 burnFeeSettlement = mintingOrBurningFee(data.burnFee, longBurnShortMintResult);
            longBurnShortMintResult -= burnFeeSettlement;

            // The mint fee taken from the subsequent mint
            uint256 mintFeeSettlement = mintingOrBurningFee(data.mintingFeeRate, longBurnShortMintResult);
            longBurnShortMintResult -= mintFeeSettlement;

            result._longSettlementFee += burnFeeSettlement;
            result._shortSettlementFee += mintFeeSettlement;
        }

        /**
         * Calculate the new long tokens minted.
         * Use amount committed LongMint/ShortMint, as well as settlement tokens generated from ShortBurnLongMint/LongBurnShortMint commits.
         */
        if (data.longMintSettlement > 0 || shortBurnLongMintResult > 0) {
            result._newLongTokens += getMint(data.longPrice, data.longMintSettlement + shortBurnLongMintResult);
        }
        if (data.shortMintSettlement > 0 || longBurnShortMintResult > 0) {
            result._newShortTokens += getMint(data.shortPrice, data.shortMintSettlement + longBurnShortMintResult);
        }

        /**
         * Calculate the settlement tokens earned through LongBurn/ShortBurn commits.
         * Once this is calculated, take off the burn fee, and add to the respective side's fee amount.
         */
        if (data.longBurnPoolTokens > 0) {
            // Calculate the amount of settlement tokens earned from burning long tokens
            uint256 longBurnResult = getBurn(data.longPrice, data.longBurnPoolTokens);
            // Calculate the fee
            uint256 longBurnFee = mintingOrBurningFee(data.burnFee, longBurnResult);
            result._longSettlementFee += longBurnFee;
            // Subtract the fee from settlement token amount
            longBurnResult -= longBurnFee;
            result._newSettlementTokens += longBurnResult;
        }
        if (data.shortBurnPoolTokens > 0) {
            // Calculate the amount of settlement tokens earned from burning short tokens
            uint256 shortBurnResult = getBurn(data.shortPrice, data.shortBurnPoolTokens);
            // Calculate the fee
            uint256 shortBurnFee = mintingOrBurningFee(data.burnFee, shortBurnResult);
            result._shortSettlementFee += shortBurnFee;
            // Subtract the fee from settlement token amount
            shortBurnResult -= shortBurnFee;
            result._newSettlementTokens += shortBurnResult;
        }

        return result;
    }
}

File 22 of 31 : 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);

    function decimals() external view returns (uint8);

    function deployer() external view returns (address);

    // #### Functions

    /**
     * @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);

    /**
     * @notice Updates the underlying oracle state and returns the new price
     * @dev Spot oracles must implement but it will be a no-op
     */
    function poll() external returns (int256);
}

File 23 of 31 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) private pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 24 of 31 : 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 25 of 31 : ERC20_Cloneable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;

import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";

/**
 * @dev Minimal Clones compatible implementation of the {IERC20} interface.
 * @dev Based Openzeppelin 3.4 ERC20 contract
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20_Cloneable is ERC20, Initializable {
    uint8 _decimals;
    string private _name;
    string private _symbol;

    address public owner;

    modifier onlyOwner() {
        require(msg.sender == owner, "msg.sender not owner");
        _;
    }

    /**
     * @dev Sets the values for {name}, {symbol} and {decimals}.
     */
    constructor(
        string memory name_,
        string memory symbol_,
        uint8 decimals_
    ) ERC20(name_, symbol_) {
        _decimals = decimals_;
    }

    function initialize(
        address _pool,
        string memory name_,
        string memory symbol_,
        uint8 decimals_
    ) external initializer {
        owner = _pool;
        _name = name_;
        _symbol = symbol_;
        _decimals = decimals_;
    }

    function decimals() public view virtual override returns (uint8) {
        return _decimals;
    }

    function name() public view virtual override returns (string memory) {
        return _name;
    }

    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @notice Transfer ownership. Implemented to help with initializable
     */
    function transferOwnership(address _owner) external onlyOwner {
        require(_owner != address(0), "Owner: setting to 0 address");
        owner = _owner;
    }
}

File 26 of 31 : 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 27 of 31 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        unchecked {
            _approve(sender, _msgSender(), currentAllowance - amount);
        }

        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(_msgSender(), spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[sender] = senderBalance - amount;
        }
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);

        _afterTokenTransfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}

File 28 of 31 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

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

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

File 29 of 31 : IKeeperRewards.sol
//SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity 0.8.7;

interface IKeeperRewards {
    /**
     * @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);

    function payKeeper(
        address _keeper,
        address _pool,
        uint256 _gasPrice,
        uint256 _gasSpent,
        uint256 _savedPreviousUpdatedTimestamp,
        uint256 _updateInterval
    ) external returns (uint256);
}

File 30 of 31 : CalldataLogic.sol
//SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity 0.8.7;

import "../interfaces/IPoolCommitter.sol";

/// @title CalldataLogic library
/// @notice Library to decode calldata, used to optimize calldata size in PerpetualPools for L2 transaction cost reduction
library CalldataLogic {
    /*
     * Calldata when parameter is a tightly packed byte array looks like this:
     * -----------------------------------------------------------------------------------------------------
     * | function signature | offset of byte array | length of byte array |           bytes array           |
     * |      4 bytes       |       32 bytes       |       32 bytes       |  20 * number_of_addresses bytes |
     * -----------------------------------------------------------------------------------------------------
     *
     * If there are two bytes arrays, then it looks like
     * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     * | function signature | offset of 1st byte array | offset of 2nd byte array | length of 1st byte array |        1st bytes array          | length of 2nd byte array |        2nd bytes array          |
     * |      4 bytes       |        32 bytes          |        32 bytes          |         32 bytes         |  20 * number_of_addresses bytes |         32 bytes         |  20 * number_of_addresses bytes |
     * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     * and so on...
     * Note that the offset indicates where the length is indicated, and the actual array itself starts 32 bytes after that
     */
    // Length of address = 20
    uint16 internal constant ADDRESS_LENGTH = 20;

    function getAddressAtOffset(uint256 offset) internal pure returns (address) {
        bytes20 addressAtOffset;
        assembly {
            addressAtOffset := calldataload(offset)
        }
        return (address(addressAtOffset));
    }

    /**
     * @notice decodes compressed commit params to standard params
     * @param args The packed commit args
     * @return The amount of settlement or pool tokens to commit
     * @return The CommitType
     * @return Whether to make the commitment from user's aggregate balance
     * @return Whether to pay for an autoclaim or not
     */
    function decodeCommitParams(bytes32 args)
        internal
        pure
        returns (
            uint256,
            IPoolCommitter.CommitType,
            bool,
            bool
        )
    {
        uint256 amount;
        IPoolCommitter.CommitType commitType;
        bool fromAggregateBalance;
        bool payForClaim;

        // `amount` is implicitly capped at 128 bits.
        assembly {
            amount := and(args, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
            commitType := and(shr(128, args), 0xFF)
            fromAggregateBalance := and(shr(136, args), 0xFF)
            payForClaim := and(shr(144, args), 0xFF)
        }
        return (amount, commitType, fromAggregateBalance, payForClaim);
    }
}

File 31 of 31 : 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);
    }
}

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

Contract ABI

[{"inputs":[{"internalType":"address","name":"_feeReceiver","type":"address"},{"internalType":"address","name":"_governance","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"autoClaim","type":"address"}],"name":"AutoClaimChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"poolCommitterAddress","type":"address"},{"indexed":false,"internalType":"address","name":"settlementToken","type":"address"},{"indexed":false,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"uint256","name":"changeInterval","type":"uint256"},{"indexed":false,"internalType":"address","name":"feeController","type":"address"}],"name":"DeployCommitter","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"address","name":"poolCommitter","type":"address"},{"indexed":false,"internalType":"string","name":"ticker","type":"string"}],"name":"DeployPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"address","name":"_receiver","type":"address"}],"name":"DeploymentFeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"FeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"}],"name":"FeeReceiverChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":true,"internalType":"address","name":"newAddress","type":"address"}],"name":"GovernanceAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"invariantCheck","type":"address"}],"name":"InvariantCheckChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"leverage","type":"uint256"}],"name":"MaxLeverageChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"mint","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"burn","type":"uint256"}],"name":"MintAndBurnFeesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_poolKeeper","type":"address"}],"name":"PoolKeeperChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAddress","type":"address"}],"name":"ProvisionalGovernanceChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"SecondaryFeeSplitChanged","type":"event"},{"inputs":[],"name":"autoClaim","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"poolName","type":"string"},{"internalType":"uint32","name":"frontRunningInterval","type":"uint32"},{"internalType":"uint32","name":"updateInterval","type":"uint32"},{"internalType":"uint16","name":"leverageAmount","type":"uint16"},{"internalType":"address","name":"settlementToken","type":"address"},{"internalType":"address","name":"oracleWrapper","type":"address"},{"internalType":"address","name":"settlementEthOracle","type":"address"},{"internalType":"address","name":"feeController","type":"address"},{"internalType":"uint256","name":"mintingFee","type":"uint256"},{"internalType":"uint256","name":"changeInterval","type":"uint256"},{"internalType":"uint256","name":"burningFee","type":"uint256"}],"internalType":"struct IPoolFactory.PoolDeployment","name":"deploymentParameters","type":"tuple"}],"name":"deployPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deploymentFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deploymentFeeReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deploymentFeeToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPoolKeeper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governanceTransferInProgress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"invariantCheck","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isValidPool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isValidPoolCommitter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numPools","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pairTokenBaseAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolBaseAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolCommitterBaseAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolKeeper","outputs":[{"internalType":"contract IPoolKeeper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"pools","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"provisionalGovernance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"secondaryFeeSplitPercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_autoClaim","type":"address"}],"name":"setAutoClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"setDeploymentFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeReceiver","type":"address"}],"name":"setFeeReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_invariantCheck","type":"address"}],"name":"setInvariantCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_poolKeeper","type":"address"}],"name":"setPoolKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newFeePercent","type":"uint256"}],"name":"setSecondaryFeeSplitPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_governance","type":"address"}],"name":"transferGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60e0604052600a6006553480156200001657600080fd5b506040516200b8b23803806200b8b283398101604081905262000039916200048a565b6001600160a01b038216620000955760405162461bcd60e51b815260206004820152601b60248201527f4665652072656365697665722063616e6e6f74206265206e756c6c000000000060448201526064015b60405180910390fd5b6001600160a01b038116620000ed5760405162461bcd60e51b815260206004820152601960248201527f476f7665726e616e63652063616e6e6f74206265206e756c6c0000000000000060448201526064016200008c565b600380546001600160a01b0319166001600160a01b0383161790556040516000906012906200011c9062000443565b60ff9091168152602001604051809103906000f08015801562000143573d6000803e3d6000fd5b506001600160601b0319606082901b166080526040519091506000906200016a9062000451565b604051809103906000f08015801562000187573d6000803e3d6000fd5b506001600160601b0319606082901b1660a052604051909150600090620001ae906200045f565b604051809103906000f080158015620001cb573d6000803e3d6000fd5b50606081901b6001600160601b03191660c0908152600a80546001600160a01b0319166001600160a01b0389811691909117909155604051637b69774360e11b81528582166004808301919091526080602483015260848201819052636261736560e01b60a4830152604482019390935260c4810192909252634241534560e01b60e4830152600860648301529192509084169063f6d2ee869061010401600060405180830381600087803b1580156200028457600080fd5b505af115801562000299573d6000803e3d6000fd5b50506040805161022081018252308082526020808301829052828401829052606083018290526001600160a01b038981166080850181905260a085015287811660c085015260e08401839052845180860186526004808252636261736560e01b93820193909352610100850152600061012085018190526001610140860181905261016086015261018085018190526101a085018490526101c085018490526101e08501939093526102008401929092529251630129011d60e41b81529194508616925063129011d091620003719185910162000512565b600060405180830381600087803b1580156200038c57600080fd5b505af1158015620003a1573d6000803e3d6000fd5b5050600354604051637403c6cd60e01b815230600482018190526024820181905260448201526001600160a01b03918216606482018190526084820152600060a4820181905260c4820181905260e48201529085169250637403c6cd915061010401600060405180830381600087803b1580156200041e57600080fd5b505af115801562000433573d6000803e3d6000fd5b50505050505050505050620006b6565b6111eb806200286983390190565b612f858062003a5483390190565b614ed980620069d983390190565b80516001600160a01b03811681146200048557600080fd5b919050565b600080604083850312156200049e57600080fd5b620004a9836200046d565b9150620004b9602084016200046d565b90509250929050565b6000815180845260005b81811015620004ea57602081850181015186830182015201620004cc565b81811115620004fd576000602083870101525b50601f01601f19169290920160200192915050565b602081526200052d6020820183516001600160a01b03169052565b600060208301516200054a60408401826001600160a01b03169052565b5060408301516001600160a01b03811660608401525060608301516001600160a01b03811660808401525060808301516001600160a01b03811660a08401525060a08301516001600160a01b03811660c08401525060c08301516001600160a01b03811660e08401525060e0830151610100620005d1818501836001600160a01b03169052565b808501519150506102206101208181860152620005f3610240860184620004c2565b908601519092506101406200060f8682018363ffffffff169052565b8601519050610160620006298682018363ffffffff169052565b8601519050610180620006418682018361ffff169052565b8601516101a08681019190915286015190506101c06200066b818701836001600160a01b03169052565b86015190506101e062000688868201836001600160a01b03169052565b8601519050610200620006a5868201836001600160a01b03169052565b959095015193019290925250919050565b60805160601c60a05160601c60c05160601c61216562000704600039600081816103ff0152610e3c0152600081816104420152610f3b015260008181610295015261188e01526121656000f3fe608060405234801561001057600080fd5b50600436106101da5760003560e01c806379bc618211610104578063d166b8e3116100a2578063ed3f7e2611610071578063ed3f7e261461043d578063efdcd97414610464578063f164e2a714610477578063f2cff57f1461048b57600080fd5b8063d166b8e3146103e7578063d3394714146103fa578063d38bfff414610421578063ddca3f431461043457600080fd5b8063ac4afa38116100de578063ac4afa3814610385578063b3f00674146103ae578063b7f4bb1c146103c1578063c70db323146103d457600080fd5b806379bc61821461034e5780637dbb36c4146103615780639fd552451461037257600080fd5b80634509017e1161017c5780635ab78ee11161014b5780635ab78ee1146102fd5780635d36b19014610320578063616e57901461032857806369fe0e2d1461033b57600080fd5b80634509017e1461027d5780634690092c1461029057806359e86a99146102b75780635aa6e675146102ea57600080fd5b8063225454c5116101b8578063225454c51461023b57806335c62bc21461024e5780633e942ad714610257578063430ed6eb1461026a57600080fd5b806303850dbb146101df5780630fa56b10146101fb578063178f9e3514610210575b600080fd5b6101e860065481565b6040519081526020015b60405180910390f35b61020e610209366004611a2b565b610494565b005b600054610223906001600160a01b031681565b6040516001600160a01b0390911681526020016101f2565b61020e6102493660046119ea565b61058c565b6101e8600c5481565b600954610223906001600160a01b031681565b61020e6102783660046119ea565b610626565b600254610223906001600160a01b031681565b6102237f000000000000000000000000000000000000000000000000000000000000000081565b6102da6102c53660046119ea565b600e6020526000908152604090205460ff1681565b60405190151581526020016101f2565b600354610223906001600160a01b031681565b6102da61030b3660046119ea565b600d6020526000908152604090205460ff1681565b61020e6106ca565b61020e610336366004611ae6565b6107e9565b61020e610349366004611ae6565b6108a6565b61020e61035c3660046119ea565b610951565b6000546001600160a01b0316610223565b600454610223906001600160a01b031681565b610223610393366004611ae6565b600b602052600090815260409020546001600160a01b031681565b600a54610223906001600160a01b031681565b6102236103cf366004611a8f565b6109eb565b600154610223906001600160a01b031681565b600754610223906001600160a01b031681565b6102237f000000000000000000000000000000000000000000000000000000000000000081565b61020e61042f3660046119ea565b6113cc565b6101e860055481565b6102237f000000000000000000000000000000000000000000000000000000000000000081565b61020e6104723660046119ea565b61152e565b6003546102da90600160a01b900460ff1681565b6101e860085481565b6003546001600160a01b031633146104c75760405162461bcd60e51b81526004016104be90611c96565b60405180910390fd5b6001600160a01b0383166105145760405162461bcd60e51b8152602060048201526014602482015273151bdad95b8818d85b9b9bdd081899481b9d5b1b60621b60448201526064016104be565b600780546001600160a01b038581166001600160a01b0319928316811790935560088590556009805491851691909216811790915560408051928352602083018590528201527f3615dcb57be737e6ec209de3ce5b503307ff77885da0ed0e1d98b48d154f9f819060600160405180910390a1505050565b6003546001600160a01b031633146105b65760405162461bcd60e51b81526004016104be90611c96565b6001600160a01b0381166105dc5760405162461bcd60e51b81526004016104be90611c6e565b600280546001600160a01b0319166001600160a01b0383169081179091556040517f24c15dcddddd5cf1c1ca428f04560f295744a59a459f278163e3ffcbd12f108690600090a250565b6003546001600160a01b031633146106505760405162461bcd60e51b81526004016104be90611c96565b6001600160a01b0381166106765760405162461bcd60e51b81526004016104be90611c6e565b600080546001600160a01b0319166001600160a01b0383169081179091556040519081527ff33ed59034aadf61086c54204694b493ee22a3d903bddbe94c276f7bb442f4e39060200160405180910390a150565b600354600160a01b900460ff166107235760405162461bcd60e51b815260206004820152601b60248201527f4e6f20676f7665726e616e6365206368616e676520616374697665000000000060448201526064016104be565b6004546001600160a01b031633811461077e5760405162461bcd60e51b815260206004820152601860248201527f4e6f742070726f766973696f6e616c20676f7665726e6f72000000000000000060448201526064016104be565b6003546040516001600160a01b038084169216907f023588d3d1dcaad34e471c9e01b616b794156174bc693539c8fe15c0bfd5d82690600090a360038054600480546001600160a01b03191690556001600160a81b0319166001600160a01b03909216919091179055565b6003546001600160a01b031633146108135760405162461bcd60e51b81526004016104be90611c96565b60648111156108735760405162461bcd60e51b815260206004820152602660248201527f5365636f6e64617279206665652073706c69742063616e6e6f7420657863656560448201526564203130302560d01b60648201526084016104be565b600681905560405181907ffd9d1596de9068fc7e910a6e5ba7d9c07e9f6018518a993f41ab74afb0f9617790600090a250565b6003546001600160a01b031633146108d05760405162461bcd60e51b81526004016104be90611c96565b67016345785d8a000081111561091e5760405162461bcd60e51b81526020600482015260136024820152724665652063616e6e6f74206265203e2031302560681b60448201526064016104be565b600581905560405181907f6bbc57480a46553fa4d156ce702beef5f3ad66303b0ed1a5d4cb44966c6584c390600090a250565b6003546001600160a01b0316331461097b5760405162461bcd60e51b81526004016104be90611c96565b6001600160a01b0381166109a15760405162461bcd60e51b81526004016104be90611c6e565b600180546001600160a01b0319166001600160a01b0383169081179091556040517fa88db2b867bf534c147cbfc947cf3f3158ed92959503ef51c8729e6f35dc109e90600090a250565b600080546001600160a01b031680610a3a5760405162461bcd60e51b8152602060048201526012602482015271141bdbdb12d9595c195c881b9bdd081cd95d60721b60448201526064016104be565b6001546001600160a01b0316610a865760405162461bcd60e51b8152602060048201526011602482015270105d5d1bd0db185a5b481b9bdd081cd95d607a1b60448201526064016104be565b6002546001600160a01b0316610ad75760405162461bcd60e51b8152602060048201526016602482015275125b9d985c9a585b9d10da1958dac81b9bdd081cd95d60521b60448201526064016104be565b33610ae860c0850160a086016119ea565b6001600160a01b031663d5f394886040518163ffffffff1660e01b815260040160206040518083038186803b158015610b2057600080fd5b505afa158015610b34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b589190611a0e565b6001600160a01b031614610bbc5760405162461bcd60e51b815260206004820152602560248201527f4465706c6f796572206d757374206265206f7261636c6520777261707065722060448201526437bbb732b960d91b60648201526084016104be565b610bcc6080840160608501611acb565b61ffff16610c1c5760405162461bcd60e51b815260206004820152601f60248201527f4c657665726167656420616d6f756e742063616e6e6f7420657175616c20300060448201526064016104be565b6012610c2e60a08501608086016119ea565b6001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610c6657600080fd5b505afa158015610c7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c9e9190611b1a565b60ff161115610cef5760405162461bcd60e51b815260206004820152601a60248201527f446563696d616c20707265636973696f6e20746f6f206869676800000000000060448201526064016104be565b60085415610d8b576007546009546008546040516323b872dd60e01b81523360048201526001600160a01b03928316602482015260448101919091529116906323b872dd90606401602060405180830381600087803b158015610d5157600080fd5b505af1158015610d65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d899190611a6d565b505b6000610d9d6040850160208601611aff565b610dad6060860160408701611aff565b610dbd6080870160608801611acb565b610dcd60a08801608089016119ea565b610ddd60c0890160a08a016119ea565b6040805163ffffffff9687166020820152959094169385019390935261ffff90911660608401526001600160a01b0390811660808401521660a082015260c0016040516020818303038152906040528051906020012090506000610e617f0000000000000000000000000000000000000000000000000000000000000000836115f8565b60015460035491925082916001600160a01b0380841692637403c6cd9230929182169116610e966101008c0160e08d016119ea565b6002546040516001600160e01b031960e088901b1681526001600160a01b039586166004820152938516602485015291841660448401528316606483015291909116608482015261010089013560a482015261014089013560c482015261012089013560e482015261010401600060405180830381600087803b158015610f1c57600080fd5b505af1158015610f30573d6000803e3d6000fd5b505050506000610f607f0000000000000000000000000000000000000000000000000000000000000000856115f8565b9050806001600160a01b0381167f82f5ba2b59209e8b2008e0addf2c8502bf23175cfee67125b2562302ce9823b985610f998b80611ea6565b604051610fa893929190611be3565b60405180910390a26000610fce610fc560808b0160608c01611acb565b61ffff1661169e565b60408051610220810182526003546001600160a01b0390811682528a16602082015291925060009190810161100960c08d0160a08e016119ea565b6001600160a01b0316815260200161102760e08d0160c08e016119ea565b6001600160a01b0316815260200161106385856110438f611f80565b604051806040016040528060028152602001614c2d60f01b8152506117a4565b6001600160a01b0316815260200161109f858561107f8f611f80565b60405180604001604052806002815260200161532d60f01b8152506117a4565b6001600160a01b0390811682528781166020830152600254166040820152606001836110cb8d80611ea6565b6040516020016110dd93929190611bac565b60405160208183030381529060405281526020018b60200160208101906111049190611aff565b63ffffffff16815260200161111f60608d0160408e01611aff565b63ffffffff16815260200161113a60808d0160608e01611acb565b61ffff1681526020016301e1855861115860608e0160408f01611aff565b63ffffffff1660055461116b9190611f4a565b6111759190611f36565b8152600a546001600160a01b031660208201523360408201526060016111a160a08d0160808e016119ea565b6001600160a01b031681526020016006548152509050836001600160a01b031663129011d0826040518263ffffffff1660e01b81526004016111e39190611ccd565b600060405180830381600087803b1580156111fd57600080fd5b505af1158015611211573d6000803e3d6000fd5b505060405163221b8a9560e11b81526001600160a01b03868116600483015288169250634437152a9150602401600060405180830381600087803b15801561125857600080fd5b505af115801561126c573d6000803e3d6000fd5b505050507fe1759462958518b57e02da7f1e24c6a789c6f0ad7f8751169def6657289695f1858b60800160208101906112a591906119ea565b858d61012001358e60e00160208101906112bf91906119ea565b604080516001600160a01b0396871681529486166020860152928516928401929092526060830152909116608082015260a00160405180910390a1600054604051633c5c7fb360e01b81526001600160a01b03858116600483015290911690633c5c7fb390602401600060405180830381600087803b15801561134157600080fd5b505af1158015611355573d6000803e3d6000fd5b5050600c80546000908152600b6020908152604080832080546001600160a01b0319166001600160a01b038b8116918217909255855460019081019096558452600d8352818420805460ff1990811687179091559c168352600e909152902080549099161790975550919998505050505050505050565b6003546001600160a01b031633146113f65760405162461bcd60e51b81526004016104be90611c96565b6003546001600160a01b038281169116141561147a5760405162461bcd60e51b815260206004820152603f60248201527f4e657720676f7665726e616e636520616464726573732063616e6e6f7420626560448201527f2073616d65206173206f6c6420676f7665726e616e636520616464726573730060648201526084016104be565b6001600160a01b0381166114d05760405162461bcd60e51b815260206004820152601960248201527f476f7665726e616e63652063616e6e6f74206265206e756c6c0000000000000060448201526064016104be565b600480546001600160a01b0383166001600160a01b031990911681179091556003805460ff60a01b1916600160a01b1790556040517f35681f4f23137fb58510a9854f1b6e95f90a2cf0b66d2fce4df74cc0becc82d590600090a250565b6003546001600160a01b031633146115585760405162461bcd60e51b81526004016104be90611c96565b6001600160a01b0381166115ae5760405162461bcd60e51b815260206004820152601b60248201527f4665652072656365697665722063616e6e6f74206265206e756c6c000000000060448201526064016104be565b600a80546001600160a01b0319166001600160a01b0383169081179091556040517f647672599d3468abcfa241a13c9e3d34383caadb5cc80fb67c3cdfcd5f78605990600090a250565b6000604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528360601b60148201526e5af43d82803e903d91602b57fd5bf360881b6028820152826037826000f59150506001600160a01b0381166116985760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c656400000000000000000060448201526064016104be565b92915050565b6060816116c25750506040805180820190915260018152600360fc1b602082015290565b8160005b81156116ec57806116d681612090565b91506116e59050600a83611f36565b91506116c6565b60008167ffffffffffffffff81111561170757611707612101565b6040519080825280601f01601f191660200182016040528015611731576020820181803683370190505b5090505b841561179c57611746600183611f69565b9150611753600a866120ab565b61175e906030611f1e565b60f81b818381518110611773576117736120eb565b60200101906001600160f81b031916908160001a905350611795600a86611f36565b9450611735565b949350505050565b600080848385600001516040516020016117c093929190611b69565b6040516020818303038152906040529050600084608001516001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561181057600080fd5b505afa158015611824573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118489190611b1a565b90506000856060015186608001518760a001518760405160200161186f9493929190611e66565b60405160208183030381529060405280519060200120905060006118b37f0000000000000000000000000000000000000000000000000000000000000000836115f8565b604051637b69774360e11b81529091506001600160a01b0382169063f6d2ee86906118e8908c90889081908990600401611c23565b600060405180830381600087803b15801561190257600080fd5b505af1158015611916573d6000803e3d6000fd5b50929b9a5050505050505050505050565b803561193281612117565b919050565b600082601f83011261194857600080fd5b813567ffffffffffffffff8082111561196357611963612101565b604051601f8301601f19908116603f0116810190828211818310171561198b5761198b612101565b816040528381528660208588010111156119a457600080fd5b836020870160208301376000602085830101528094505050505092915050565b803561ffff8116811461193257600080fd5b803563ffffffff8116811461193257600080fd5b6000602082840312156119fc57600080fd5b8135611a0781612117565b9392505050565b600060208284031215611a2057600080fd5b8151611a0781612117565b600080600060608486031215611a4057600080fd5b8335611a4b81612117565b9250602084013591506040840135611a6281612117565b809150509250925092565b600060208284031215611a7f57600080fd5b81518015158114611a0757600080fd5b600060208284031215611aa157600080fd5b813567ffffffffffffffff811115611ab857600080fd5b82016101608185031215611a0757600080fd5b600060208284031215611add57600080fd5b611a07826119c4565b600060208284031215611af857600080fd5b5035919050565b600060208284031215611b1157600080fd5b611a07826119d6565b600060208284031215611b2c57600080fd5b815160ff81168114611a0757600080fd5b60008151808452611b55816020860160208601612060565b601f01601f19169290920160200192915050565b60008451611b7b818460208901612060565b845190830190611b8f818360208901612060565b8451910190611ba2818360208801612060565b0195945050505050565b60008451611bbe818460208901612060565b602d60f81b908301908152838560018301376000930160010192835250909392505050565b6001600160a01b03841681526040602082018190528101829052818360608301376000818301606090810191909152601f909201601f1916010192915050565b6001600160a01b0385168152608060208201819052600090611c4790830186611b3d565b8281036040840152611c598186611b3d565b91505060ff8316606083015295945050505050565b6020808252600e908201526d18d85b9b9bdd081899481b9d5b1b60921b604082015260600190565b60208082526019908201527f6d73672e73656e646572206e6f7420676f7665726e616e636500000000000000604082015260600190565b60208152611ce76020820183516001600160a01b03169052565b60006020830151611d0360408401826001600160a01b03169052565b5060408301516001600160a01b03811660608401525060608301516001600160a01b03811660808401525060808301516001600160a01b03811660a08401525060a08301516001600160a01b03811660c08401525060c08301516001600160a01b03811660e08401525060e0830151610100611d89818501836001600160a01b03169052565b808501519150506102206101208181860152611da9610240860184611b3d565b90860151909250610140611dc48682018363ffffffff169052565b8601519050610160611ddd8682018363ffffffff169052565b8601519050610180611df48682018361ffff169052565b8601516101a08681019190915286015190506101c0611e1d818701836001600160a01b03169052565b86015190506101e0611e39868201836001600160a01b03169052565b8601519050610200611e55868201836001600160a01b03169052565b959095015193019290925250919050565b61ffff851681526001600160a01b03848116602083015283166040820152608060608201819052600090611e9c90830184611b3d565b9695505050505050565b6000808335601e19843603018112611ebd57600080fd5b83018035915067ffffffffffffffff821115611ed857600080fd5b602001915036819003821315611eed57600080fd5b9250929050565b604051610160810167ffffffffffffffff81118282101715611f1857611f18612101565b60405290565b60008219821115611f3157611f316120bf565b500190565b600082611f4557611f456120d5565b500490565b6000816000190483118215151615611f6457611f646120bf565b500290565b600082821015611f7b57611f7b6120bf565b500390565b60006101608236031215611f9357600080fd5b611f9b611ef4565b823567ffffffffffffffff811115611fb257600080fd5b611fbe36828601611937565b825250611fcd602084016119d6565b6020820152611fde604084016119d6565b6040820152611fef606084016119c4565b606082015261200060808401611927565b608082015261201160a08401611927565b60a082015261202260c08401611927565b60c082015261203360e08401611927565b60e08201526101008381013590820152610120808401359082015261014092830135928101929092525090565b60005b8381101561207b578181015183820152602001612063565b8381111561208a576000848401525b50505050565b60006000198214156120a4576120a46120bf565b5060010190565b6000826120ba576120ba6120d5565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461212c57600080fd5b5056fea2646970667358221220f949e46e4d1e19b80ce079fbbe7756598fe1a792ea59f2cf443bd03beb4a986a64736f6c6343000807003360806040523480156200001157600080fd5b50604051620011eb380380620011eb833981016040819052620000349162000175565b6040518060400160405280600a8152602001692120a9a2afaa27a5a2a760b11b815250604051806040016040528060048152602001634241534560e01b81525082828281600390805190602001906200008f929190620000cf565b508051620000a5906004906020840190620000cf565b50506005805460ff909316620100000262ff0000199093169290921790915550620001de92505050565b828054620000dd90620001a1565b90600052602060002090601f0160209004810192826200010157600085556200014c565b82601f106200011c57805160ff19168380011785556200014c565b828001600101855582156200014c579182015b828111156200014c5782518255916020019190600101906200012f565b506200015a9291506200015e565b5090565b5b808211156200015a57600081556001016200015f565b6000602082840312156200018857600080fd5b815160ff811681146200019a57600080fd5b9392505050565b600181811c90821680620001b657607f821691505b60208210811415620001d857634e487b7160e01b600052602260045260246000fd5b50919050565b610ffd80620001ee6000396000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c80638da5cb5b11610097578063a9059cbb11610066578063a9059cbb14610233578063dd62ed3e14610246578063f2fde38b1461027f578063f6d2ee861461029257600080fd5b80638da5cb5b146101da57806395d89b41146102055780639dc29fac1461020d578063a457c2d71461022057600080fd5b8063313ce567116100d3578063313ce5671461016b578063395093511461018957806340c10f191461019c57806370a08231146101b157600080fd5b806306fdde0314610105578063095ea7b31461012357806318160ddd1461014657806323b872dd14610158575b600080fd5b61010d6102a5565b60405161011a9190610eae565b60405180910390f35b610136610131366004610e84565b610337565b604051901515815260200161011a565b6002545b60405190815260200161011a565b610136610166366004610dbb565b61034d565b60055462010000900460ff1660405160ff909116815260200161011a565b610136610197366004610e84565b6103fc565b6101af6101aa366004610e84565b610438565b005b61014a6101bf366004610d66565b6001600160a01b031660009081526020819052604090205490565b6008546101ed906001600160a01b031681565b6040516001600160a01b03909116815260200161011a565b61010d610470565b6101af61021b366004610e84565b61047f565b61013661022e366004610e84565b6104b3565b610136610241366004610e84565b61054c565b61014a610254366004610d88565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6101af61028d366004610d66565b610559565b6101af6102a0366004610df7565b6105fb565b6060600680546102b490610f60565b80601f01602080910402602001604051908101604052809291908181526020018280546102e090610f60565b801561032d5780601f106103025761010080835404028352916020019161032d565b820191906000526020600020905b81548152906001019060200180831161031057829003601f168201915b5050505050905090565b600061034433848461070b565b50600192915050565b600061035a848484610830565b6001600160a01b0384166000908152600160209081526040808320338452909152902054828110156103e45760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084015b60405180910390fd5b6103f1853385840361070b565b506001949350505050565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610344918590610433908690610f31565b61070b565b6008546001600160a01b031633146104625760405162461bcd60e51b81526004016103db90610f03565b61046c82826109ff565b5050565b6060600780546102b490610f60565b6008546001600160a01b031633146104a95760405162461bcd60e51b81526004016103db90610f03565b61046c8282610ade565b3360009081526001602090815260408083206001600160a01b0386168452909152812054828110156105355760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016103db565b610542338585840361070b565b5060019392505050565b6000610344338484610830565b6008546001600160a01b031633146105835760405162461bcd60e51b81526004016103db90610f03565b6001600160a01b0381166105d95760405162461bcd60e51b815260206004820152601b60248201527f4f776e65723a2073657474696e6720746f20302061646472657373000000000060448201526064016103db565b600880546001600160a01b0319166001600160a01b0392909216919091179055565b600554610100900460ff1680610614575060055460ff16155b6106775760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016103db565b600554610100900460ff16158015610699576005805461ffff19166101011790555b600880546001600160a01b0319166001600160a01b03871617905583516106c7906006906020870190610c24565b5082516106db906007906020860190610c24565b506005805462ff000019166201000060ff8516021790558015610704576005805461ff00191690555b5050505050565b6001600160a01b03831661076d5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016103db565b6001600160a01b0382166107ce5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016103db565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6001600160a01b0383166108945760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016103db565b6001600160a01b0382166108f65760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016103db565b6001600160a01b0383166000908152602081905260409020548181101561096e5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016103db565b6001600160a01b038085166000908152602081905260408082208585039055918516815290812080548492906109a5908490610f31565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516109f191815260200190565b60405180910390a350505050565b6001600160a01b038216610a555760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016103db565b8060026000828254610a679190610f31565b90915550506001600160a01b03821660009081526020819052604081208054839290610a94908490610f31565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b6001600160a01b038216610b3e5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016103db565b6001600160a01b03821660009081526020819052604090205481811015610bb25760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016103db565b6001600160a01b0383166000908152602081905260408120838303905560028054849290610be1908490610f49565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001610823565b828054610c3090610f60565b90600052602060002090601f016020900481019282610c525760008555610c98565b82601f10610c6b57805160ff1916838001178555610c98565b82800160010185558215610c98579182015b82811115610c98578251825591602001919060010190610c7d565b50610ca4929150610ca8565b5090565b5b80821115610ca45760008155600101610ca9565b80356001600160a01b0381168114610cd457600080fd5b919050565b600082601f830112610cea57600080fd5b813567ffffffffffffffff80821115610d0557610d05610fb1565b604051601f8301601f19908116603f01168101908282118183101715610d2d57610d2d610fb1565b81604052838152866020858801011115610d4657600080fd5b836020870160208301376000602085830101528094505050505092915050565b600060208284031215610d7857600080fd5b610d8182610cbd565b9392505050565b60008060408385031215610d9b57600080fd5b610da483610cbd565b9150610db260208401610cbd565b90509250929050565b600080600060608486031215610dd057600080fd5b610dd984610cbd565b9250610de760208501610cbd565b9150604084013590509250925092565b60008060008060808587031215610e0d57600080fd5b610e1685610cbd565b9350602085013567ffffffffffffffff80821115610e3357600080fd5b610e3f88838901610cd9565b94506040870135915080821115610e5557600080fd5b50610e6287828801610cd9565b925050606085013560ff81168114610e7957600080fd5b939692955090935050565b60008060408385031215610e9757600080fd5b610ea083610cbd565b946020939093013593505050565b600060208083528351808285015260005b81811015610edb57858101830151858201604001528201610ebf565b81811115610eed576000604083870101525b50601f01601f1916929092016040019392505050565b60208082526014908201527336b9b39739b2b73232b9103737ba1037bbb732b960611b604082015260600190565b60008219821115610f4457610f44610f9b565b500190565b600082821015610f5b57610f5b610f9b565b500390565b600181811c90821680610f7457607f821691505b60208210811415610f9557634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fdfea2646970667358221220b320f0be9f47be35b28ed0993064618c5a156b6d8c320679645cf9de9097a62664736f6c63430008070033608060405234801561001057600080fd5b50612f65806100206000396000f3fe608060405234801561001057600080fd5b50600436106102f15760003560e01c80637bfe789a1161019d578063bbcaac38116100e9578063d38bfff4116100a2578063f164e2a71161007c578063f164e2a714610642578063f3466dfa14610656578063f633a7401461066b578063fd2c80ae1461067e57600080fd5b8063d38bfff41461060f578063ddca3f4314610622578063e3d346cd1461062f57600080fd5b8063bbcaac38146105be578063c40f6aa9146105d1578063cd39f30f146105d9578063ced72f87146105ec578063d0b06f09146105f4578063d11262a01461060757600080fd5b806389e5915411610156578063aced166111610130578063aced166114610587578063b9ed8abf1461059a578063ba8d5468146105ad578063baba68de146105b657600080fd5b806389e591541461053e5780639fd5524514610547578063ab47c2b31461055a57600080fd5b80637bfe789a146104d25780637de93f93146104fe5780637e71fc7d146105075780638226f396146105105780638456cb591461052357806387f9ca5d1461052b57600080fd5b806346fdc99a1161025c5780636dc2b27111610215578063796da7af116101ef578063796da7af1461047f5780637b9e618d146104875780637bb98a681461049a5780637bbf1033146104b557600080fd5b80636dc2b27114610446578063748747e61461045957806375a024a21461046c57600080fd5b806346fdc99a146103d65780634f64b2be146103df5780635aa6e675146103f25780635c975abb146104055780635d36b190146104295780636d3e313e1461043157600080fd5b8063129011d0116102ae578063129011d0146103625780632fd7d643146103755780633c3f82521461037d5780633f4ba83a1461039057806341275358146103985780634509017e146103c357600080fd5b806303850dbb146102f657806304883c2714610312578063062e0d691461032a5780630c96871a146103325780630d20a1dc1461033c578063110b1bed1461034f575b600080fd5b6102ff600a5481565b6040519081526020015b60405180910390f35b61031a610695565b6040516103099493929190612d30565b6102ff600081565b61033a61074c565b005b61033a61034a366004612934565b610892565b61033a61035d3660046129be565b610903565b61033a610370366004612afc565b610987565b61033a6113f8565b61033a61038b366004612ada565b611464565b61033a6114f3565b6008546103ab906001600160a01b031681565b6040516001600160a01b039091168152602001610309565b6004546103ab906001600160a01b031681565b6102ff600c5481565b6103ab6103ed366004612b5c565b611555565b6003546103ab906001600160a01b031681565b60065461041990600160a01b900460ff1681565b6040519015158152602001610309565b61033a611575565b610439611696565b6040516103099190612cf6565b610419610454366004612975565b6116da565b61033a6104673660046128fa565b6118bd565b61033a61047a3660046128fa565b611991565b6102ff611a04565b600d546103ab906001600160a01b031681565b60015460025460408051928352602083019190915201610309565b600454601354600160c01b90910463ffffffff1601421015610419565b6004546104e990600160a01b900463ffffffff1681565b60405163ffffffff9091168152602001610309565b6102ff60135481565b6102ff60025481565b6010546103ab906001600160a01b031681565b61033a611a86565b61033a610539366004612b99565b611b1e565b6102ff600b5481565b6006546103ab906001600160a01b031681565b60055461056e90600160801b900460801b81565b6040516001600160801b03199091168152602001610309565b6007546103ab906001600160a01b031681565b600f546103ab906001600160a01b031681565b6102ff60015481565b6102ff611bef565b61033a6105cc3660046128fa565b611c68565b6102ff600181565b600e546103ab906001600160a01b031681565b6102ff611d67565b61033a610602366004612975565b611db3565b61033a611e1e565b61033a61061d3660046128fa565b611e83565b60055461056e9060801b81565b6009546103ab906001600160a01b031681565b60065461041990600160a81b900460ff1681565b61065e611fd8565b6040516103099190612d5c565b61033a610679366004612ada565b612066565b6004546104e990600160c01b900463ffffffff1681565b60006060600080600080600f60009054906101000a90046001600160a01b03166001600160a01b031663db6d8fcf6040518163ffffffff1660e01b815260040160006040518083038186803b1580156106ed57600080fd5b505afa158015610701573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526107299190810190612a21565b60135460045492999198509650600160c01b90910463ffffffff16945092505050565b6003546001600160a01b0316331461077f5760405162461bcd60e51b815260040161077690612dce565b60405180910390fd5b600654600160a01b900460ff166107c75760405162461bcd60e51b815260206004820152600c60248201526b506f6f6c206973206c69766560a01b6044820152606401610776565b600d546040516370a0823160e01b81523060048201526001600160a01b039091169060009082906370a082319060240160206040518083038186803b15801561080f57600080fd5b505afa158015610823573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108479190612a08565b600d54909150610861906001600160a01b03163383612300565b604051819033907f3304a35fcccaae1037a5405395dfc497e010ed92b42060cc21ac54cfb0e3e85090600090a35050565b600e546001600160a01b031633146108bc5760405162461bcd60e51b815260040161077690612d97565b600654600160a01b900460ff16156108e65760405162461bcd60e51b815260040161077690612d6f565b600d546108fe906001600160a01b0316848484612363565b505050565b600e546001600160a01b0316331461092d5760405162461bcd60e51b815260040161077690612d97565b600654600160a01b900460ff16156109575760405162461bcd60e51b815260040161077690612d6f565b8215610979576108fe8282601160005b01546001600160a01b03169190612300565b6108fe828260116001610967565b600054610100900460ff16806109a0575060005460ff16155b610a035760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610776565b600054610100900460ff16158015610a25576000805461ffff19166101011790555b6000610a396101c084016101a085016128fa565b6001600160a01b03161415610a855760405162461bcd60e51b81526020600482015260126024820152711199594818d85b9b9bdd081899481b9d5b1b60721b6044820152606401610776565b6000610a9961020084016101e085016128fa565b6001600160a01b03161415610af05760405162461bcd60e51b815260206004820152601f60248201527f536574746c656d656e7420746f6b656e2063616e6e6f74206265206e756c6c006044820152606401610776565b6000610b0260608401604085016128fa565b6001600160a01b03161415610b595760405162461bcd60e51b815260206004820152601d60248201527f4f7261636c6520777261707065722063616e6e6f74206265206e756c6c0000006044820152606401610776565b6000610b6b60808401606085016128fa565b6001600160a01b03161415610bc25760405162461bcd60e51b815260206004820152601c60248201527f4b6565706572206f7261636c652063616e6e6f74206265206e756c6c000000006044820152606401610776565b6000610bd160208401846128fa565b6001600160a01b03161415610c1f5760405162461bcd60e51b815260206004820152601460248201527313dddb995c8818d85b9b9bdd081899481b9d5b1b60621b6044820152606401610776565b6000610c3160408401602085016128fa565b6001600160a01b03161415610c805760405162461bcd60e51b815260206004820152601560248201527412d9595c195c8818d85b9b9bdd081899481b9d5b1b605a1b6044820152606401610776565b6000610c9260a08401608085016128fa565b6001600160a01b03161415610ce95760405162461bcd60e51b815260206004820152601960248201527f4c6f6e6720746f6b656e2063616e6e6f74206265206e756c6c000000000000006044820152606401610776565b6000610cfb60c0840160a085016128fa565b6001600160a01b03161415610d525760405162461bcd60e51b815260206004820152601a60248201527f53686f727420746f6b656e2063616e6e6f74206265206e756c6c0000000000006044820152606401610776565b6000610d6460e0840160c085016128fa565b6001600160a01b03161415610dbb5760405162461bcd60e51b815260206004820152601c60248201527f506f6f6c436f6d6d69747465722063616e6e6f74206265206e756c6c000000006044820152606401610776565b6000610dce610100840160e085016128fa565b6001600160a01b03161415610e255760405162461bcd60e51b815260206004820152601d60248201527f496e76617269616e74436865636b2063616e6e6f74206265206e756c6c0000006044820152606401610776565b670de0b6b3a764000082610180013510610e6f5760405162461bcd60e51b815260206004820152600b60248201526a466565203e3d203130302560a81b6044820152606401610776565b60648261020001351115610ed45760405162461bcd60e51b815260206004820152602660248201527f5365636f6e64617279206665652073706c69742063616e6e6f7420657863656560448201526564203130302560d01b6064820152608401610776565b610ee661016083016101408401612c48565b63ffffffff16610f385760405162461bcd60e51b815260206004820152601b60248201527f55706461746520696e74657276616c2063616e6e6f74206265203000000000006044820152606401610776565b610f4560208301836128fa565b600380546001600160a01b0319166001600160a01b0392909216919091179055610f7560408301602084016128fa565b600780546001600160a01b0319166001600160a01b0392909216919091179055610fa560608301604084016128fa565b600f80546001600160a01b0319166001600160a01b0392909216919091179055610fd560808301606084016128fa565b601080546001600160a01b0319166001600160a01b039290921691909117905561100761020083016101e084016128fa565b600d80546001600160a01b0319166001600160a01b0392909216919091179055611038610100830160e084016128fa565b600480546001600160a01b0319166001600160a01b039290921691909117905561106a61014083016101208401612c48565b6004805463ffffffff92909216600160a01b0263ffffffff60a01b199092169190911790556110a161016083016101408401612c48565b6004805463ffffffff92909216600160c01b0263ffffffff60c01b19909216919091178155604051636fedf26d60e11b81527358639957c0e526ff4e4bb1cbfbdfefdeb16af2379163dfdbe4da9161110491610180870135910190815260200190565b60206040518083038186803b15801561111c57600080fd5b505af4158015611130573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115491906129de565b600580546001600160801b03191660809290921c9190911790557358639957c0e526ff4e4bb1cbfbdfefdeb16af23763dfdbe4da61119a61018085016101608601612b38565b6040516001600160e01b031960e084901b16815261ffff909116600482015260240160206040518083038186803b1580156111d457600080fd5b505af41580156111e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061120c91906129de565b6005805460809290921c600160801b026fffffffffffffffffffffffffffffffff9092169190911790556112486101c083016101a084016128fa565b600880546001600160a01b0319166001600160a01b039290921691909117905561127a6101e083016101c084016128fa565b600980546001600160a01b0319166001600160a01b0392909216919091179055610200820135600a55426013556112b5610100830183612e05565b6112c191601491612843565b506112d260a08301608084016128fa565b601160000180546001600160a01b0319166001600160a01b039290921691909117905561130560c0830160a084016128fa565b601160010180546001600160a01b0319166001600160a01b039290921691909117905561133860e0830160c084016128fa565b600e80546001600160a01b0319166001600160a01b039290921691909117905561136860c0830160a084016128fa565b6001600160a01b031661138160a08401608085016128fa565b6001600160a01b03167fbc8bd61e5dd69ff5cb8c389529c029503515ba368eaafbdaa406e072fdd360726113bd61020086016101e087016128fa565b6113cb610100870187612e05565b6040516113da93929190612cb6565b60405180910390a380156113f4576000805461ff00191690555b5050565b600b80546000909155600854600d5461141e916001600160a01b03918216911683612300565b6008546040518281526001600160a01b03909116907fe756bfac49549fbcb50e6d0f75a98c96a16261fbaf06f41dd35de05f2902fa4e906020015b60405180910390a250565b600e546001600160a01b0316331461148e5760405162461bcd60e51b815260040161077690612d97565b600654600160a01b900460ff16156114b85760405162461bcd60e51b815260040161077690612d6f565b60028290556001819055604051819083907fb31885164844201997ff1f41bcad765029c634f5183880298a4c69ec4540d89f90600090a35050565b6003546001600160a01b0316331461151d5760405162461bcd60e51b815260040161077690612dce565b6006805460ff60a01b191690556040517fa45f47fdea8a1efdd9029a5691c7f759c32b7c698632b563573e155625d1693390600090a1565b6011816002811061156557600080fd5b01546001600160a01b0316905081565b600654600160a81b900460ff166115ce5760405162461bcd60e51b815260206004820152601b60248201527f4e6f20676f7665726e616e6365206368616e67652061637469766500000000006044820152606401610776565b6006546001600160a01b03163381146116295760405162461bcd60e51b815260206004820152601860248201527f4e6f742070726f766973696f6e616c20676f7665726e6f7200000000000000006044820152606401610776565b6003546040516001600160a01b038084169216907f023588d3d1dcaad34e471c9e01b616b794156174bc693539c8fe15c0bfd5d82690600090a3600380546001600160a01b0319166001600160a01b039290921691909117905560068054600161ff0160a01b0319169055565b61169e6128c7565b60408051808201918290529060119060029082845b81546001600160a01b031681526001909101906020018083116116b3575050505050905090565b600754604080516342bd698960e11b815290516000926001600160a01b03169163857ad31291600480830192602092919082900301818787803b15801561172057600080fd5b505af1158015611734573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117589190612917565b6001600160a01b0316336001600160a01b0316146117b85760405162461bcd60e51b815260206004820152601c60248201527f6d73672e73656e646572206e6f74206b656570657252657761726473000000006044820152606401610776565b600654600160a01b900460ff16156117e25760405162461bcd60e51b815260040161077690612d6f565b6001546002546117f28183612e53565b8410611803576000925050506118b7565b60405163dc35bc1360e01b81526004810185905260248101839052604481018290527358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063dc35bc1390606401604080518083038186803b15801561185b57600080fd5b505af415801561186f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118939190612b75565b600255600155600d546118b0906001600160a01b03168686612300565b6001925050505b92915050565b6003546001600160a01b031633146118e75760405162461bcd60e51b815260040161077690612dce565b6001600160a01b0381166119355760405162461bcd60e51b815260206004820152601560248201527412d9595c195c8818d85b9b9bdd081899481b9d5b1b605a1b6044820152606401610776565b6007546040516001600160a01b038084169216907f402b3f9a8de3e388e7653c7a5892204fe18b579c8c23db19d6e00f1043ceb92190600090a3600780546001600160a01b0319166001600160a01b0392909216919091179055565b6009546001600160a01b031633146119a857600080fd5b6009546040516001600160a01b038084169216907f4a1fe1b44fae78218ab520acca2aaba5067e706a4084bccf913f291c8d6e074990600090a3600980546001600160a01b0319166001600160a01b0392909216919091179055565b600f5460408051634c6afee560e11b815290516000926001600160a01b0316916398d5fdca916004808301926020929190829003018186803b158015611a4957600080fd5b505afa158015611a5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a819190612a08565b905090565b6004546001600160a01b03163314611ae05760405162461bcd60e51b815260206004820152601d60248201527f6d73672e73656e646572206e6f7420696e76617269616e74436865636b0000006044820152606401610776565b6006805460ff60a01b1916600160a01b1790556040517f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e75290600090a1565b600e546001600160a01b03163314611b485760405162461bcd60e51b815260040161077690612d97565b600654600160a01b900460ff1615611b725760405162461bcd60e51b815260040161077690612d6f565b60118360028110611b8557611b85612ee0565b0154604051632770a7eb60e21b81526001600160a01b0383811660048301526024820185905290911690639dc29fac90604401600060405180830381600087803b158015611bd257600080fd5b505af1158015611be6573d6000803e3d6000fd5b50505050505050565b60055460405163aeb64d7d60e01b8152600160801b90910460801b6001600160801b03191660048201526000907358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063aeb64d7d906024015b60206040518083038186803b158015611c5457600080fd5b505af4158015611a5d573d6000803e3d6000fd5b6003546001600160a01b03163314611c925760405162461bcd60e51b815260040161077690612dce565b600654600160a01b900460ff1615611cbc5760405162461bcd60e51b815260040161077690612d6f565b6001600160a01b038116611d0b5760405162461bcd60e51b81526020600482015260166024820152751058d8dbdd5b9d0818d85b9b9bdd081899481b9d5b1b60521b6044820152606401610776565b6008546040516001600160a01b038084169216907f11f35a22548bcd4c3788ab4a7e4fba427a2014f02e5d5e2da9af62212c03183f90600090a3600880546001600160a01b0319166001600160a01b0392909216919091179055565b60055460405163aeb64d7d60e01b815260809190911b6001600160801b03191660048201526000907358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063aeb64d7d90602401611c3c565b600e546001600160a01b03163314611ddd5760405162461bcd60e51b815260040161077690612d97565b600654600160a01b900460ff1615611e075760405162461bcd60e51b815260040161077690612d6f565b600d546113f4906001600160a01b03168383612300565b600c80546000909155600954600d54611e44916001600160a01b03918216911683612300565b6009546040518281526001600160a01b03909116907f2f76018fcfcd8aaaa8299da890bcca2dfebfc638250a89e5785b34bdc68b636390602001611459565b6003546001600160a01b03163314611ead5760405162461bcd60e51b815260040161077690612dce565b6003546001600160a01b0382811691161415611f315760405162461bcd60e51b815260206004820152603f60248201527f4e657720676f7665726e616e636520616464726573732063616e6e6f7420626560448201527f2073616d65206173206f6c6420676f7665726e616e63652061646472657373006064820152608401610776565b6001600160a01b038116611f875760405162461bcd60e51b815260206004820152601960248201527f476f7665726e616e63652063616e6e6f74206265206e756c6c000000000000006044820152606401610776565b60068054600161ff0160a01b0319166001600160a01b038316908117600160a81b179091556040517f35681f4f23137fb58510a9854f1b6e95f90a2cf0b66d2fce4df74cc0becc82d590600090a250565b60148054611fe590612ea5565b80601f016020809104026020016040519081016040528092919081815260200182805461201190612ea5565b801561205e5780601f106120335761010080835404028352916020019161205e565b820191906000526020600020905b81548152906001019060200180831161204157829003601f168201915b505050505081565b6007546001600160a01b031633146120b85760405162461bcd60e51b815260206004820152601560248201527436b9b39739b2b73232b9103737ba1035b2b2b832b960591b6044820152606401610776565b600654600160a01b900460ff16156120e25760405162461bcd60e51b815260040161077690612d6f565b600454601354600160c01b90910463ffffffff16014210156121465760405162461bcd60e51b815260206004820152601d60248201527f55706461746520696e74657276616c206861736e2774207061737365640000006044820152606401610776565b601380544290915561215883836123a1565b600e5460048054600254600154604051638f5b8e8760e01b8152938401869052600160c01b90920463ffffffff1660248401526044830152606482015260009182918291829182916001600160a01b031690638f5b8e879060840160a060405180830381600087803b1580156121cd57600080fd5b505af11580156121e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122059190612c08565b601381905560028390556001829055939850919650945092509050841561228b576011546040516340c10f1960e01b8152306004820152602481018790526001600160a01b03909116906340c10f1990604401600060405180830381600087803b15801561227257600080fd5b505af1158015612286573d6000803e3d6000fd5b505050505b83156122f6576012546040516340c10f1960e01b8152306004820152602481018690526001600160a01b03909116906340c10f1990604401600060405180830381600087803b1580156122dd57600080fd5b505af11580156122f1573d6000803e3d6000fd5b505050505b5050505050505050565b6040516001600160a01b0383166024820152604481018290526108fe90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261252b565b6040516001600160a01b038085166024830152831660448201526064810182905261239b9085906323b872dd60e01b9060840161232c565b50505050565b6000821315806123b2575060008113155b156123e857604051819083907f27f70c863f1bd3e335bb4d6bf2a31075c861752f0b21a03b816a356035b1dec690600090a35050565b60015460025460055460405163b89bf2ef60e01b81526004810183905260248101849052600160801b8204608090811b6001600160801b03199081166044840152606483018890526084830187905292901b90911660a48201526000908190819081907358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063b89bf2ef9060c40160806040518083038186803b15801561248257600080fd5b505af4158015612496573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124ba9190612bd2565b604080518b850381528a8603602082015290810182905260608101839052939750919550935091507ff1e6677cbfc1dcb704bc91fc51ba87e318c4ed18c6a5571907e52a09a2c0f9aa9060800160405180910390a1600284905560018390556122f66125268284612e53565b6125fd565b6000612580826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166126c99092919063ffffffff16565b8051909150156108fe578080602001905181019061259e91906129a1565b6108fe5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610776565b6009546001600160a01b031661261857600b80548201905550565b600a5460405163fea3de3360e01b81526004810183905260248101919091526064604482018190526000917358639957c0e526ff4e4bb1cbfbdfefdeb16af2379163fea3de33910160206040518083038186803b15801561267857600080fd5b505af415801561268c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b09190612a08565b600c805482019055600b80549184039091019055505b50565b60606126d884846000856126e2565b90505b9392505050565b6060824710156127435760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610776565b843b6127915760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610776565b600080866001600160a01b031685876040516127ad9190612c9a565b60006040518083038185875af1925050503d80600081146127ea576040519150601f19603f3d011682016040523d82523d6000602084013e6127ef565b606091505b50915091506127ff82828661280a565b979650505050505050565b606083156128195750816126db565b8251156128295782518084602001fd5b8160405162461bcd60e51b81526004016107769190612d5c565b82805461284f90612ea5565b90600052602060002090601f01602090048101928261287157600085556128b7565b82601f1061288a5782800160ff198235161785556128b7565b828001600101855582156128b7579182015b828111156128b757823582559160200191906001019061289c565b506128c39291506128e5565b5090565b60405180604001604052806002906020820280368337509192915050565b5b808211156128c357600081556001016128e6565b60006020828403121561290c57600080fd5b81356126db81612f0c565b60006020828403121561292957600080fd5b81516126db81612f0c565b60008060006060848603121561294957600080fd5b833561295481612f0c565b9250602084013561296481612f0c565b929592945050506040919091013590565b6000806040838503121561298857600080fd5b823561299381612f0c565b946020939093013593505050565b6000602082840312156129b357600080fd5b81516126db81612f21565b6000806000606084860312156129d357600080fd5b833561295481612f21565b6000602082840312156129f057600080fd5b81516001600160801b0319811681146126db57600080fd5b600060208284031215612a1a57600080fd5b5051919050565b60008060408385031215612a3457600080fd5b82519150602083015167ffffffffffffffff80821115612a5357600080fd5b818501915085601f830112612a6757600080fd5b815181811115612a7957612a79612ef6565b604051601f8201601f19908116603f01168101908382118183101715612aa157612aa1612ef6565b81604052828152886020848701011115612aba57600080fd5b612acb836020830160208801612e79565b80955050505050509250929050565b60008060408385031215612aed57600080fd5b50508035926020909101359150565b600060208284031215612b0e57600080fd5b813567ffffffffffffffff811115612b2557600080fd5b820161022081850312156126db57600080fd5b600060208284031215612b4a57600080fd5b813561ffff811681146126db57600080fd5b600060208284031215612b6e57600080fd5b5035919050565b60008060408385031215612b8857600080fd5b505080516020909101519092909150565b600080600060608486031215612bae57600080fd5b83359250602084013591506040840135612bc781612f0c565b809150509250925092565b60008060008060808587031215612be857600080fd5b505082516020840151604085015160609095015191969095509092509050565b600080600080600060a08688031215612c2057600080fd5b5050835160208501516040860151606087015160809097015192989197509594509092509050565b600060208284031215612c5a57600080fd5b813563ffffffff811681146126db57600080fd5b60008151808452612c86816020860160208601612e79565b601f01601f19169290920160200192915050565b60008251612cac818460208701612e79565b9190910192915050565b6001600160a01b03841681526040602082018190528101829052818360608301376000818301606090810191909152601f909201601f1916010192915050565b60408101818360005b6002811015612d275781516001600160a01b0316835260209283019290910190600101612cff565b50505092915050565b848152608060208201526000612d496080830186612c6e565b6040830194909452506060015292915050565b6020815260006126db6020830184612c6e565b6020808252600e908201526d141bdbdb081a5cc81c185d5cd95960921b604082015260600190565b6020808252601c908201527f6d73672e73656e646572206e6f7420706f6f6c436f6d6d697474657200000000604082015260600190565b60208082526019908201527f6d73672e73656e646572206e6f7420676f7665726e616e636500000000000000604082015260600190565b6000808335601e19843603018112612e1c57600080fd5b83018035915067ffffffffffffffff821115612e3757600080fd5b602001915036819003821315612e4c57600080fd5b9250929050565b60008219821115612e7457634e487b7160e01b600052601160045260246000fd5b500190565b60005b83811015612e94578181015183820152602001612e7c565b8381111561239b5750506000910152565b600181811c90821680612eb957607f821691505b60208210811415612eda57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146126c657600080fd5b80151581146126c657600080fdfea26469706673582212200a4b8ff1742d412bdaabed3356502edd536bc0b2ad9447ad68ac9ad86860753e64736f6c634300080700336080604052600180546001600160801b0319168117905534801561002257600080fd5b50614ea7806100326000396000f3fe6080604052600436106102935760003560e01c80638f5b8e871161015a578063c45a0155116100c1578063ea348c9a1161007a578063ea348c9a146108fc578063f14fcbc814610953578063f3fa35a0146105eb578063f9dc972014610966578063fa87166514610986578063fe5898581461099b57600080fd5b8063c45a01551461084a578063c70db3231461086a578063ce66091614610890578063d4c4e283146108a6578063e20acc79146108bb578063e323aac6146108dc57600080fd5b8063af91497611610113578063af91497614610780578063b27a048414610796578063b59d88a9146107e0578063b9fc75b0146107f5578063c3bed53f14610815578063c40f6aa91461083557600080fd5b80638f5b8e871461062957806390f712cc1461067157806397ac250f1461068b5780639a546848146106a15780639d03132e146107405780639fa21dca1461076057600080fd5b806355c10624116101fe5780636fa1268e116101b75780636fa1268e146105785780637403c6cd146105965780638456cb59146105b6578063882d882b146105cb57806388b78aaa146105eb5780638d7269fa1461060957600080fd5b806355c106241461048b5780635a64ad95146104c65780635aa6e675146104e75780635c975abb146105075780636333091a146105385780636999b3771461055857600080fd5b80633ed4c678116102505780633ed4c6781461039c5780633f4ba83a146103bc5780634437152a146103d15780634509017e146103f15780634f64b2be1461042957806354da49921461044957600080fd5b8063062e0d69146102985780631aabc7e7146102ca5780631d92c26d146103135780631e83409a1461033a578063238a47091461035c578063384564e31461037c575b600080fd5b3480156102a457600080fd5b506102ad600081565b6040516001600160801b0390911681526020015b60405180910390f35b3480156102d657600080fd5b506102fa6102e5366004614a4d565b60066020526000908152604090205460801b81565b6040516001600160801b031990911681526020016102c1565b34801561031f57600080fd5b50610328600f81565b60405160ff90911681526020016102c1565b34801561034657600080fd5b5061035a61035536600461489c565b610a18565b005b34801561036857600080fd5b5061035a610377366004614a4d565b610de4565b34801561038857600080fd5b5061035a610397366004614a4d565b610f40565b3480156103a857600080fd5b5061035a6103b736600461489c565b6110a7565b3480156103c857600080fd5b5061035a611160565b3480156103dd57600080fd5b5061035a6103ec36600461489c565b6111f2565b3480156103fd57600080fd5b50601454610411906001600160a01b031681565b6040516001600160a01b0390911681526020016102c1565b34801561043557600080fd5b50610411610444366004614a4d565b61136a565b34801561045557600080fd5b5061046961046436600461489c565b61138a565b60408051825181526020808401519082015291810151908201526060016102c1565b34801561049757600080fd5b506104b86104a636600461489c565b600d6020526000908152604090205481565b6040519081526020016102c1565b3480156104d257600080fd5b506001546102fa90600160801b900460801b81565b3480156104f357600080fd5b50601154610411906001600160a01b031681565b34801561051357600080fd5b5060145461052890600160a01b900460ff1681565b60405190151581526020016102c1565b34801561054457600080fd5b50610528610553366004614a66565b611615565b34801561056457600080fd5b50601254610411906001600160a01b031681565b34801561058457600080fd5b506102fa66201bb1a2bc2ec560c91b81565b3480156105a257600080fd5b5061035a6105b13660046148b9565b61164f565b3480156105c257600080fd5b5061035a611c18565b3480156105d757600080fd5b506105286105e6366004614a66565b611cb0565b3480156105f757600080fd5b506102fa668075782dace9d960c71b81565b34801561061557600080fd5b50610528610624366004614a66565b611cd4565b34801561063557600080fd5b50610649610644366004614b85565b611cf9565b604080519586526020860194909452928401919091526060830152608082015260a0016102c1565b34801561067d57600080fd5b506002546102fa9060801b81565b34801561069757600080fd5b506104b860095481565b3480156106ad57600080fd5b5061070b6106bc366004614943565b600c602052816000526040600020602052806000526040600020600091509150508060000154908060010154908060020154908060030154908060040154908060050154908060060154905087565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e0016102c1565b34801561074c57600080fd5b506001546102ad906001600160801b031681565b34801561076c57600080fd5b5061035a61077b36600461489c565b612202565b34801561078c57600080fd5b506104b8600a5481565b3480156107a257600080fd5b506107d26107b1366004614a4d565b600560205260009081526040902054608081811b91600160801b9004901b82565b6040516102c1929190614bdd565b3480156107ec57600080fd5b506104b861273d565b34801561080157600080fd5b506104b8610810366004614943565b6127df565b34801561082157600080fd5b50610528610830366004614a66565b612810565b34801561084157600080fd5b506102ad600181565b34801561085657600080fd5b50601054610411906001600160a01b031681565b34801561087657600080fd5b50600054610411906201000090046001600160a01b031681565b34801561089c57600080fd5b506104b860085481565b3480156108b257600080fd5b506102ad612818565b3480156108c757600080fd5b506002546102fa90600160801b900460801b81565b3480156108e857600080fd5b50601354610411906001600160a01b031681565b34801561090857600080fd5b5061093861091736600461489c565b60076020526000908152604090208054600182015460029092015490919083565b604080519384526020840192909252908201526060016102c1565b61035a610961366004614a4d565b612a38565b34801561097257600080fd5b5061035a610981366004614a4d565b613118565b34801561099257600080fd5b506104b8613261565b3480156109a757600080fd5b506109eb6109b6366004614a4d565b600b60205260009081526040902080546001820154600283015460038401546004850154600590950154939492939192909186565b604080519687526020870195909552938501929092526060840152608083015260a082015260c0016102c1565b80336001600160a01b0382161480610a4057506000546201000090046001600160a01b031633145b610a9f5760405162461bcd60e51b815260206004820152602560248201527f6d73672e73656e646572206e6f7420636f6d6d6974746572206f72204175746f604482015264436c61696d60d81b60648201526084015b60405180910390fd5b601454600160a01b900460ff1615610aea5760405162461bcd60e51b815260206004820152600e60248201526d141bdbdb081a5cc81c185d5cd95960921b6044820152606401610a96565b610af382612202565b6001600160a01b0380831660008181526007602081815260408084208151606081018352815481526001820180548286015260028301805483860152601354898952969095529186905590859055918490555190949190911692917f0c7ef932d3b91976772937f18d5ef9b39a9930bef486b576c374f047c4b512dc91a2336001600160a01b038516148015610c0e5750600054604051631107d3c960e31b81526001600160a01b038681166004830152306024830152620100009092049091169063883e9e489060440160206040518083038186803b158015610bd657600080fd5b505afa158015610bea573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c0e9190614a01565b15610c7a576000546040516307119aeb60e01b81526001600160a01b03868116600483015262010000909204909116906307119aeb90602401600060405180830381600087803b158015610c6157600080fd5b505af1158015610c75573d6000803e3d6000fd5b505050505b604082015115610cee57604082810151905163d0b06f0960e01b81526001600160a01b03868116600483015260248201929092529082169063d0b06f0990604401600060405180830381600087803b158015610cd557600080fd5b505af1158015610ce9573d6000803e3d6000fd5b505050505b815115610d6357815160405163110b1bed60e01b8152600160048201526001600160a01b03868116602483015260448201929092529082169063110b1bed90606401600060405180830381600087803b158015610d4a57600080fd5b505af1158015610d5e573d6000803e3d6000fd5b505050505b602082015115610dde57602082015160405163110b1bed60e01b8152600060048201526001600160a01b03868116602483015260448201929092529082169063110b1bed90606401600060405180830381600087803b158015610dc557600080fd5b505af1158015610dd9573d6000803e3d6000fd5b505050505b50505050565b6012546001600160a01b03163314610e0e5760405162461bcd60e51b8152600401610a9690614c57565b604051636fedf26d60e11b8152600481018290527358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063dfdbe4da9060240160206040518083038186803b158015610e5957600080fd5b505af4158015610e6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e919190614a23565b60018054608092831c600160801b9081026001600160801b039092169190911791829055668075782dace9d960c71b910490911b6001600160801b0319161115610f125760405162461bcd60e51b81526020600482015260126024820152714d696e74696e6720666565203e203130302560701b6044820152606401610a96565b60405181907f07aefbcc0d8cdf34e241279c78d93d1df9540b5a60d320437958df12ef59e46390600090a250565b6012546001600160a01b03163314610f6a5760405162461bcd60e51b8152600401610a9690614c57565b604051636fedf26d60e11b8152600481018290527358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063dfdbe4da9060240160206040518083038186803b158015610fb557600080fd5b505af4158015610fc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fed9190614a23565b60028054608092831c600160801b9081026001600160801b039092169190911791829055668075782dace9d960c71b910490911b6001600160801b03191611156110795760405162461bcd60e51b815260206004820152601d60248201527f4368616e676520496e74657276616c2065786365656473206c696d69740000006044820152606401610a96565b60405181907f1f609e633399cb8530717d1970d31255fae2af322c2c3b90793cc1c1a7fe88be90600090a250565b6011546001600160a01b03163314806110ca57506012546001600160a01b031633145b6111165760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f742073657420666565436f6e74726f6c6c657200000000000000006044820152606401610a96565b601280546001600160a01b0319166001600160a01b0383169081179091556040517f4645d48e2e83635dc48865c27880f748bc610aab2e41ca3358057b565ff56a5790600090a250565b6011546001600160a01b031633146111ba5760405162461bcd60e51b815260206004820152601960248201527f6d73672e73656e646572206e6f7420676f7665726e616e6365000000000000006044820152606401610a96565b6014805460ff60a01b191690556040517fa45f47fdea8a1efdd9029a5691c7f759c32b7c698632b563573e155625d1693390600090a1565b6010546001600160a01b031633146112455760405162461bcd60e51b8152602060048201526016602482015275436f6d6d69747465723a206e6f7420666163746f727960501b6044820152606401610a96565b6001600160a01b03811661129b5760405162461bcd60e51b815260206004820152601d60248201527f4c657665726167656420706f6f6c2063616e6e6f74206265206e756c6c0000006044820152606401610a96565b601380546001600160a01b0319166001600160a01b0383169081179091556040805163369f189f60e11b81528151636d3e313e92600480840193919291829003018186803b1580156112ec57600080fd5b505afa158015611300573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611324919061496f565b6113329060039060026147ce565b506040516001600160a01b038216907fa84ed99a050d1610bc3dddf2c220cd77842a3ab1a46664af49062a70417debdf90600090a250565b6003816002811061137a57600080fd5b01546001600160a01b0316905081565b6113ae60405180606001604052806000815260200160008152602001600081525090565b6001600160a01b0382166000818152600760209081526040808320815160608082018452825482526001808401548387015260029093015482850152835160e08101855292546001600160801b0316835282850186905282840186905282018590526080820185905260a0820185905260c08201859052948452600e8352818420805483518186028101860190945280845291949390919083018282801561147557602002820191906000526020600020905b815481526020019060010190808311611461575b5050835193945050600f83109150611490905057600f611492565b805b60ff1660c084015260005b8360c0015160ff168110156115c15760008382815181106114c0576114c0614e30565b6020026020010151905080600014156114d957506115b9565b6001600160a01b0388166000908152600c60209081526040808320848452825291829020825160e08101845281548152600180830154938201939093526002820154938101939093526003810154606084015260048101546080840152600581015460a08401526006015460c0830181905290546001600160801b031611156115b6576000611567826132ad565b905080600001518760200181815161157f9190614d7d565b9052506020810151604088018051611598908390614d7d565b90525060808101516060880180516115b1908390614d7d565b905250505b50505b60010161149d565b506020830151845185906115d6908390614d7d565b90525060408301516020850180516115ef908390614d7d565b9052506060830151604085018051611608908390614d7d565b9052509295945050505050565b6000600282600581111561162b5761162b614e04565b1480611649575060005b82600581111561164757611647614e04565b145b92915050565b600054610100900460ff1680611668575060005460ff16155b6116cb5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610a96565b600054610100900460ff161580156116ed576000805461ffff19166101011790555b6001600160a01b03891661173c5760405162461bcd60e51b8152602060048201526016602482015275119858dd1bdc9e4818d85b9b9bdd081899481b9d5b1b60521b6044820152606401610a96565b6001600160a01b0388166117925760405162461bcd60e51b815260206004820152601860248201527f4175746f436c61696d2063616e6e6f74206265206e756c6c00000000000000006044820152606401610a96565b6001600160a01b0386166117e85760405162461bcd60e51b815260206004820152601d60248201527f66656520636f6e74726f6c6c65722063616e6e6f74206265206e756c6c0000006044820152606401610a96565b6001600160a01b03851661183e5760405162461bcd60e51b815260206004820152601d60248201527f696e76617269616e74436865636b2063616e6e6f74206265206e756c6c0000006044820152606401610a96565b600180546001600160801b03191681179055601080546001600160a01b038b81166001600160a01b0319928316179092556014805492881692909116919091179055604051636fedf26d60e11b8152600481018590527358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063dfdbe4da9060240160206040518083038186803b1580156118cb57600080fd5b505af41580156118df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119039190614a23565b6001805460809290921c600160801b026001600160801b03909216919091179055604051636fedf26d60e11b8152600481018490527358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063dfdbe4da9060240160206040518083038186803b15801561196f57600080fd5b505af4158015611983573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a79190614a23565b600280546001600160801b03191660809290921c919091179055604051636fedf26d60e11b8152600481018390527358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063dfdbe4da9060240160206040518083038186803b158015611a0c57600080fd5b505af4158015611a20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a449190614a23565b600280546001600160801b0316600160801b608093841c810291909117909155600154668075782dace9d960c71b6001600160801b03199290910490921b161115611ad15760405162461bcd60e51b815260206004820152601960248201527f4d696e74696e67206665652065786365656473206c696d6974000000000000006044820152606401610a96565b60025466201bb1a2bc2ec560c91b60809190911b6001600160801b0319161115611b3d5760405162461bcd60e51b815260206004820152601960248201527f4275726e696e67206665652065786365656473206c696d6974000000000000006044820152606401610a96565b600254668075782dace9d960c71b600160801b90910460801b6001600160801b0319161115611bae5760405162461bcd60e51b815260206004820152601d60248201527f4368616e676520496e74657276616c2065786365656473206c696d69740000006044820152606401610a96565b601280546001600160a01b038089166001600160a01b031992831617909255600080548b8416620100000262010000600160b01b031990911617905560118054928a16929091169190911790558015611c0d576000805461ff00191690555b505050505050505050565b6014546001600160a01b03163314611c725760405162461bcd60e51b815260206004820152601d60248201527f6d73672e73656e646572206e6f7420696e76617269616e74436865636b0000006044820152606401610a96565b6014805460ff60a01b1916600160a01b1790556040517f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e75290600090a1565b60006002826005811115611cc657611cc6614e04565b148061164957506003611635565b600060035b826005811115611ceb57611ceb614e04565b148061164957506001611635565b60135460009081908190819081906001600160a01b03163314611d5e5760405162461bcd60e51b815260206004820152601c60248201527f6d73672e73656e646572206e6f74206c6576657261676564506f6f6c000000006044820152606401610a96565b6040805160a081018083526003546318160ddd60e01b90915291516001926000929182916001600160a01b0316906318160ddd9060a480850191602091818703018186803b158015611daf57600080fd5b505afa158015611dc3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de79190614b1a565b8152602001600360010160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611e3d57600080fd5b505afa158015611e51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e759190614b1a565b81526000602080830182905260408084018390526060938401929092528351918401919091528201519082015290505b600f60ff83161161202e57611ebd60ff83168b614d95565b611ec7908c614d7d565b421061201e57600180546001600160801b03908116608084810182815260028054600094855260066020908152604080872080546001600160801b031916939098169290921790965591518452600b855292819020815160c0810183528154815295810154948601949094529183015491840191909152600382015460608401526004820154908301526005015460a0820152611f6690828b8b613435565b602085810193909352928452608080850151600254604051921b6001600160801b0319168252919c50929a50917f260463f9bbb6d40c58e9fc4e09a71c3f80fc18d818d6d9d5df86b2443722a59f910160405180910390a260808101516000908152600b60205260408120818155600180820183905560028201839055600382018390556004820183905560059091019190915580546001600160801b038082168301166001600160801b0319909116179055612023565b61202e565b600182019150611ea5565b8051604051635cf4ee9160e01b8152600481018b90526024810191909152612159907358639957c0e526ff4e4bb1cbfbdfefdeb16af23790635cf4ee919060440160206040518083038186803b15801561208757600080fd5b505af415801561209b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120bf9190614a23565b6020830151604051635cf4ee9160e01b81527358639957c0e526ff4e4bb1cbfbdfefdeb16af23791635cf4ee9191612104918e91600401918252602082015260400190565b60206040518083038186803b15801561211c57600080fd5b505af4158015612130573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121549190614a23565b613c16565b600f60ff8316118015612197575089612173600184614dcb565b6121809060ff168c614d95565b61218a908d614d7d565b6121949042614db4565b10155b156121c5576121a7600183614dcb565b6121b49060ff168b614d95565b6121be908c614d7d565b9a506121c9565b429a505b604081015181516121da9190614db4565b816060015182602001516121ee9190614db4565b909c909b999a509798979650505050505050565b6001600160a01b0381166000818152600760209081526040808320815160e0810183526001546001600160801b03168152808401859052808301859052606081018590526080810185905260a0810185905260c08101859052948452600e83528184208054835181860281018601909452808452919594939091908301828280156122ac57602002820191906000526020600020905b815481526020019060010190808311612298575b5050835193945050600f831091506122c7905057600f6122c9565b805b60ff1660c084015260005b8360c0015160ff168110156125605760008382815181106122f7576122f7614e30565b6020026020010151905080600014156123105750612558565b6001600160a01b0387166000908152600c60209081526040808320848452825291829020825160e08101845281548152600180830154938201939093526002820154938101939093526003810154606084015260048101546080840152600581015460a08401526006015460c0830181905290546001600160801b0316111561251957600061239e826132ad565b90508060000151876020018181516123b69190614d7d565b90525060208101516040880180516123cf908390614d7d565b90525060808101516060880180516123e8908390614d7d565b9052506040810151608088018051612401908390614d7d565b905250606081015160a08801805161241a908390614d7d565b9052506001600160a01b0389166000818152600c6020908152604080832087845282528083208381556001810184905560028101849055600381018490556004810184905560058101849055600601839055928252600e905220600f86118015612485575080546001105b801561249d5750805461249a90600190614db4565b85105b156124ec57805481906124b290600190614db4565b815481106124c2576124c2614e30565b90600052602060002001548186815481106124df576124df614e30565b6000918252602090912001555b808054806124fc576124fc614e1a565b600190038181906000526020600020016000905590555050612555565b600f841161255557600f85848151811061253557612535614e30565b602090810291909101810151825460018101845560009384529190922001555b50505b6001016122d4565b50600f811161259d576001600160a01b0385166000908152600e60205260409020600f8054612590929190614826565b5061259d600f6000614866565b82602001518460000160008282546125b59190614d7d565b909155505060408301516001850180546000906125d3908490614d7d565b909155505060608301516002850180546000906125f1908490614d7d565b909155505060135460408051630f77314d60e31b815281516001600160a01b039093169260009283928592637bb98a6892600480840193919291829003018186803b15801561263f57600080fd5b505afa158015612653573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126779190614b33565b91509150826001600160a01b0316633c3f825287608001518361269a9190614d7d565b60a08901516126a99086614d7d565b6040516001600160e01b031960e085901b16815260048101929092526024820152604401600060405180830381600087803b1580156126e757600080fd5b505af11580156126fb573d6000803e3d6000fd5b50506040516001600160a01b038b1692507fdefa4140341b4fd283af41700779361dc94f34ae245a186b3b9c187033c065549150600090a25050505050505050565b60015460405163aeb64d7d60e01b8152600160801b90910460801b6001600160801b03191660048201526000907358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063aeb64d7d906024015b60206040518083038186803b1580156127a257600080fd5b505af41580156127b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127da9190614b1a565b905090565b600e60205281600052604060002081815481106127fb57600080fd5b90600052602060002001600091509150505481565b600080611cd9565b60135460408051637de93f9360e01b815290516000926001600160a01b0316917358639957c0e526ff4e4bb1cbfbdfefdeb16af2379163ff9afe879142918591637de93f93916004808301926020929190829003018186803b15801561287d57600080fd5b505afa158015612891573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128b59190614b1a565b846001600160a01b0316637bfe789a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156128ee57600080fd5b505afa158015612902573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129269190614bb7565b856001600160a01b031663fd2c80ae6040518163ffffffff1660e01b815260040160206040518083038186803b15801561295f57600080fd5b505afa158015612973573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129979190614bb7565b60015460405160e087901b6001600160e01b03191681526004810195909552602485019390935263ffffffff91821660448501521660648301526001600160801b0316608482015260a40160206040518083038186803b1580156129fa57600080fd5b505af4158015612a0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a329190614b1a565b91505090565b6001600160801b03811660ff608083901c811690608884901c811690609085901c1683612aa75760405162461bcd60e51b815260206004820152601760248201527f416d6f756e74206d757374206e6f74206265207a65726f0000000000000000006044820152606401610a96565b612ab033612202565b60135460408051637e96405760e11b815290516001600160a01b0390921691600091839163fd2c80ae91600480820192602092909190829003018186803b158015612afa57600080fd5b505afa158015612b0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b329190614bb7565b63ffffffff1690506000826001600160a01b0316637de93f936040518163ffffffff1660e01b815260040160206040518083038186803b158015612b7557600080fd5b505afa158015612b89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bad9190614b1a565b90506000836001600160a01b0316637bfe789a6040518163ffffffff1660e01b815260040160206040518083038186803b158015612bea57600080fd5b505afa158015612bfe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c229190614bb7565b60015460405160016265017960e01b031981524260048201526024810185905263ffffffff9290921660448301819052606483018690526001600160801b03909116608483015291506000907358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063ff9afe879060a40160206040518083038186803b158015612ca557600080fd5b505af4158015612cb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cdd9190614b1a565b6000818152600b60209081526040808320338452600c835281842085855290925290912060068101549293509091612d1757600681018390555b336000908152600e6020526040902054801580612d685750336000908152600e602052604090208490612d4b600184614db4565b81548110612d5b57612d5b614e30565b9060005260206000200154105b15612d9057336000908152600e60209081526040822080546001810182559083529120018490555b604051633199848d60e11b81523090636333091a90612db3908e90600401614bf8565b60206040518083038186803b158015612dcb57600080fd5b505afa158015612ddf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e039190614a01565b8015612e0c5750895b15612e9b57336000908152600760205260409020600201548c1115612e735760405162461bcd60e51b815260206004820152601e60248201527f496e73756666696369656e7420736574746c656d656e7420746f6b656e7300006044820152606401610a96565b33600090815260076020526040812060020180548e9290612e95908490614db4565b90915550505b612ea9888c8e8d868861404a565b604051633199848d60e11b81523090636333091a90612ecc908e90600401614bf8565b60206040518083038186803b158015612ee457600080fd5b505afa158015612ef8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f1c9190614a01565b8015612f26575089155b15612f9857601354604051630348287760e21b81523360048201526001600160a01b039182166024820152604481018e905290891690630d20a1dc90606401600060405180830381600087803b158015612f7f57600080fd5b505af1158015612f93573d6000803e3d6000fd5b505050505b88156130495734612fe05760405162461bcd60e51b81526020600482015260126024820152714d7573742070617920666f7220636c61696d60701b6044820152606401610a96565b600054604051634da17a3f60e11b8152336004820152620100009091046001600160a01b031690639b42f47e9034906024016000604051808303818588803b15801561302b57600080fd5b505af115801561303f573d6000803e3d6000fd5b5050505050613090565b34156130905760405162461bcd60e51b81526020600482015260166024820152756d73672e76616c7565206d757374206265207a65726f60501b6044820152606401610a96565b8a60058111156130a2576130a2614e04565b600154604080518781528d151560208201528c151591810191909152600160801b909104608090811b6001600160801b03191660608301528e9133917f04f9dae5361dfaa5b3f324fc0af5e71bcb4d76e8324ad9ce78d70496c548d503910160405180910390a450505050505050505050505050565b6012546001600160a01b031633146131425760405162461bcd60e51b8152600401610a9690614c57565b604051636fedf26d60e11b8152600481018290527358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063dfdbe4da9060240160206040518083038186803b15801561318d57600080fd5b505af41580156131a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131c59190614a23565b60028054608092831c6001600160801b0319918216179182905566201bb1a2bc2ec560c91b9190921b909116106132335760405162461bcd60e51b81526020600482015260126024820152714275726e696e6720666565203e3d2031302560701b6044820152606401610a96565b60405181907f9bd544d4dd4e5359b6126812d7f52828638adf3d7beab61310fab58b27d811cc90600090a250565b60025460405163aeb64d7d60e01b815260809190911b6001600160801b03191660048201526000907358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063aeb64d7d9060240161278a565b6132df6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b604080516101808101825260c08085018051600090815260056020818152868320546001600160801b0319608091821b811688528551855292825287842054600160801b90819004821b841683890152600154908104821b8416888a01526001600160801b03166060808901919091528551828901528a5160a0808a0191909152838c0151978901979097528a89015160e08901528a015161010088015294890151610120870152848901516101408701529251825260069092528490205490911b166101608201529051639eb17fa360e01b81527358639957c0e526ff4e4bb1cbfbdfefdeb16af23790639eb17fa3906133de908490600401614c8e565b60a06040518083038186803b1580156133f657600080fd5b505af415801561340a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061342e9190614aaa565b9392505050565b6080830180516000908152600b6020526040808220600201549251825281205460085491928392839283929161346b9190614db4565b6134759190614db4565b6008819055506000604051806101000160405280878b604001516134999190614d7d565b8152602001888b600001516134ae9190614d7d565b8152602001600081526020016000815260200160008152602001600081526020018a60a001518b602001516134e39190614d7d565b81526020018a608001518b606001516134fc9190614d7d565b815250905060007358639957c0e526ff4e4bb1cbfbdfefdeb16af237635cf4ee9189600a548c600001516135309190614d7d565b6040516001600160e01b031960e085901b1681526004810192909252602482015260440160206040518083038186803b15801561356c57600080fd5b505af4158015613580573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135a49190614a23565b905060007358639957c0e526ff4e4bb1cbfbdfefdeb16af237635cf4ee91896009548d602001516135d59190614d7d565b6040516001600160e01b031960e085901b1681526004810192909252602482015260440160206040518083038186803b15801561361157600080fd5b505af4158015613625573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136499190614a23565b90506040518060400160405280836001600160801b0319168152602001826001600160801b031916815250600560008c60800151815260200190815260200160002060008201518160000160006101000a8154816001600160801b03021916908360801c021790555060208201518160000160106101000a8154816001600160801b03021916908360801c021790555090505060007358639957c0e526ff4e4bb1cbfbdfefdeb16af23763839dff3d8d60a0015185600260009054906101000a900460801b600160109054906101000a900460801b6040518563ffffffff1660e01b815260040161376094939291909384526001600160801b03199283166020850152908216604084015216606082015260800190565b60606040518083038186803b15801561377857600080fd5b505af415801561378c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137b09190614b57565b505060808501819052845190915084906137cb908390614d7d565b90525060808b81015160025460015460405163839dff3d60e01b815260048101939093526001600160801b0319858116602485015291841b82166044840152600160801b900490921b90911660648201526000907358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063839dff3d9060840160606040518083038186803b15801561385657600080fd5b505af415801561386a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061388e9190614b57565b505060a08501819052602085018051919250906138ac908390614d7d565b905250895160a08401518c517358639957c0e526ff4e4bb1cbfbdfefdeb16af2379263ab41556f9290916138e09190614d7d565b600a546040516001600160e01b031960e086901b16815260048101939093526024830191909152604482018d9052606482015260840160206040518083038186803b15801561392e57600080fd5b505af4158015613942573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139669190614b1a565b6040848101919091528a5160c0850151600a54925163debb52df60e01b815260048101929092526024820152604481018b905260648101919091527358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063debb52df9060840160206040518083038186803b1580156139d857600080fd5b505af41580156139ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a109190614b1a565b83602001818151613a219190614db4565b915081815250507358639957c0e526ff4e4bb1cbfbdfefdeb16af23763ab41556f8b6020015185608001518e60400151613a5b9190614d7d565b6009546040516001600160e01b031960e086901b16815260048101939093526024830191909152604482018c9052606482015260840160206040518083038186803b158015613aa957600080fd5b505af4158015613abd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ae19190614b1a565b606084015260208a015160e084015160095460405163debb52df60e01b815260048101939093526024830191909152604482018a905260648201527358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063debb52df9060840160206040518083038186803b158015613b5357600080fd5b505af4158015613b67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b8b9190614b1a565b83518490613b9a908390614db4565b90525060c0830151600a8054600090613bb4908490614db4565b909155505060e083015160098054600090613bd0908490614db4565b909155505060408301518a51613be69190614d7d565b83606001518b60200151613bfa9190614d7d565b60208501519451919d909c50939a509850919650505050505050565b6040516345e7ef9b60e01b81526000907358639957c0e526ff4e4bb1cbfbdfefdeb16af237906345e7ef9b90613c529086908690600401614bdd565b60206040518083038186803b158015613c6a57600080fd5b505af4158015613c7e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ca29190614a23565b604051630308593b60e31b81529091507358639957c0e526ff4e4bb1cbfbdfefdeb16af23790631842c9d890613ce390613fff60f01b908590600401614bdd565b60206040518083038186803b158015613cfb57600080fd5b505af4158015613d0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d339190614a87565b60000b6000191415613ec657600154600254604051630308593b60e31b81527358639957c0e526ff4e4bb1cbfbdfefdeb16af23792631842c9d892613d9092600160801b92839004608090811b9390920490911b90600401614bdd565b60206040518083038186803b158015613da857600080fd5b505af4158015613dbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613de09190614a87565b60000b6000191415613e0057600180546001600160801b03169055505050565b600154600254604051630e8418dd60e01b81527358639957c0e526ff4e4bb1cbfbdfefdeb16af23792630e8418dd92613e5192600160801b92839004608090811b9390920490911b90600401614bdd565b60206040518083038186803b158015613e6957600080fd5b505af4158015613e7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ea19190614a23565b600160106101000a8154816001600160801b03021916908360801c0217905550505050565b600154600254604051635b597cfb60e11b81527358639957c0e526ff4e4bb1cbfbdfefdeb16af2379263b6b2f9f692613f1792600160801b92839004608090811b9390920490911b90600401614bdd565b60206040518083038186803b158015613f2f57600080fd5b505af4158015613f43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f679190614a23565b600180546001600160801b0316600160801b608093841c81029190911791829055604051630308593b60e31b81527358639957c0e526ff4e4bb1cbfbdfefdeb16af23793631842c9d893613fce939004901b90668075782dace9d960c71b90600401614bdd565b60206040518083038186803b158015613fe657600080fd5b505af4158015613ffa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061401e9190614a87565b60000b6001141561404557600180546001600160801b0316668075782dace9d960c71b1790555b505050565b336000908152600760209081526040808320815160608101835281548152600182015493810193909352600201548282015251633199848d60e11b81529091903090636333091a906140a0908a90600401614bf8565b60206040518083038186803b1580156140b857600080fd5b505afa1580156140cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140f09190614a01565b156141bb5760015460405163aacf0ecb60e01b8152600160801b90910460801b6001600160801b0319166004820152602481018790527358639957c0e526ff4e4bb1cbfbdfefdeb16af2379063aacf0ecb9060440160206040518083038186803b15801561415d57600080fd5b505af4158015614171573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141959190614b1a565b90506141a18187614db4565b955085600860008282546141b59190614d7d565b90915550505b60028760058111156141cf576141cf614e04565b14156142f757600080896001600160a01b0316637bb98a686040518163ffffffff1660e01b8152600401604080518083038186803b15801561421057600080fd5b505afa158015614224573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142489190614b33565b91509150878660000160008282546142609190614d7d565b9091555050845488908690600090614279908490614d7d565b90915550506001600160a01b038a16633c3f82526142978584614d7d565b846040518363ffffffff1660e01b81526004016142be929190918252602082015260400190565b600060405180830381600087803b1580156142d857600080fd5b505af11580156142ec573d6000803e3d6000fd5b505050505050610dd9565b600387600581111561430b5761430b614e04565b14156144475785600a60008282546143239190614d7d565b925050819055508584600101600082825461433e9190614d7d565b92505081905550858360010160008282546143599190614d7d565b909155505084156144165781518611156143855760405162461bcd60e51b8152600401610a9690614c20565b33600090815260076020526040812080548892906143a4908490614db4565b90915550506013546040516387f9ca5d60e01b81526001600160a01b03808b16926387f9ca5d926143df926000928c92911690600401614d53565b600060405180830381600087803b1580156143f957600080fd5b505af115801561440d573d6000803e3d6000fd5b50505050610dd9565b6040516387f9ca5d60e01b81526001600160a01b038916906387f9ca5d906143df906000908a903390600401614d53565b600087600581111561445b5761445b614e04565b141561454f57600080896001600160a01b0316637bb98a686040518163ffffffff1660e01b8152600401604080518083038186803b15801561449c57600080fd5b505afa1580156144b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144d49190614b33565b91509150878660020160008282546144ec9190614d7d565b92505081905550878560020160008282546145079190614d7d565b90915550506001600160a01b038a16633c3f8252826145268686614d7d565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016142be565b600187600581111561456357614563614e04565b141561466e57856009600082825461457b9190614d7d565b92505081905550858460030160008282546145969190614d7d565b92505081905550858360030160008282546145b19190614d7d565b9091555050841561463d5781602001518611156145e05760405162461bcd60e51b8152600401610a9690614c20565b3360009081526007602052604081206001018054889290614602908490614db4565b90915550506013546040516387f9ca5d60e01b81526001600160a01b03808b16926387f9ca5d926143df926001928c92911690600401614d53565b6040516387f9ca5d60e01b81526001600160a01b038916906387f9ca5d906143df906001908a903390600401614d53565b600487600581111561468257614682614e04565b14156146d05785600a600082825461469a9190614d7d565b92505081905550858460050160008282546146b59190614d7d565b92505081905550858360050160008282546143599190614d7d565b60058760058111156146e4576146e4614e04565b1415610dd95785600960008282546146fc9190614d7d565b92505081905550858460040160008282546147179190614d7d565b92505081905550858360040160008282546147329190614d7d565b909155505084156147615781602001518611156145e05760405162461bcd60e51b8152600401610a9690614c20565b6040516387f9ca5d60e01b81526001600160a01b038916906387f9ca5d90614792906001908a903390600401614d53565b600060405180830381600087803b1580156147ac57600080fd5b505af11580156147c0573d6000803e3d6000fd5b505050505050505050505050565b8260028101928215614816579160200282015b8281111561481657825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906147e1565b50614822929150614887565b5090565b8280548282559060005260206000209081019282156148165760005260206000209182015b8281111561481657825482559160010191906001019061484b565b50805460008255906000526020600020908101906148849190614887565b50565b5b808211156148225760008155600101614888565b6000602082840312156148ae57600080fd5b813561342e81614e5c565b600080600080600080600080610100898b0312156148d657600080fd5b88356148e181614e5c565b975060208901356148f181614e5c565b9650604089013561490181614e5c565b9550606089013561491181614e5c565b9450608089013561492181614e5c565b979a969950949793969560a0850135955060c08501359460e001359350915050565b6000806040838503121561495657600080fd5b823561496181614e5c565b946020939093013593505050565b60006040828403121561498157600080fd5b82601f83011261499057600080fd5b6040516040810181811067ffffffffffffffff821117156149b3576149b3614e46565b80604052508083856040860111156149ca57600080fd5b60005b60028110156149f65781516149e181614e5c565b835260209283019291909101906001016149cd565b509195945050505050565b600060208284031215614a1357600080fd5b8151801515811461342e57600080fd5b600060208284031215614a3557600080fd5b81516001600160801b03198116811461342e57600080fd5b600060208284031215614a5f57600080fd5b5035919050565b600060208284031215614a7857600080fd5b81356006811061342e57600080fd5b600060208284031215614a9957600080fd5b81518060000b811461342e57600080fd5b600060a08284031215614abc57600080fd5b60405160a0810181811067ffffffffffffffff82111715614adf57614adf614e46565b806040525082518152602083015160208201526040830151604082015260608301516060820152608083015160808201528091505092915050565b600060208284031215614b2c57600080fd5b5051919050565b60008060408385031215614b4657600080fd5b505080516020909101519092909150565b600080600060608486031215614b6c57600080fd5b8351925060208401519150604084015190509250925092565b60008060008060808587031215614b9b57600080fd5b5050823594602084013594506040840135936060013592509050565b600060208284031215614bc957600080fd5b815163ffffffff8116811461342e57600080fd5b6001600160801b031992831681529116602082015260400190565b6020810160068310614c1a57634e487b7160e01b600052602160045260246000fd5b91905290565b60208082526018908201527f496e73756666696369656e7420706f6f6c20746f6b656e730000000000000000604082015260600190565b6020808252601d908201527f6d73672e73656e646572206e6f742066656520636f6e74726f6c6c6572000000604082015260600190565b81516001600160801b031916815261018081016020830151614cbc60208401826001600160801b0319169052565b506040830151614cd860408401826001600160801b0319169052565b50606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151818401525061014080840151818401525061016080840151614d4b828501826001600160801b0319169052565b505092915050565b6001600160801b0393909316835260208301919091526001600160a01b0316604082015260600190565b60008219821115614d9057614d90614dee565b500190565b6000816000190483118215151615614daf57614daf614dee565b500290565b600082821015614dc657614dc6614dee565b500390565b600060ff821660ff841680821015614de557614de5614dee565b90039392505050565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461488457600080fdfea2646970667358221220de70843153c66cf73755c030a6488177c99036fdaeb260b1b90ab9c3998fbb9164736f6c634300080700330000000000000000000000000f79e82ae88e1318b8cfc8b4a205fe2f982b928a000000000000000000000000cc97eb5651e05d5a0ae8bcd2813b9dfdad6f92a5

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

0000000000000000000000000f79e82ae88e1318b8cfc8b4a205fe2f982b928a000000000000000000000000cc97eb5651e05d5a0ae8bcd2813b9dfdad6f92a5

-----Decoded View---------------
Arg [0] : _feeReceiver (address): 0x0f79e82ae88e1318b8cfc8b4a205fe2f982b928a
Arg [1] : _governance (address): 0xcc97eb5651e05d5a0ae8bcd2813b9dfdad6f92a5

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000000f79e82ae88e1318b8cfc8b4a205fe2f982b928a
Arg [1] : 000000000000000000000000cc97eb5651e05d5a0ae8bcd2813b9dfdad6f92a5


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.