Contract 0x9c6b9bd786adb1f644cedbdcb193203cbc90d1af

 

Contract Overview

Dopex: ETH Atlantic Puts Pool
Balance:
0 ETH

ETH Value:
$0.00

Token:
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xdf55b2720164d8f7b6054c9d64320a8334ee7c11f2f49b604a406d239627cb1cEmergency Withdr...735904302023-03-25 16:52:42179 days 16 hrs ago0x29ed22a9e56ee1813e6ff69fc6cac676aa24c09c IN  Dopex: ETH Atlantic Puts Pool0 ETH0.0000681 0.1
0xd2f53e4e3b2e99cf9ae54cb54ced59411e6cab213f74078f9fdefedfec66c27cPause735893232023-03-25 16:48:09179 days 16 hrs ago0x29ed22a9e56ee1813e6ff69fc6cac676aa24c09c IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00003135 0.1
0xdfafbda6600a5ce37541a4a67e10175b0856e2bf849ca1902d0c1a3fcf11cb43Bootstrap735888882023-03-25 16:46:20179 days 16 hrs ago0x29ed22a9e56ee1813e6ff69fc6cac676aa24c09c IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00005419 0.1
0x4edb41042d73cda8e6493b7f9766c554c23d58acf862f826061a748cfe5f346aExpire Epoch731126372023-03-24 8:00:54181 days 1 hr ago0x29ed22a9e56ee1813e6ff69fc6cac676aa24c09c IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00003953 0.10513
0xaba6b81f556722f11d0f77755b76480ac414668955cf381aca745cce8a4c1e73Deposit727335902023-03-23 6:43:04182 days 2 hrs ago0x62c3e540dd14e3ba57af110ab8ba2165d06cb595 IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00007205 0.1
0x04f32a7f6e2e91b90839c4085ca62055cb9ce5eb7536a297704d4f02d9cd7272Withdraw708113242023-03-17 17:06:24187 days 16 hrs ago0x9a6b9f703de8adf7e7dda5dc5fb3256562266525 IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00006923 0.1
0xe5eceb149eee37b036f5851b9b6bf19a4bb47bac9e24a89f86ba493fe4d3964aSet Epoch Reward...706834332023-03-17 8:17:02188 days 1 hr ago0x29ed22a9e56ee1813e6ff69fc6cac676aa24c09c IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00007106 0.1
0x558b23e7c908a2c90978d52adbab14c636b7161acbd5065c8f0f58cc79ba576bRollover Deposit...706827522023-03-17 8:14:17188 days 1 hr ago0x29ed22a9e56ee1813e6ff69fc6cac676aa24c09c IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00022423 0.1
0xe9e8ffdc7bb3ea5146a1f45e8dd5f7231404e5a2faf7530b227ef19ed8f404b3Bootstrap706819982023-03-17 8:11:05188 days 1 hr ago0x29ed22a9e56ee1813e6ff69fc6cac676aa24c09c IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00004879 0.1
0xab8d824cef84d4c6b5924ce868d617ad4742463920076cecd2da8c417fce3e39Expire Epoch706813882023-03-17 8:08:36188 days 1 hr ago0x29ed22a9e56ee1813e6ff69fc6cac676aa24c09c IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00002813 0.1
0xe54ff247db51480c470bd09f13f5c97c95be85b36aa073375b3e435ec2a15437Withdraw685357012023-03-10 12:58:28194 days 20 hrs ago0x76d2ddce6b781e66c4b184c82fbf4f94346cfb0d IN  Dopex: ETH Atlantic Puts Pool0 ETH0.0000682 0.1
0x0413663d0cb2c5236ba6e8c31e9d336d7ab349f7d24c0d2b9dacf4fc910ce7ffSet Deposit Roll...684803112023-03-10 8:57:31195 days 28 mins ago0x9a6b9f703de8adf7e7dda5dc5fb3256562266525 IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00003613 0.1
0xc9bbdf4a6bb3d8cbd3f16edc85e9044f2a20c499cbf69c7d34a14937c498debeDeposit684801832023-03-10 8:56:58195 days 28 mins ago0x9a6b9f703de8adf7e7dda5dc5fb3256562266525 IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00008271 0.1
0xa10a3ed8f6c28183b283d5bff386cc87665233f8c672bc78537fd93cc294a836Withdraw684797122023-03-10 8:55:00195 days 30 mins ago0x9a6b9f703de8adf7e7dda5dc5fb3256562266525 IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00006168 0.1
0xde7bfc4f22103431c774425d9921fffa9674946834a062828ff2a2f80a461594Rollover Deposit...684681032023-03-10 8:05:29195 days 1 hr ago0x29ed22a9e56ee1813e6ff69fc6cac676aa24c09c IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00025142 0.1
0x778d0f596cd2ee14b5a75ece51b044fea4935ce613c4316bdc3b8df895340837Bootstrap684673342023-03-10 8:02:14195 days 1 hr ago0x29ed22a9e56ee1813e6ff69fc6cac676aa24c09c IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00006086 0.1
0x145975d3279dc90e41d8638a68610a1b7f338d227a470c6109ab56b6afc17cf4Expire Epoch684670502023-03-10 8:00:59195 days 1 hr ago0x29ed22a9e56ee1813e6ff69fc6cac676aa24c09c IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00004311 0.1
0x84150aaed31a8cd8129d7f79c950f3687297fcbfb2389ef46393524cabaefe05Set Epoch Reward...684630542023-03-10 7:43:41195 days 1 hr ago0x29ed22a9e56ee1813e6ff69fc6cac676aa24c09c IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00008795 0.1
0x5e8a439b0c272749896cb0ac7d785b077b805662a9b666ea798e64771aced03fSet Deposit Roll...680626712023-03-09 1:16:51196 days 8 hrs ago0x9a6b9f703de8adf7e7dda5dc5fb3256562266525 IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00005802 0.1
0xc7390a00168bc52296f10414de82026bf909e8e972297c063a1d74f70fbc8e56Deposit679499852023-03-08 15:45:45196 days 17 hrs ago0x9a6b9f703de8adf7e7dda5dc5fb3256562266525 IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00010001 0.1
0x6c1fe9081f8454e0da799cf9d59454a05acf286bb757ca03e31ef4cb3d029d89Withdraw679490882023-03-08 15:41:50196 days 17 hrs ago0x9a6b9f703de8adf7e7dda5dc5fb3256562266525 IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00009516 0.1
0xf2980605fc52edba8d69f24a7d6d97cc2eb71fc0722e90a3122033569b8ef986Deposit674091422023-03-06 21:49:58198 days 11 hrs ago0xf51e5a0a85d29aa0508894405d734bcf044dcb5b IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00015661 0.1
0xa6c4adc16215eeeba1ec34d43f52d9f207ae4d46f8508e96412955f2ef1e8855Set Deposit Roll...673320352023-03-06 15:48:42198 days 17 hrs ago0x76d2ddce6b781e66c4b184c82fbf4f94346cfb0d IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00006986 0.1
0x723f839c84d8e19b2ed31ab6081fe6ceeb83b0fcd9a36855217759f3e95f6126Deposit673298042023-03-06 15:39:12198 days 17 hrs ago0x76d2ddce6b781e66c4b184c82fbf4f94346cfb0d IN  Dopex: ETH Atlantic Puts Pool0 ETH0.00011225 0.1
0x031aaee5fbf865c8737a803d13948a6610e3ff6dc564c354bfb4ef596f9c4e13Withdraw670783922023-03-05 19:52:28199 days 13 hrs ago0x1a2898a0b1daabc3e3a29fa534b08d9d6ab98602 IN  Dopex: ETH Atlantic Puts Pool0 ETH0.000093130.1
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x476f55c244e3afc1872e337aedd424a4661317a840365860903b3d9676ffe4f3716234632023-03-20 2:09:00185 days 7 hrs ago Dopex: ETH Atlantic Puts Pool Arbitrum: USDC Token0 ETH
0x476f55c244e3afc1872e337aedd424a4661317a840365860903b3d9676ffe4f3716234632023-03-20 2:09:00185 days 7 hrs ago Dopex: ETH Atlantic Puts Pool Arbitrum: USDC Token0 ETH
0x476f55c244e3afc1872e337aedd424a4661317a840365860903b3d9676ffe4f3716234632023-03-20 2:09:00185 days 7 hrs ago 0x0980a15ad64182b275b625c321beeab41e0ca1a0 Dopex: ETH Atlantic Puts Pool0 ETH
0x476f55c244e3afc1872e337aedd424a4661317a840365860903b3d9676ffe4f3716234632023-03-20 2:09:00185 days 7 hrs ago 0x0980a15ad64182b275b625c321beeab41e0ca1a0 Dopex: ETH Atlantic Puts Pool0 ETH
0x476f55c244e3afc1872e337aedd424a4661317a840365860903b3d9676ffe4f3716234632023-03-20 2:09:00185 days 7 hrs ago 0x0980a15ad64182b275b625c321beeab41e0ca1a0 Dopex: ETH Atlantic Puts Pool0 ETH
0x476f55c244e3afc1872e337aedd424a4661317a840365860903b3d9676ffe4f3716234632023-03-20 2:09:00185 days 7 hrs ago 0x0980a15ad64182b275b625c321beeab41e0ca1a0 Dopex: ETH Atlantic Puts Pool0 ETH
0x476f55c244e3afc1872e337aedd424a4661317a840365860903b3d9676ffe4f3716234632023-03-20 2:09:00185 days 7 hrs ago 0x0980a15ad64182b275b625c321beeab41e0ca1a0 Dopex: ETH Atlantic Puts Pool0 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago Dopex: ETH Atlantic Puts Pool Arbitrum: USDC Token0 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago Dopex: ETH Atlantic Puts Pool Arbitrum: USDC Token0 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago Dopex: ETH Atlantic Puts Pool 0x2d68011bca022ed0e474264145f46cc4de96a0020 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago Dopex: ETH Atlantic Puts Pool 0xbd8d541500c10774cd1404f57344d233eec425c90 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago Dopex: ETH Atlantic Puts Pool Dopex: Option Pricing0 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago Dopex: ETH Atlantic Puts Pool 0xab59586a62a1a5ad6dffe64e3bc583261b16e0d20 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago Dopex: ETH Atlantic Puts Pool 0x2d68011bca022ed0e474264145f46cc4de96a0020 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago 0x0980a15ad64182b275b625c321beeab41e0ca1a0 Dopex: ETH Atlantic Puts Pool0 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago Dopex: ETH Atlantic Puts Pool Dopex: Option Pricing0 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago Dopex: ETH Atlantic Puts Pool 0xab59586a62a1a5ad6dffe64e3bc583261b16e0d20 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago Dopex: ETH Atlantic Puts Pool 0x2d68011bca022ed0e474264145f46cc4de96a0020 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago 0x0980a15ad64182b275b625c321beeab41e0ca1a0 Dopex: ETH Atlantic Puts Pool0 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago Dopex: ETH Atlantic Puts Pool 0x2d68011bca022ed0e474264145f46cc4de96a0020 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago Dopex: ETH Atlantic Puts Pool 0xbd8d541500c10774cd1404f57344d233eec425c90 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago 0x0980a15ad64182b275b625c321beeab41e0ca1a0 Dopex: ETH Atlantic Puts Pool0 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago 0x82ad5948e47c6e18d0cd1a5a243a4f032420f3d4 Dopex: ETH Atlantic Puts Pool0 ETH
0xe624d998e17299286def1838f264f46a11117d3d09429d21695f7800db2eb002716233832023-03-20 2:08:40185 days 7 hrs ago 0x82ad5948e47c6e18d0cd1a5a243a4f032420f3d4 Dopex: ETH Atlantic Puts Pool0 ETH
0xf4ce12ced77fa4563362c313d3fa9672be8ee924d136f1851ddb31d75d359225716233152023-03-20 2:08:22185 days 7 hrs ago 0x0980a15ad64182b275b625c321beeab41e0ca1a0 Dopex: ETH Atlantic Puts Pool0 ETH
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
EthWeeklyAtlanticPutsV3

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, Unlicense license
File 1 of 17 : Counters.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)

pragma solidity ^0.8.0;

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library Counters {
    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0, "Counter: decrement overflow");
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}

File 2 of 17 : AtlanticPutsPool.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

// Libraries
import {SafeTransferLib} from "solady/src/utils/SafeTransferLib.sol";
import {OwnableRoles} from "solady/src/auth/OwnableRoles.sol";

// Libraries
import {StructuredLinkedList} from "solidity-linked-list/contracts/StructuredLinkedList.sol";
import {Counters} from "@openzeppelin/contracts/utils/Counters.sol";

// Contracts
import {ReentrancyGuard} from "../helpers/ReentrancyGuard.sol";
import {Pausable} from "../helpers/Pausable.sol";
import {AtlanticPutsPoolState} from "./AtlanticPutsPoolState.sol";

// Interfaces
import {IERC20} from "../interfaces/IERC20.sol";
import {IOptionPricing} from "../interfaces/IOptionPricing.sol";
import {IPriceOracle} from "../interfaces/IPriceOracle.sol";
import {IVolatilityOracle} from "../interfaces/IVolatilityOracle.sol";
import {IOptionPricing} from "../interfaces/IOptionPricing.sol";
import {IDopexFeeStrategy} from "../fees/interfaces/IDopexFeeStrategy.sol";

// Enums
import {OptionsState, EpochState, Contracts, VaultConfig} from "./AtlanticPutsPoolEnums.sol";

// Structs
import {EpochData, MaxStrikesRange, Checkpoint, OptionsPurchase, DepositPosition, EpochRewards, MaxStrike} from "./AtlanticPutsPoolStructs.sol";

contract AtlanticPutsPool is
    AtlanticPutsPoolState,
    Pausable,
    ReentrancyGuard,
    OwnableRoles
{
    using StructuredLinkedList for StructuredLinkedList.List;

    uint256 internal constant ADMIN_ROLE = _ROLE_0;
    uint256 internal constant MANAGED_CONTRACT_ROLE = _ROLE_1;
    uint256 internal constant BOOSTRAPPER_ROLE = _ROLE_2;
    uint256 internal constant WHITELISTED_CONTRACT_ROLE = _ROLE_3;

    /**
     * @notice Structured linked list for max strikes
     * @dev    epoch => strike list
     */
    mapping(uint256 => StructuredLinkedList.List) private epochStrikesList;

    /// @dev Number of deicmals of deposit/premium token
    uint256 private immutable COLLATERAL_TOKEN_DECIMALS;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CONSTRUCTOR                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    constructor(address collateralToken) {
        COLLATERAL_TOKEN_DECIMALS = IERC20(collateralToken).decimals();
        _setOwner(msg.sender);
        _grantRoles(msg.sender, ADMIN_ROLE);
        _grantRoles(msg.sender, MANAGED_CONTRACT_ROLE);
        _grantRoles(msg.sender, BOOSTRAPPER_ROLE);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        PUBLIC VIEWS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice                Get amount amount of underlying to
     *                        be unwinded against options.
     * @param  _optionStrike  Strike price of the option.
     * @param  _optionsAmount Amount of options to unwind.
     * @return unwindAmount
     */
    function getUnwindAmount(uint256 _optionStrike, uint256 _optionsAmount)
        public
        view
        returns (uint256 unwindAmount)
    {
        if (_optionStrike < getUsdPrice()) {
            unwindAmount = (_optionsAmount * _optionStrike) / getUsdPrice();
        } else {
            unwindAmount = _optionsAmount;
        }
    }

    /**
     * @notice       Calculate Pnl for exercising options.
     * @param price  price of BaseToken.
     * @param strike strike price of the option.
     * @param amount amount of options.
     */
    function calculatePnl(
        uint256 price,
        uint256 strike,
        uint256 amount
    ) public view returns (uint256) {
        if (price == 0) price = getUsdPrice();
        return strike > price ? (strikeMulAmount((strike - price), amount)) : 0;
    }

    /**
     * @notice                  Calculate funding fees based on days
     *                          left till expiry.
     * @param _collateralAccess Amount of collateral borrowed.
     * @param _entryTimestamp   Timestamp of entry of unlockCollatera().
     *                          which is used to calc. how much funding
     *                          is to be charged.
     * @return fees
     */
    function calculateFundingFees(
        uint256 _collateralAccess,
        uint256 _entryTimestamp
    ) public view returns (uint256 fees) {
        fees =
            ((_epochData[currentEpoch].expiryTime - _entryTimestamp) /
                vaultConfig[VaultConfig.FundingInterval]) *
            vaultConfig[VaultConfig.BaseFundingRate];

        fees =
            ((_collateralAccess * (FEE_BPS_PRECISION + fees)) /
                FEE_BPS_PRECISION) -
            _collateralAccess;
    }

    /**
     * @notice          Calculate Fees for purchase.
     * @param  strike   strike price of the BaseToken option.
     * @param  amount   amount of options being bought.
     * @return finalFee purchase fee in QuoteToken.
     */
    function calculatePurchaseFees(
        address account,
        uint256 strike,
        uint256 amount
    ) public view returns (uint256 finalFee) {
        uint256 feeBps = _getFeeBps(PURCHASE_FEES_KEY, account);

        finalFee =
            (((amount * (FEE_BPS_PRECISION + feeBps)) / FEE_BPS_PRECISION) -
                amount) /
            10**(OPTION_TOKEN_DECIMALS - COLLATERAL_TOKEN_DECIMALS);

        if (getUsdPrice() < strike) {
            uint256 feeMultiplier = (strike * (10**STRIKE_DECIMALS)) /
                getUsdPrice();
            finalFee = (finalFee * feeMultiplier) / 10**STRIKE_DECIMALS;
        }
    }

    /**
     * @notice         Calculate premium for an option.
     * @param  _strike Strike price of the option.
     * @param  _amount Amount of options.
     * @return premium in QuoteToken.
     */
    function calculatePremium(uint256 _strike, uint256 _amount)
        public
        view
        returns (uint256 premium)
    {
        uint256 currentPrice = getUsdPrice();
        premium = strikeMulAmount(
            IOptionPricing(addresses[Contracts.OptionPricing]).getOptionPrice(
                true,
                _epochData[currentEpoch].expiryTime,
                _strike,
                currentPrice,
                getVolatility(_strike)
            ),
            _amount
        );
    }

    /**
     * @notice       Returns the price of the BaseToken in USD.
     * @return price Price of the base token in 1e8 decimals.
     */
    function getUsdPrice() public view returns (uint256) {
        return
            IPriceOracle(addresses[Contracts.PriceOracle]).getPrice(
                addresses[Contracts.BaseToken],
                false,
                false,
                false
            ) / 10**(PRICE_ORACLE_DECIMALS - STRIKE_DECIMALS);
    }

    /**
     * @notice        Returns the volatility from the volatility oracle
     * @param _strike Strike of the option
     */
    function getVolatility(uint256 _strike) public view returns (uint256) {
        return
            (IVolatilityOracle(addresses[Contracts.VolatilityOracle])
                .getVolatility(_strike) *
                (FEE_BPS_PRECISION + vaultConfig[VaultConfig.IvBoost])) /
            FEE_BPS_PRECISION;
    }

    /**
     * @notice         Multiply strike and amount depending on strike
     *                 and options decimals.
     * @param  strike Option strike.
     * @param  amount Amount of options.
     * @return result  Product of strike and amount in collateral/quote
     *                 token decimals.
     */
    function strikeMulAmount(uint256 strike, uint256 amount)
        public
        view
        returns (uint256 result)
    {
        uint256 divisor = (STRIKE_DECIMALS + OPTION_TOKEN_DECIMALS) -
            COLLATERAL_TOKEN_DECIMALS;
        return ((strike * amount) / 10**divisor);
    }

    /**
     * @notice         A view fn to check if the current epoch of the
     *                 pool is within the exercise window or not.
     * @return whether Whether the current epoch is within exercise
     *                 window of options.
     */
    function isWithinBlackoutWindow() public view returns (bool) {
        uint256 expiry = _epochData[currentEpoch].expiryTime;
        if (expiry == 0) return false;
        return
            block.timestamp >=
            (expiry - vaultConfig[VaultConfig.BlackoutWindow]);
    }

    /**
     * @notice            A view fn to get the state of the options.
     *                    Although by default it returns the state of
     *                    the option but if the epoch of the options
     *                    are expired it will return the state as
     *                    settled.
     * @param _purchaseId ID of the options purchase.
     * @return state      State of the options.
     */
    function getOptionsState(uint256 _purchaseId)
        public
        view
        returns (OptionsState)
    {
        uint256 epoch = _optionsPositions[_purchaseId].epoch;
        if (block.timestamp >= _epochData[epoch].expiryTime) {
            return OptionsState.Settled;
        } else {
            return _optionsPositions[_purchaseId].state;
        }
    }

    /**
     * @param _depositId Epoch of atlantic pool to inquire
     * @return depositAmount Total deposits of user
     * @return premium       Total premiums earned
     * @return borrowFees    Total borrowFees fees earned
     * @return underlying    Total underlying earned on unwinds
     */
    function getWithdrawable(uint256 _depositId)
        public
        view
        returns (
            uint256 depositAmount,
            uint256 premium,
            uint256 borrowFees,
            uint256 underlying,
            uint256[] memory rewards
        )
    {
        DepositPosition memory userDeposit = _depositPositions[_depositId];
        rewards = new uint256[](
            epochMaxStrikeCheckpoints[userDeposit.epoch][userDeposit.strike]
                .rewardRates
                .length
        );

        rewards = epochMaxStrikeCheckpoints[userDeposit.epoch][
            userDeposit.strike
        ].rewardRates;

        Checkpoint memory checkpoint = epochMaxStrikeCheckpoints[
            userDeposit.epoch
        ][userDeposit.strike].checkpoints[userDeposit.checkpoint];

        for (uint256 i; i < rewards.length; ) {
            rewards[i] =
                (((userDeposit.liquidity * checkpoint.activeCollateral) /
                    checkpoint.totalLiquidity) * rewards[i]) /
                10**COLLATERAL_TOKEN_DECIMALS;

            unchecked {
                ++i;
            }
        }

        borrowFees +=
            (userDeposit.liquidity * checkpoint.borrowFeesAccrued) /
            checkpoint.totalLiquidity;

        premium +=
            (userDeposit.liquidity * checkpoint.premiumAccrued) /
            checkpoint.totalLiquidity;

        underlying +=
            (userDeposit.liquidity * checkpoint.underlyingAccrued) /
            checkpoint.totalLiquidity;

        depositAmount +=
            (userDeposit.liquidity * checkpoint.liquidityBalance) /
            checkpoint.totalLiquidity;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      EXTERNAL METHODS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice            Rollover deposits from a previous epoch to current epoch.
     * @param _depositIds IDs of the deposit positinos.
     */
    function rolloverDeposits(
        uint256[] calldata _depositIds,
        address _feeReceiver
    ) external {
        DepositPosition memory _depositPosition;
        uint256[] memory rewards;
        uint256 premium;
        uint256 depositBalance;
        uint256 borrowFees;
        uint256 underlying;

        for (uint256 i; i < _depositIds.length; ) {
            _depositPosition = _depositPositions[_depositIds[i]];
            if (_depositPosition.rollover) {
                _validate(
                    _epochData[_depositPosition.epoch].state ==
                        EpochState.Expired,
                    4
                );

                _validate(_depositPosition.epoch < currentEpoch, 21);

                (
                    depositBalance,
                    premium,
                    borrowFees,
                    underlying,
                    rewards
                ) = getWithdrawable(_depositIds[i]);

                delete _depositPositions[_depositIds[i]];

                uint256 depositFee = ((depositBalance *
                    (FEE_BPS_PRECISION +
                        _getFeeBps(
                            ROLLOVER_FEES_KEY,
                            _depositPosition.depositor
                        ))) / FEE_BPS_PRECISION) - depositBalance;

                if (depositFee > vaultConfig[VaultConfig.MaxDepositFee]) {
                    depositFee = vaultConfig[VaultConfig.MaxDepositFee];
                }

                _deposit(
                    currentEpoch,
                    _depositPosition.strike,
                    depositBalance - depositFee,
                    _depositPosition.depositor,
                    _depositPosition.rollover
                );

                if (underlying != 0) {
                    _safeTransfer(
                        addresses[Contracts.BaseToken],
                        _depositPosition.depositor,
                        underlying
                    );
                }

                if (premium + borrowFees != 0) {
                    _safeTransfer(
                        addresses[Contracts.QuoteToken],
                        _depositPosition.depositor,
                        premium + borrowFees
                    );
                }

                for (uint256 j; j < rewards.length; ) {
                    if (rewards[j] != 0) {
                        _safeTransfer(
                            _epochRewards[_depositPosition.epoch].rewardTokens[
                                j
                            ],
                            _depositPosition.depositor,
                            rewards[j]
                        );
                    }
                    unchecked {
                        ++j;
                    }
                }

                _safeTransfer(
                    addresses[Contracts.QuoteToken],
                    _feeReceiver,
                    depositFee
                );

                emit Withdraw(
                    _depositIds[i],
                    _depositPosition.depositor,
                    0,
                    borrowFees,
                    premium,
                    underlying,
                    rewards
                );
            }
            unchecked {
                ++i;
            }
        }
    }

    function setDepositRollover(uint256 _depositId, bool _setAs) external {
        _validate(_depositPositions[_depositId].depositor == msg.sender, 16);
        _depositPositions[_depositId].rollover = _setAs;
        emit rolloverDepositSet(_depositId, _setAs);
    }

    /**
     * @notice             Gracefully exercises an atlantic
     *                     sends collateral to integrated protocol,
     *                     underlying to writers.
     * @param unwindAmount Amount charged from caller (unwind amount + fees).
     * @param purchaseId   Options purchase id.
     */
    function unwind(uint256 purchaseId, uint256 unwindAmount)
        external
        onlyRoles(MANAGED_CONTRACT_ROLE)
    {
        _whenNotPaused();

        _validate(_isVaultBootstrapped(currentEpoch), 7);

        OptionsPurchase memory _userOptionsPurchase = _optionsPositions[
            purchaseId
        ];

        _validate(_userOptionsPurchase.delegate == msg.sender, 9);
        _validate(_userOptionsPurchase.state == OptionsState.Unlocked, 10);

        uint256 expectedUnwindAmount = getUnwindAmount(
            _userOptionsPurchase.optionStrike,
            _userOptionsPurchase.optionsAmount
        );

        uint256 collateralAccess = strikeMulAmount(
            _userOptionsPurchase.optionStrike,
            _userOptionsPurchase.optionsAmount
        );

        for (uint256 i; i < _userOptionsPurchase.strikes.length; ) {
            _unwind(
                _userOptionsPurchase.epoch,
                _userOptionsPurchase.strikes[i],
                ((
                    unwindAmount > expectedUnwindAmount
                        ? expectedUnwindAmount
                        : unwindAmount
                ) * _userOptionsPurchase.weights[i]) / WEIGHTS_MUL_DIV,
                (collateralAccess * _userOptionsPurchase.weights[i]) /
                    WEIGHTS_MUL_DIV,
                _userOptionsPurchase.checkpoints[i]
            );

            unchecked {
                ++i;
            }
        }

        // Transfer excess to user.
        if (unwindAmount > expectedUnwindAmount) {
            _safeTransfer(
                addresses[Contracts.BaseToken],
                _userOptionsPurchase.user,
                unwindAmount - expectedUnwindAmount
            );
        }

        _safeTransferFrom(
            addresses[Contracts.BaseToken],
            msg.sender,
            address(this),
            unwindAmount
        );

        delete _optionsPositions[purchaseId];
    }

    /**
     * @notice             Callable by managed contracts that wish
     *                     to relock collateral that was unlocked previously.
     * @param relockAmount Amount of collateral to relock.
     * @param purchaseId   User options purchase id.
     */
    function relockCollateral(uint256 purchaseId, uint256 relockAmount)
        external
        onlyRoles(MANAGED_CONTRACT_ROLE)
    {
        _whenNotPaused();

        _validate(_isVaultBootstrapped(currentEpoch), 7);

        OptionsPurchase memory _userOptionsPurchase = _optionsPositions[
            purchaseId
        ];

        _validate(_userOptionsPurchase.delegate == msg.sender, 9);
        _validate(_userOptionsPurchase.state == OptionsState.Unlocked, 13);

        uint256 collateralAccess = strikeMulAmount(
            _userOptionsPurchase.optionStrike,
            _userOptionsPurchase.optionsAmount
        );

        uint256 fundingRefund = calculateFundingFees(
            collateralAccess,
            _userOptionsPurchase.unlockEntryTimestamp
        );

        /// @dev refund = funding charged previsouly - funding charged for borrowing
        fundingRefund =
            fundingRefund -
            (fundingRefund -
                calculateFundingFees(collateralAccess, block.timestamp));

        if (collateralAccess > relockAmount) {
            /**
             * Settle the option if fail to relock atleast collateral amount
             * to disallow reuse of options.
             * */
            _optionsPositions[purchaseId].state = OptionsState.Settled;
            delete fundingRefund;
        } else {
            _optionsPositions[purchaseId].state = OptionsState.Active;
        }

        for (uint256 i; i < _userOptionsPurchase.strikes.length; ) {
            _relockCollateral(
                _userOptionsPurchase.epoch,
                _userOptionsPurchase.strikes[i],
                (((
                    relockAmount > collateralAccess
                        ? collateralAccess
                        : relockAmount
                ) * _userOptionsPurchase.weights[i]) / WEIGHTS_MUL_DIV),
                ((fundingRefund * _userOptionsPurchase.weights[i]) /
                    WEIGHTS_MUL_DIV),
                _userOptionsPurchase.checkpoints[i]
            );

            unchecked {
                ++i;
            }
        }

        // Transfer to user any excess.
        if (collateralAccess < relockAmount) {
            _safeTransfer(
                addresses[Contracts.QuoteToken],
                _userOptionsPurchase.user,
                relockAmount - collateralAccess
            );
        }

        _safeTransferFrom(
            addresses[Contracts.QuoteToken],
            msg.sender,
            address(this),
            relockAmount
        );

        if (fundingRefund != 0) {
            _safeTransfer(
                addresses[Contracts.QuoteToken],
                _userOptionsPurchase.user,
                fundingRefund
            );
        }
    }

    /**
     * @notice                    Unlock collateral to borrow against AP option.
     *                            Only Callable by managed contracts.
     * @param  purchaseId         User options purchase ID
     * @param  to                 Collateral to transfer to
     * @return unlockedCollateral Amount of collateral unlocked plus fees
     */
    function unlockCollateral(uint256 purchaseId, address to)
        external
        nonReentrant
        onlyRoles(MANAGED_CONTRACT_ROLE)
        returns (uint256 unlockedCollateral)
    {
        _whenNotPaused();

        _validate(_isVaultBootstrapped(currentEpoch), 7);

        OptionsPurchase memory _userOptionsPurchase = _optionsPositions[
            purchaseId
        ];

        unlockedCollateral = strikeMulAmount(
            _userOptionsPurchase.optionStrike,
            _userOptionsPurchase.optionsAmount
        );

        _validate(_userOptionsPurchase.delegate == msg.sender, 9);
        // Cannot unlock collateral after expiry
        _validate(getOptionsState(purchaseId) == OptionsState.Active, 10);

        _userOptionsPurchase.state = OptionsState.Unlocked;
        _userOptionsPurchase.unlockEntryTimestamp = block.timestamp;

        uint256 borrowFees = calculateFundingFees(
            unlockedCollateral,
            block.timestamp
        );

        for (uint256 i; i < _userOptionsPurchase.strikes.length; ) {
            _unlockCollateral(
                _userOptionsPurchase.epoch,
                _userOptionsPurchase.strikes[i],
                (_userOptionsPurchase.weights[i] * unlockedCollateral) /
                    WEIGHTS_MUL_DIV,
                (_userOptionsPurchase.weights[i] * borrowFees) /
                    WEIGHTS_MUL_DIV,
                _userOptionsPurchase.checkpoints[i]
            );

            unchecked {
                ++i;
            }
        }

        _optionsPositions[purchaseId] = _userOptionsPurchase;

        /// @dev Transfer out collateral
        _safeTransfer(addresses[Contracts.QuoteToken], to, unlockedCollateral);

        _safeTransferFrom(
            addresses[Contracts.QuoteToken],
            msg.sender,
            address(this),
            borrowFees
        );
    }

    /**
     * @notice           Purchases puts for the current epoch
     * @param _strike    Strike index for current epoch
     * @param _amount    Amount of puts to purchase
     * @param _account   Address of the user options were purchased
     *                   on behalf of.
     * @param _delegate  Address of the delegate who will be in charge
     *                   of the options.
     * @return purchaseId
     */
    function purchase(
        uint256 _strike,
        uint256 _amount,
        address _delegate,
        address _account
    )
        external
        nonReentrant
        onlyRoles(MANAGED_CONTRACT_ROLE)
        returns (uint256 purchaseId)
    {
        _whenNotPaused();
        _validate(!isWithinBlackoutWindow(), 20);

        uint256 epoch = currentEpoch;

        _validate(_isVaultBootstrapped(epoch), 7);
        _validate(_account != address(0), 1);
        _validateParams(_strike, _amount, epoch, _delegate);

        // Calculate liquidity required
        uint256 collateralRequired = strikeMulAmount(_strike, _amount);

        // Should have adequate cumulative liquidity
        _validate(_epochData[epoch].totalLiquidity >= collateralRequired, 11);

        // Price/premium of option
        uint256 premium = calculatePremium(_strike, _amount);

        // Fees on top of premium for fee distributor
        uint256 fees = calculatePurchaseFees(_account, _strike, _amount);

        purchaseId = _newPurchasePosition(
            _account,
            _delegate,
            _strike,
            _amount,
            epoch
        );

        _squeezeMaxStrikes(
            epoch,
            _strike,
            collateralRequired,
            premium,
            purchaseId
        );

        _epochData[epoch].totalLiquidity -= collateralRequired;
        _epochData[epoch].totalActiveCollateral += collateralRequired;

        _safeTransferFrom(
            addresses[Contracts.QuoteToken],
            msg.sender,
            address(this),
            premium
        );

        _safeTransferFrom(
            addresses[Contracts.QuoteToken],
            msg.sender,
            addresses[Contracts.FeeDistributor],
            fees
        );

        emit NewPurchase(
            epoch,
            purchaseId,
            premium,
            fees,
            _account,
            msg.sender
        );
    }

    /**
     * @notice           Deposit liquidity into a max strike for
     *                   current epoch for selected strikes.
     * @param _maxStrike Exact price of strike in 1e8 decimals.
     * @param _liquidity Amount of liquidity to provide in quote token decimals/
     * @param _user      Address of the user to deposit for.
     */
    function deposit(
        uint256 _maxStrike,
        uint256 _liquidity,
        address _user,
        bool _rollover
    ) public nonReentrant returns (uint256 depositId) {
        depositId = _deposit(
            currentEpoch,
            _maxStrike,
            _liquidity,
            _user,
            _rollover
        );

        _safeTransferFrom(
            addresses[Contracts.QuoteToken],
            msg.sender,
            address(this),
            _liquidity
        );
    }

    /**
     * @notice           Withdraws balances for a strike from epoch
     *                   deposted in a epoch.
     * @param depositIds Deposit Ids of the deposit positions.
     * @param receiver   Address of the receiver of tokens.
     */
    function withdraw(uint256[] calldata depositIds, address receiver)
        external
        nonReentrant
    {
        _whenNotPaused();

        uint256 epoch;
        uint256[] memory rewards;
        uint256 premium;
        uint256 depositBalance;
        uint256 borrowFees;
        uint256 underlying;

        for (uint256 i; i < depositIds.length; ) {
            epoch = _depositPositions[depositIds[i]].epoch;

            _validate(
                _depositPositions[depositIds[i]].depositor == msg.sender,
                16
            );

            _validate(_epochData[epoch].state == EpochState.Expired, 4);

            (
                depositBalance,
                premium,
                borrowFees,
                underlying,
                rewards
            ) = getWithdrawable(depositIds[i]);

            delete _depositPositions[depositIds[i]];

            if (underlying != 0) {
                _safeTransfer(
                    addresses[Contracts.BaseToken],
                    receiver,
                    underlying
                );
            }

            if (premium + depositBalance + borrowFees != 0) {
                _safeTransfer(
                    addresses[Contracts.QuoteToken],
                    receiver,
                    premium + depositBalance + borrowFees
                );
            }

            for (uint256 j; j < rewards.length; ) {
                if (rewards[j] != 0) {
                    _safeTransfer(
                        _epochRewards[epoch].rewardTokens[j],
                        receiver,
                        rewards[j]
                    );
                }
                unchecked {
                    ++j;
                }
            }

            emit Withdraw(
                depositIds[i],
                receiver,
                depositBalance,
                borrowFees,
                premium,
                underlying,
                rewards
            );

            unchecked {
                ++i;
            }
        }
    }

    /// @notice Sets the current epoch as expired.
    function expireEpoch() external nonReentrant {
        uint256 epoch = currentEpoch;
        _validate(_epochData[epoch].state != EpochState.Expired, 17);
        uint256 epochExpiry = _epochData[epoch].expiryTime;
        _validate((block.timestamp >= epochExpiry), 18);
        _allocateRewardsForStrikes(epoch);
        _epochData[epoch].state = EpochState.Expired;
        emit EpochExpired(msg.sender);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       EXTERNAL VIEWS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice                 Get OptionsPurchase instance for a given positionId.
     * @param  _positionId     ID of the options purchase.
     * @return OptionsPurchase Options purchase data.
     */
    function getOptionsPurchase(uint256 _positionId)
        external
        view
        returns (OptionsPurchase memory)
    {
        return _optionsPositions[_positionId];
    }

    /**
     * @notice                 Get OptionsPurchase instance for a given positionId.
     * @param  _positionId     ID of the options purchase.
     * @return DepositPosition Deposit position data.
     */
    function getDepositPosition(uint256 _positionId)
        external
        view
        returns (DepositPosition memory)
    {
        return _depositPositions[_positionId];
    }

    /**
     * @notice              Get checkpoints of a maxstrike in a epoch.
     * @param  _epoch       Epoch of the pool.
     * @param  _maxStrike   Max strike to query for.
     * @return _checkpoints array of checkpoints of a max strike.
     */
    function getEpochCheckpoints(uint256 _epoch, uint256 _maxStrike)
        external
        view
        returns (Checkpoint[] memory _checkpoints)
    {
        _checkpoints = new Checkpoint[](
            epochMaxStrikeCheckpointsLength[_epoch][_maxStrike]
        );

        for (
            uint256 i;
            i < epochMaxStrikeCheckpointsLength[_epoch][_maxStrike];

        ) {
            _checkpoints[i] = epochMaxStrikeCheckpoints[_epoch][_maxStrike]
                .checkpoints[i];
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice            Fetches all max strikes written in a epoch.
     * @param  epoch      Epoch of the pool.
     * @return maxStrikes
     */
    function getEpochStrikes(uint256 epoch)
        external
        view
        returns (uint256[] memory maxStrikes)
    {
        maxStrikes = new uint256[](epochStrikesList[epoch].sizeOf());

        uint256 nextNode = _epochData[epoch].maxStrikesRange.highest;
        uint256 iterator;
        while (nextNode != 0) {
            maxStrikes[iterator] = nextNode;
            iterator++;
            (, nextNode) = epochStrikesList[epoch].getNextNode(nextNode);
        }
    }

    /**
     * @notice Fetch the tick size set for the onGoing epoch.
     * @return tickSize
     */
    function getEpochTickSize(uint256 _epoch) external view returns (uint256) {
        return _epochData[_epoch].tickSize;
    }

    /**
     * @notice Fetch epoch data of an epoch.
     * @return DataOfTheEpoch.
     */
    function getEpochData(uint256 _epoch)
        external
        view
        returns (EpochData memory)
    {
        return _epochData[_epoch];
    }

    /**
     * @notice Fetch rewards set for an epoch.
     * @return RewardsAllocated.
     */
    function getEpochRewards(uint256 _epoch)
        external
        view
        returns (EpochRewards memory)
    {
        return _epochRewards[_epoch];
    }

    /**
     * @notice           Get MaxStrike type data.
     * @param _epoch     Epoch of the pool.
     * @param _maxStrike Max strike to query for.
     */
    function getEpochMaxStrikeData(uint256 _epoch, uint256 _maxStrike)
        external
        view
        returns (uint256 activeCollateral, uint256[] memory rewardRates)
    {
        activeCollateral = epochMaxStrikeCheckpoints[_epoch][_maxStrike]
            .activeCollateral;
        rewardRates = new uint256[](
            epochMaxStrikeCheckpoints[_epoch][_maxStrike].rewardRates.length
        );
        rewardRates = epochMaxStrikeCheckpoints[_epoch][_maxStrike].rewardRates;
    }

    /**
     * @notice Fetch checkpoint data of a max strike.
     * @return Checkpoint data.
     */
    function getEpochMaxStrikeCheckpoint(
        uint256 _epoch,
        uint256 _maxStrike,
        uint256 _checkpoint
    ) external view returns (Checkpoint memory) {
        return
            epochMaxStrikeCheckpoints[_epoch][_maxStrike].checkpoints[
                _checkpoint
            ];
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                 INTERNAL / PRIVATE METHODS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice           Creates deposition position and updates checkpoint.
     * @param _epoch     Epoch to deposit for.
     * @param _maxStrike Max strike to deposit into.
     * @param _liquidity Amount of liquidity provided.
     * @param _user      Address of the depositor.
     * @param _rollover  Whether to rollover the deposit.
     * @return depositId ID of the deposit position.
     */
    function _deposit(
        uint256 _epoch,
        uint256 _maxStrike,
        uint256 _liquidity,
        address _user,
        bool _rollover
    ) private returns (uint256 depositId) {
        _isEligibleSender();
        _whenNotPaused();
        _validate(_isVaultBootstrapped(_epoch), 7);
        _validateParams(_maxStrike, _liquidity, _epoch, _user);

        uint256 checkpoint = _updateCheckpoint(_epoch, _maxStrike, _liquidity);

        depositId = _newDepositPosition(
            _epoch,
            _liquidity,
            _maxStrike,
            checkpoint,
            _user,
            _rollover
        );

        _epochData[_epoch].totalLiquidity += _liquidity;

        // Emit event
        emit NewDeposit(
            _epoch,
            depositId,
            _maxStrike,
            _liquidity,
            _user,
            msg.sender
        );
    }

    /**
     * @notice        Add max strike to strikesList (linked list).
     * @param _strike Strike to add to strikesList.
     * @param _epoch  Epoch of the pool.
     */
    function _addMaxStrike(uint256 _strike, uint256 _epoch) internal {
        uint256 highestMaxStrike = _epochData[_epoch].maxStrikesRange.highest;
        uint256 lowestMaxStrike = _epochData[_epoch].maxStrikesRange.lowest;

        if (_strike > highestMaxStrike) {
            _epochData[_epoch].maxStrikesRange.highest = _strike;
        }
        if (_strike < lowestMaxStrike || lowestMaxStrike == 0) {
            _epochData[_epoch].maxStrikesRange.lowest = _strike;
        }

        // Add new max strike after the next largest strike
        uint256 strikeToInsertAfter = _getSortedSpot(_strike, _epoch);

        if (strikeToInsertAfter == 0)
            epochStrikesList[_epoch].pushBack(_strike);
        else
            epochStrikesList[_epoch].insertBefore(strikeToInsertAfter, _strike);
    }

    /**
     * @notice                 Helper function for unlockCollateral().
     * @param epoch            epoch of the vault.
     * @param maxStrike        Max strike to unlock collateral from.
     * @param collateralAmount Amount of collateral to unlock.
     * @param checkpoint      Checkpoint of the max strike.
     */
    function _unlockCollateral(
        uint256 epoch,
        uint256 maxStrike,
        uint256 collateralAmount,
        uint256 borrowFees,
        uint256 checkpoint
    ) internal {
        epochMaxStrikeCheckpoints[epoch][maxStrike]
            .checkpoints[checkpoint]
            .unlockedCollateral += collateralAmount;

        epochMaxStrikeCheckpoints[epoch][maxStrike]
            .checkpoints[checkpoint]
            .borrowFeesAccrued += borrowFees;

        epochMaxStrikeCheckpoints[epoch][maxStrike]
            .checkpoints[checkpoint]
            .liquidityBalance -= collateralAmount;
        emit UnlockCollateral(epoch, collateralAmount, msg.sender);
    }

    /**
     * @notice                  Update checkpoint states and total unlocked
     *                          collateral for a max strike.
     * @param epoch            Epoch of the pool.
     * @param maxStrike        maxStrike to update states for.
     * @param collateralAmount Collateral token amount relocked.
     * @param borrowFeesRefund Borrow fees to be refunded.
     * @param checkpoint       Checkpoint pointer to update.
     *
     */
    function _relockCollateral(
        uint256 epoch,
        uint256 maxStrike,
        uint256 collateralAmount,
        uint256 borrowFeesRefund,
        uint256 checkpoint
    ) internal {
        epochMaxStrikeCheckpoints[epoch][maxStrike]
            .checkpoints[checkpoint]
            .liquidityBalance += collateralAmount;

        epochMaxStrikeCheckpoints[epoch][maxStrike]
            .checkpoints[checkpoint]
            .unlockedCollateral -= collateralAmount;

        epochMaxStrikeCheckpoints[epoch][maxStrike]
            .checkpoints[checkpoint]
            .borrowFeesAccrued -= borrowFeesRefund;
        emit RelockCollateral(epoch, maxStrike, collateralAmount, msg.sender);
    }

    /**
     *
     * @notice                  Update unwind related states for corr-
     *                          esponding max strikes.
     * @param epoch            Epoch of the options.
     * @param maxStrike        Max strike to update.
     * @param underlyingAmount Amount of underlying to unwind.
     * @param collateralAmount Equivalent collateral amount com-
     *                          pared to options unwinded.
     * @param checkpoint       Checkpoint to update.
     *
     */
    function _unwind(
        uint256 epoch,
        uint256 maxStrike,
        uint256 underlyingAmount,
        uint256 collateralAmount,
        uint256 checkpoint
    ) internal {
        epochMaxStrikeCheckpoints[epoch][maxStrike]
            .checkpoints[checkpoint]
            .underlyingAccrued += underlyingAmount;
        epochMaxStrikeCheckpoints[epoch][maxStrike]
            .checkpoints[checkpoint]
            .unlockedCollateral -= collateralAmount;
        emit Unwind(epoch, maxStrike, underlyingAmount, msg.sender);
    }

    /**
     * @notice           Creates a new checkpoint or update existing
     *                   checkpoint.
     * @param  epoch     Epoch of the pool.
     * @param  maxStrike Max strike deposited into.
     * @param  liquidity Amount of deposits / liquidity to add
     *                   to totalLiquidity, totalLiquidityBalance.
     * @return index     Returns the checkpoint number.
     */
    function _updateCheckpoint(
        uint256 epoch,
        uint256 maxStrike,
        uint256 liquidity
    ) internal returns (uint256 index) {
        index = epochMaxStrikeCheckpointsLength[epoch][maxStrike];

        // Add `maxStrike` if it doesn't exist
        if (epochMaxStrikeCheckpoints[epoch][maxStrike].maxStrike == 0) {
            _addMaxStrike(maxStrike, epoch);
            epochMaxStrikeCheckpoints[epoch][maxStrike].maxStrike = maxStrike;
        }

        if (index == 0) {
            epochMaxStrikeCheckpoints[epoch][maxStrike].checkpoints[index] = (
                Checkpoint(block.timestamp, 0, 0, 0, 0, liquidity, liquidity, 0)
            );
            unchecked {
                ++epochMaxStrikeCheckpointsLength[epoch][maxStrike];
            }
        } else {
            Checkpoint memory currentCheckpoint = epochMaxStrikeCheckpoints[
                epoch
            ][maxStrike].checkpoints[index - 1];

            /**
             * @dev Check if checkpoint interval was exceeded
             *      compared to previous checkpoint start time
             *      if yes then create a new checkpoint or
             *      else accumulate to previous checkpoint.
             */

            /** @dev If a checkpoint's options have active collateral,
             *       add liquidity to next checkpoint.
             */
            if (currentCheckpoint.activeCollateral != 0) {
                epochMaxStrikeCheckpoints[epoch][maxStrike]
                    .checkpoints[index]
                    .startTime = block.timestamp;
                epochMaxStrikeCheckpoints[epoch][maxStrike]
                    .checkpoints[index]
                    .totalLiquidity += liquidity;
                epochMaxStrikeCheckpoints[epoch][maxStrike]
                    .checkpoints[index]
                    .liquidityBalance += liquidity;
                epochMaxStrikeCheckpointsLength[epoch][maxStrike]++;
            } else {
                --index;
                currentCheckpoint.totalLiquidity += liquidity;
                currentCheckpoint.liquidityBalance += liquidity;

                epochMaxStrikeCheckpoints[epoch][maxStrike].checkpoints[
                        index
                    ] = currentCheckpoint;
            }
        }
    }

    /**
     * @notice            Create a deposit position instance and update ID counter.
     * @param _epoch      Epoch of the pool.
     * @param _liquidity  Amount of collateral token deposited.
     * @param _maxStrike     Max strike deposited into.
     * @param _checkpoint Checkpoint of the max strike deposited into.
     * @param _user       Address of the user to deposit for / is depositing.
     */
    function _newDepositPosition(
        uint256 _epoch,
        uint256 _liquidity,
        uint256 _maxStrike,
        uint256 _checkpoint,
        address _user,
        bool _rollover
    ) internal returns (uint256 depositId) {
        depositId = depositPositionsCounter;

        ++depositPositionsCounter;

        _depositPositions[depositId].epoch = _epoch;
        _depositPositions[depositId].strike = _maxStrike;
        _depositPositions[depositId].liquidity = _liquidity;
        _depositPositions[depositId].checkpoint = _checkpoint;
        _depositPositions[depositId].depositor = _user;
        _depositPositions[depositId].rollover = _rollover;
    }

    function _safeTransfer(
        address _token,
        address _to,
        uint256 _amount
    ) internal {
        SafeTransferLib.safeTransfer(_token, _to, _amount);
    }

    function _safeTransferFrom(
        address _token,
        address _from,
        address _to,
        uint256 _amount
    ) internal {
        SafeTransferLib.safeTransferFrom(_token, _from, _to, _amount);
    }

    /**
     * @notice             Create new purchase position positon.
     * @param  _user       Address of the user to create for.
     * @param  _delegate   Address of the delagate who will be
     *                     in charge of the options.
     * @param  _strike     Strike price of the option.
     * @param  _amount     Amount of options.
     * @param  _epoch      Epoch of the pool.
     * @return purchaseId  TokenID and positionID of the purchase position.
     */
    function _newPurchasePosition(
        address _user,
        address _delegate,
        uint256 _strike,
        uint256 _amount,
        uint256 _epoch
    ) internal returns (uint256 purchaseId) {
        purchaseId = purchasePositionsCounter;

        _optionsPositions[purchaseId].user = _user;
        _optionsPositions[purchaseId].delegate = _delegate;
        _optionsPositions[purchaseId].optionStrike = _strike;
        _optionsPositions[purchaseId].optionsAmount = _amount;
        _optionsPositions[purchaseId].epoch = _epoch;
        _optionsPositions[purchaseId].state = OptionsState.Active;

        unchecked {
            ++purchasePositionsCounter;
        }
    }

    /**
     * @notice                    Loop through max strike allocating
     *                            for liquidity for options.
     * @param  epoch              Epoch of the pool.
     * @param  putStrike          Strike to purchase.
     * @param  collateralRequired Amount of collateral to squeeze from
     *                            max strike.
     * @param  premium            Amount of premium to distribute.
     */
    function _squeezeMaxStrikes(
        uint256 epoch,
        uint256 putStrike,
        uint256 collateralRequired,
        uint256 premium,
        uint256 purchaseId
    ) internal {
        uint256 liquidityFromMaxStrikes;
        uint256 liquidityProvided;
        uint256 nextStrike = _epochData[epoch].maxStrikesRange.highest;
        uint256 _liquidityRequired;

        while (liquidityFromMaxStrikes != collateralRequired) {
            _liquidityRequired = collateralRequired - liquidityFromMaxStrikes;

            _validate(putStrike <= nextStrike, 12);

            liquidityProvided = _squeezeMaxStrikeCheckpoints(
                epoch,
                nextStrike,
                collateralRequired,
                _liquidityRequired,
                premium,
                purchaseId
            );

            epochMaxStrikeCheckpoints[epoch][nextStrike]
                .activeCollateral += liquidityProvided;

            liquidityFromMaxStrikes += liquidityProvided;

            (, nextStrike) = epochStrikesList[epoch].getNextNode(nextStrike);
        }
    }

    /**
     * @notice                         Squeezes out liquidity from checkpoints within
     *                                 each max strike/
     * @param epoch                    Epoch of the pool
     * @param maxStrike                Max strike to squeeze liquidity from
     * @param totalCollateralRequired  Total amount of liquidity required for the option
     *                                 purchase/
     * @param collateralRequired       As the loop _squeezeMaxStrikes() accumulates
     *                                 liquidity, this value deducts liquidity is
     *                                 accumulated.
     *                                 collateralRequired = totalCollateralRequired - liquidity
     *                                 accumulated till the max strike in the context of the loop
     * @param premium                  Premium to distribute among the checkpoints and maxstrike
     * @param purchaseId               Options purchase ID
     */
    function _squeezeMaxStrikeCheckpoints(
        uint256 epoch,
        uint256 maxStrike,
        uint256 totalCollateralRequired,
        uint256 collateralRequired,
        uint256 premium,
        uint256 purchaseId
    ) internal returns (uint256 liquidityProvided) {
        uint256 startIndex = epochMaxStrikeCheckpointStartIndex[epoch][
            maxStrike
        ];
        //check if previous checkpoint liquidity all consumed
        if (
            startIndex != 0 &&
            epochMaxStrikeCheckpoints[epoch][maxStrike]
                .checkpoints[startIndex - 1]
                .totalLiquidity >
            epochMaxStrikeCheckpoints[epoch][maxStrike]
                .checkpoints[startIndex - 1]
                .activeCollateral
        ) {
            --startIndex;
        }
        uint256 endIndex;
        // Unchecked since only max strikes with checkpoints != 0 will come to this point
        endIndex = epochMaxStrikeCheckpointsLength[epoch][maxStrike] - 1;
        uint256 liquidityProvidedFromCurrentMaxStrike;

        while (
            startIndex <= endIndex && liquidityProvided != collateralRequired
        ) {
            uint256 availableLiquidity = epochMaxStrikeCheckpoints[epoch][
                maxStrike
            ].checkpoints[startIndex].totalLiquidity -
                epochMaxStrikeCheckpoints[epoch][maxStrike]
                    .checkpoints[startIndex]
                    .activeCollateral;

            uint256 _requiredLiquidity = collateralRequired - liquidityProvided;

            /// @dev if checkpoint has more than required liquidity
            if (availableLiquidity >= _requiredLiquidity) {
                /// @dev Liquidity provided from current max strike at current index
                unchecked {
                    liquidityProvidedFromCurrentMaxStrike = _requiredLiquidity;
                    liquidityProvided += liquidityProvidedFromCurrentMaxStrike;

                    /// @dev Add to active collateral, later if activeCollateral == totalliquidity, then we stop
                    //  coming back to this checkpoint
                    epochMaxStrikeCheckpoints[epoch][maxStrike]
                        .checkpoints[startIndex]
                        .activeCollateral += _requiredLiquidity;

                    /// @dev Add to premium accured
                    epochMaxStrikeCheckpoints[epoch][maxStrike]
                        .checkpoints[startIndex]
                        .premiumAccrued +=
                        (liquidityProvidedFromCurrentMaxStrike * premium) /
                        totalCollateralRequired;
                }

                _updatePurchasePositionMaxStrikesLiquidity(
                    purchaseId,
                    maxStrike,
                    startIndex,
                    (liquidityProvidedFromCurrentMaxStrike * WEIGHTS_MUL_DIV) /
                        totalCollateralRequired
                );
            } else if (availableLiquidity != 0) {
                /// @dev if checkpoint has less than required liquidity
                liquidityProvidedFromCurrentMaxStrike = availableLiquidity;
                unchecked {
                    liquidityProvided += liquidityProvidedFromCurrentMaxStrike;

                    epochMaxStrikeCheckpoints[epoch][maxStrike]
                        .checkpoints[startIndex]
                        .activeCollateral += liquidityProvidedFromCurrentMaxStrike;

                    /// @dev Add to premium accured
                    epochMaxStrikeCheckpoints[epoch][maxStrike]
                        .checkpoints[startIndex]
                        .premiumAccrued +=
                        (liquidityProvidedFromCurrentMaxStrike * premium) /
                        totalCollateralRequired;
                }

                _updatePurchasePositionMaxStrikesLiquidity(
                    purchaseId,
                    maxStrike,
                    startIndex,
                    (liquidityProvidedFromCurrentMaxStrike * WEIGHTS_MUL_DIV) /
                        totalCollateralRequired
                );

                unchecked {
                    ++epochMaxStrikeCheckpointStartIndex[epoch][maxStrike];
                }
            }
            unchecked {
                ++startIndex;
            }
        }
    }

    /**
     * @notice      Allocate rewards for strikes based
     *              on active collateral present.
     * @param epoch Epoch of the pool
     */
    function _allocateRewardsForStrikes(uint256 epoch) internal {
        uint256 nextNode = _epochData[epoch].maxStrikesRange.highest;
        uint256 iterator;

        EpochRewards memory epochRewards = _epochRewards[epoch];
        uint256 activeCollateral;
        uint256 totalEpochActiveCollateral = _epochData[epoch]
            .totalActiveCollateral;
        while (nextNode != 0) {
            activeCollateral = epochMaxStrikeCheckpoints[epoch][nextNode]
                .activeCollateral;

            for (uint256 i; i < epochRewards.rewardTokens.length; ) {
                /**
                 * rewards allocated for a strike:
                 *               strike's active collateral
                 *    rewards *  --------------------------
                 *               total active collateral
                 *
                 * Reward rate per active collateral:
                 *
                 *      rewards allocated
                 *      ------------------
                 *   strike's active collateral
                 */

                if (activeCollateral != 0) {
                    epochMaxStrikeCheckpoints[epoch][nextNode].rewardRates.push(
                            (((activeCollateral * epochRewards.amounts[i]) /
                                totalEpochActiveCollateral) *
                                (10**COLLATERAL_TOKEN_DECIMALS)) /
                                activeCollateral
                        );
                }
                unchecked {
                    ++i;
                }
            }

            iterator++;
            (, nextNode) = epochStrikesList[epoch].getNextNode(nextNode);
        }
    }

    /**
     * @notice            Pushes new item into strikes, checkpoints and
     *                    weights in a single-go for a options purchase
     *                    instance.
     * @param _purchaseId Options purchase ID
     * @param _maxStrike  Maxstrike to push into strikes array of the
     *                    options purchase.
     * @param _checkpoint Checkpoint to push into checkpoints array of
     *                    the options purchase.
     * @param _weight     Weight (%) to push into weights array of the
     *                    options purchase in 1e18 decimals.
     */
    function _updatePurchasePositionMaxStrikesLiquidity(
        uint256 _purchaseId,
        uint256 _maxStrike,
        uint256 _checkpoint,
        uint256 _weight
    ) internal {
        _optionsPositions[_purchaseId].strikes.push(_maxStrike);
        _optionsPositions[_purchaseId].checkpoints.push(_checkpoint);
        _optionsPositions[_purchaseId].weights.push(_weight);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   INTERNAL / PRIVATE VIEWS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice Validate params for purchase and deposit.
     * @param _strike Strike price for the option.
     * @param _amount Amount of options or liquidity added.
     * @param _epoch  Epoch of the pool.
     * @param _user   User address provided.
     */

    function _validateParams(
        uint256 _strike,
        uint256 _amount,
        uint256 _epoch,
        address _user
    ) internal view {
        _validate(_user != address(0), 1);
        _validate(_amount != 0, 3);
        _validate(
            _strike != 0 && _strike % _epochData[_epoch].tickSize == 0,
            5
        );
    }

    /**
     * @notice              Revert-er function to revert with string error message.
     * @param trueCondition Similar to require, a condition that has to be false
     *                      to revert.
     * @param errorCode     Index in the errors[] that was set in error controller.
     */
    function _validate(bool trueCondition, uint256 errorCode) internal pure {
        if (!trueCondition) {
            revert AtlanticPutsPoolError(errorCode);
        }
    }

    /**
     * @notice       Checks if vault is not expired and bootstrapped.
     * @param  epoch Epoch of the pool.
     * @return isVaultBootstrapped
     */
    function _isVaultBootstrapped(uint256 epoch) internal view returns (bool) {
        return
            _epochData[epoch].state == EpochState.BootStrapped &&
            block.timestamp <= _epochData[epoch].expiryTime;
    }

    /**
     * @param  _value Value of max strike / node
     * @param  _epoch Epoch of the pool
     * @return tail   of the linked list
     */
    function _getSortedSpot(uint256 _value, uint256 _epoch)
        private
        view
        returns (uint256)
    {
        if (epochStrikesList[_epoch].sizeOf() == 0) {
            return 0;
        }

        uint256 next;
        (, next) = epochStrikesList[_epoch].getAdjacent(0, true);
        // Switch to descending
        while (
            (next != 0) &&
            (
                (_value <
                    (
                        epochMaxStrikeCheckpoints[_epoch][next].maxStrike != 0
                            ? next
                            : 0
                    ))
            )
        ) {
            next = epochStrikesList[_epoch].list[next][true];
        }
        return next;
    }

    /**
     * @notice         Get fee basis points from dopex fee strategy.
     * @param _feeKey  Identifier Key for fee type.
     * @param _account Address of the account for discount purposes.
     */
    function _getFeeBps(uint256 _feeKey, address _account)
        private
        view
        returns (uint256 feeBps)
    {
        feeBps = IDopexFeeStrategy(addresses[Contracts.FeeStrategy]).getFeeBps(
            _feeKey,
            _account,
            vaultConfig[VaultConfig.UseDiscount] == 1 ? true : false
        );
    }

    /**
     * @dev         checks for contract or eoa addresses
     * @param  addr the address to check
     * @return bool whether the passed address is a contract address
     */
    function _isContract(address addr) private view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(addr)
        }
        return size != 0;
    }

    /**
     * @notice Check for whitelisted contracts.
     */
    function _isEligibleSender() internal view {
        // the below condition checks whether the caller is a contract or not
        if (msg.sender != tx.origin) {
            _checkRoles(WHITELISTED_CONTRACT_ROLE);
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ADMIN METHODS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice        Set  vault configurations.
     * @param _types   Configuration type.
     * @param _configs Configuration parameter.
     */
    function setVaultConfigs(
        VaultConfig[] calldata _types,
        uint256[] calldata _configs
    ) external onlyRoles(ADMIN_ROLE) {
        _validate(_types.length == _configs.length, 0);
        for (uint256 i; i < _types.length; ) {
            vaultConfig[_types[i]] = _configs[i];
            emit VaultConfigSet(_types[i], _configs[i]);

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice          Sets (adds) a list of addresses to the address list.
     * @dev             an only be called by the owner.
     * @param _types     Contract type to set from Contracs enum
     * @param _addresses  address of the contract.
     */
    function setAddresses(
        Contracts[] calldata _types,
        address[] calldata _addresses
    ) external onlyRoles(ADMIN_ROLE) {
        _validate(_types.length == _addresses.length, 0);
        for (uint256 i; i < _types.length; ) {
            _validate(_addresses[i] != address(0), 1);
            addresses[_types[i]] = _addresses[i];
            emit AddressSet(_types[i], _addresses[i]);

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Pauses the vault for emergency cases.
     * @dev    Can only be called by the owner.
     */
    function pause() external onlyRoles(ADMIN_ROLE) {
        _pause();
    }

    /**
     * @notice Unpauses the vault
     * @dev    Can only be called by the owner
     */

    function unpause() external onlyRoles(ADMIN_ROLE) {
        _unpause();
    }

    /**
     * @notice Transfers all funds to msg.sender
     * @dev Can only be called by DEFAULT_ADMIN_ROLE
     * @param tokens The list of erc20 tokens to withdraw
     * @param transferNative Whether should transfer the native currency
     */
    function emergencyWithdraw(address[] calldata tokens, bool transferNative)
        external
        onlyRoles(ADMIN_ROLE)
        returns (bool)
    {
        _whenPaused();
        if (transferNative) payable(msg.sender).transfer(address(this).balance);

        for (uint256 i; i < tokens.length; ) {
            _safeTransfer(
                tokens[i],
                msg.sender,
                IERC20(tokens[i]).balanceOf(address(this))
            );
            unchecked {
                ++i;
            }
        }

        emit EmergencyWithdraw(msg.sender);

        return true;
    }

    /**
     * @notice              Set rewards for an upcoming epoch.
     * @param _rewardTokens Addresses of the reward tokens.
     * @param _amounts      Amounts of tokens to reward.
     * @param _epoch        Upcoming epoch.
     */
    function setEpochRewards(
        address[] calldata _rewardTokens,
        uint256[] calldata _amounts,
        uint256 _epoch
    ) external onlyRoles(ADMIN_ROLE) {
        _validate(_rewardTokens.length == _amounts.length, 0);

        for (uint256 i; i < _rewardTokens.length; ) {
            _safeTransferFrom(
                _rewardTokens[i],
                msg.sender,
                address(this),
                _amounts[i]
            );

            _epochRewards[_epoch].rewardTokens.push(_rewardTokens[i]);
            _epochRewards[_epoch].amounts.push(_amounts[i]);

            emit EpochRewardsSet(_epoch, _amounts[i], _rewardTokens[i]);

            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Bootstraps a new epoch, sets the strike based on offset% set. To be called after expiry
     *         of every epoch. Ensure strike offset is set before calling this function
     * @param  expiry   Expiry of the epoch to set.
     * @param  tickSize Spacing between max strikes.
     * @return success
     */
    function bootstrap(uint256 expiry, uint256 tickSize)
        external
        nonReentrant
        onlyRoles(BOOSTRAPPER_ROLE)
        returns (bool)
    {
        _validate(expiry > block.timestamp, 2);
        _validate(tickSize != 0, 3);

        uint256 nextEpoch = currentEpoch + 1;

        EpochData memory _vaultState = _epochData[nextEpoch];

        // Prev epoch must be expired
        if (currentEpoch > 0)
            _validate(_epochData[nextEpoch - 1].state == EpochState.Expired, 4);

        _vaultState.startTime = block.timestamp;
        _vaultState.tickSize = tickSize;
        _vaultState.expiryTime = expiry;
        _vaultState.state = EpochState.BootStrapped;

        currentEpoch = nextEpoch;

        _epochData[nextEpoch] = _vaultState;

        emit Bootstrap(nextEpoch);

        return true;
    }
}

File 3 of 17 : AtlanticPutsPoolEnums.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

enum OptionsState {
    Settled,
    Active,
    Unlocked
}

enum EpochState {
    InActive,
    BootStrapped,
    Expired,
    Paused
}

enum Contracts {
    QuoteToken,
    BaseToken,
    FeeDistributor,
    FeeStrategy,
    OptionPricing,
    PriceOracle,
    VolatilityOracle,
    Gov
}

enum VaultConfig {
    IvBoost,
    BlackoutWindow,
    FundingInterval,
    BaseFundingRate,
    UseDiscount,
    MaxDepositFee
}

File 4 of 17 : AtlanticPutsPoolState.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

// Structs
import {EpochData, MaxStrikesRange, Checkpoint, OptionsPurchase, DepositPosition, EpochRewards, MaxStrike} from "./AtlanticPutsPoolStructs.sol";

// Enums
import {OptionsState, EpochState, Contracts, VaultConfig} from "./AtlanticPutsPoolEnums.sol";

contract AtlanticPutsPoolState {
    uint256 internal constant PURCHASE_FEES_KEY = 0;
    uint256 internal constant ROLLOVER_FEES_KEY = 2;
    uint256 internal constant FEE_BPS_PRECISION = 10000000;
    uint256 internal constant PRICE_ORACLE_DECIMALS = 30;

    /// @dev Options amounts precision
    uint256 internal constant OPTION_TOKEN_DECIMALS = 18;

    /// @dev Number of decimals for max strikes
    uint256 internal constant STRIKE_DECIMALS = 8;

    /// @dev Max strike weights divisor/multiplier
    uint256 internal constant WEIGHTS_MUL_DIV = 1e18;

    uint256 public currentEpoch;

    uint256 public purchasePositionsCounter = 1;
    
    uint256 public depositPositionsCounter = 0;

    mapping(VaultConfig => uint256) public vaultConfig;
    mapping(Contracts => address) public addresses;
    mapping(uint256 => EpochData) internal _epochData;
    mapping(uint256 => EpochRewards) internal _epochRewards;
    mapping(uint256 => DepositPosition) internal _depositPositions;
    mapping(uint256 => OptionsPurchase) internal _optionsPositions;

    /**
     * @notice Checkpoints for a max strike in a epoch
     * @dev    epoch => max strike => Checkpoint[]
     */
    mapping(uint256 => mapping(uint256 => MaxStrike))
        internal epochMaxStrikeCheckpoints;

    mapping(uint256 => mapping(uint256 => uint256))
        internal epochMaxStrikeCheckpointsLength;

    /**
     *  @notice Start index of checkpoint (reference point to
     *           loop from on _squeeze())
     *  @dev    epoch => index
     */
    mapping(uint256 => mapping(uint256 => uint256))
        internal epochMaxStrikeCheckpointStartIndex;

    event EmergencyWithdraw(address sender);

    event Bootstrap(uint256 epoch);

    event NewDeposit(
        uint256 epoch,
        uint256 depositId,
        uint256 strike,
        uint256 amount,
        address user,
        address sender
    );

    event NewPurchase(
        uint256 epoch,
        uint256 purchaseId,
        uint256 premium,
        uint256 fee,
        address user,
        address sender
    );

    event Withdraw(
        uint256 depositId,
        address receiver,
        uint256 withdrawableAmount,
        uint256 borrowFees,
        uint256 premium,
        uint256 underlying,
        uint256[] rewards
    );

    event EpochRewardsSet(uint256 epoch, uint256 amount, address rewardToken);

    event Unwind(uint256 epoch, uint256 strike, uint256 amount, address caller);

    event UnlockCollateral(
        uint256 epoch,
        uint256 totalCollateral,
        address caller
    );

    event NewSettle(
        uint256 epoch,
        uint256 strike,
        address user,
        uint256 amount,
        uint256 pnl
    );

    event RelockCollateral(
        uint256 epoch,
        uint256 strike,
        uint256 totalCollateral,
        address caller
    );

    event AddressSet(Contracts _type, address _address);

    event EpochExpired(address _sender);

    event VaultConfigSet(VaultConfig _type, uint256 _config);

    event rolloverDepositSet(uint256 _depositId, bool _setAs);

    error AtlanticPutsPoolError(uint256 errorCode);
}

File 5 of 17 : AtlanticPutsPoolStructs.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import {OptionsState, EpochState} from "./AtlanticPutsPoolEnums.sol";

struct EpochData {
    uint256 startTime;
    uint256 expiryTime;
    uint256 totalLiquidity;
    uint256 totalActiveCollateral;
    uint256 fundingRate;
    uint256 tickSize;
    MaxStrikesRange maxStrikesRange;
    EpochState state;
}

struct MaxStrikesRange {
    uint256 highest;
    uint256 lowest;
}

struct Checkpoint {
    uint256 startTime;
    uint256 unlockedCollateral;
    uint256 premiumAccrued;
    uint256 borrowFeesAccrued;
    uint256 underlyingAccrued;
    uint256 totalLiquidity;
    uint256 liquidityBalance;
    uint256 activeCollateral;
}

struct EpochRewards {
    address[] rewardTokens;
    uint256[] amounts;
}

struct OptionsPurchase {
    uint256 epoch;
    uint256 optionStrike;
    uint256 optionsAmount;
    uint256 unlockEntryTimestamp;
    uint256[] strikes;
    uint256[] checkpoints;
    uint256[] weights;
    OptionsState state;
    address user;
    address delegate;
}

struct DepositPosition {
    uint256 epoch;
    uint256 strike;
    uint256 liquidity;
    uint256 checkpoint;
    address depositor;
    bool rollover;
}

struct MaxStrike {
    uint256 maxStrike;
    uint256 activeCollateral;
    uint256[] rewardRates;
    mapping(uint256 => Checkpoint) checkpoints;
}

File 6 of 17 : IDopexFeeStrategy.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

interface IDopexFeeStrategy {
 function getFeeBps(
        uint256 feeType,
        address user,
        bool useDiscount
    ) external view returns (uint256 _feeBps);
}

File 7 of 17 : Pausable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.17;

/// @title Lighter version of the Openzeppelin Pausable contract
/// @author witherblock
/// @notice Helps pause a contract to block the execution of selected functions
/// @dev Difference from the Openzeppelin version is changing the modifiers to internal fns and requires to reverts
abstract contract Pausable {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Internal function to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _whenNotPaused() internal view {
        if (paused()) revert ContractPaused();
    }

    /**
     * @dev Internal function to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _whenPaused() internal view {
        if (!paused()) revert ContractNotPaused();
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual {
        _whenNotPaused();
        _paused = true;
        emit Paused(msg.sender);
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual {
        _whenPaused();
        _paused = false;
        emit Unpaused(msg.sender);
    }

    error ContractPaused();
    error ContractNotPaused();
}

File 8 of 17 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity 0.8.17;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    error ReentrancyCall();

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        if (_status == _ENTERED) revert ReentrancyCall();

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 9 of 17 : IERC20.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 * NOTE: Modified to include symbols and decimals.
 */
interface IERC20 {

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

  function decimals() external view returns (uint8);

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

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

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

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

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

}

File 10 of 17 : IOptionPricing.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

interface IOptionPricing {
  function getOptionPrice(
    bool isPut,
    uint256 expiry,
    uint256 strike,
    uint256 lastPrice,
    uint256 baseIv
  ) external view returns (uint256);
}

File 11 of 17 : IPriceOracle.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

interface IPriceOracle {
    function latestAnswer() external view returns (uint256);

    function getUnderlyingPrice() external view returns (uint256);

    function getCollateralPrice() external view returns (uint256);

    function getPrice(
        address token,
        bool maximise,
        bool includeAmmPrice,
        bool useSwapPricing
    ) external view returns (uint256);
}

File 12 of 17 : IVolatilityOracle.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

interface IVolatilityOracle {
    function getVolatility(uint256 strike) external view returns (uint256);
}

File 13 of 17 : EthWeeklyAtlanticPutsV3.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

// Contracts
import {AtlanticPutsPool} from "../atlantic-pools/AtlanticPutsPool.sol";

/// @title BTC Weekly Puts SSOV V3 contract
contract EthWeeklyAtlanticPutsV3 is AtlanticPutsPool {
    constructor()
        AtlanticPutsPool(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8)
    {}
}

File 14 of 17 : Ownable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
/// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)
/// for compatibility, the nomenclature for the 2-step ownership handover
/// may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev `bytes4(keccak256(bytes("Unauthorized()")))`.
    uint256 private constant _UNAUTHORIZED_ERROR_SELECTOR = 0x82b42900;

    /// @dev `bytes4(keccak256(bytes("NewOwnerIsZeroAddress()")))`.
    uint256 private constant _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR = 0x7448fbae;

    /// @dev `bytes4(keccak256(bytes("NoHandoverRequest()")))`.
    uint256 private constant _NO_HANDOVER_REQUEST_ERROR_SELECTOR = 0x6f5e8818;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`.
    /// It is intentionally choosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Clean the upper 96 bits.
            newOwner := shr(96, shl(96, newOwner))
            // Store the new value.
            sstore(not(_OWNER_SLOT_NOT), newOwner)
            // Emit the {OwnershipTransferred} event.
            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            let ownerSlot := not(_OWNER_SLOT_NOT)
            // Clean the upper 96 bits.
            newOwner := shr(96, shl(96, newOwner))
            // Emit the {OwnershipTransferred} event.
            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
            // Store the new value.
            sstore(ownerSlot, newOwner)
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                revert(0x1c, 0x04)
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR)
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will be automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, _NO_HANDOVER_REQUEST_ERROR_SELECTOR)
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(not(_OWNER_SLOT_NOT))
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    function ownershipHandoverValidFor() public view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

File 15 of 17 : OwnableRoles.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./Ownable.sol";

/// @notice Simple single owner and multiroles authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
/// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)
/// for compatibility, the nomenclature for the 2-step ownership handover and roles
/// may be unique to this codebase.
abstract contract OwnableRoles is Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev `bytes4(keccak256(bytes("Unauthorized()")))`.
    uint256 private constant _UNAUTHORIZED_ERROR_SELECTOR = 0x82b42900;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The `user`'s roles is updated to `roles`.
    /// Each bit of `roles` represents whether the role is set.
    event RolesUpdated(address indexed user, uint256 indexed roles);

    /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.
    uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE =
        0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The role slot of `user` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED))
    ///     let roleSlot := keccak256(0x00, 0x20)
    /// ```
    /// This automatically ignores the upper bits of the `user` in case
    /// they are not clean, as well as keep the `keccak256` under 32-bytes.
    ///
    /// Note: This is equal to `_OWNER_SLOT_NOT` in for gas efficiency.
    uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Grants the roles directly without authorization guard.
    /// Each bit of `roles` represents the role to turn on.
    function _grantRoles(address user, uint256 roles) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the role slot.
            mstore(0x0c, _ROLE_SLOT_SEED)
            mstore(0x00, user)
            let roleSlot := keccak256(0x0c, 0x20)
            // Load the current value and `or` it with `roles`.
            roles := or(sload(roleSlot), roles)
            // Store the new value.
            sstore(roleSlot, roles)
            // Emit the {RolesUpdated} event.
            log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
        }
    }

    /// @dev Removes the roles directly without authorization guard.
    /// Each bit of `roles` represents the role to turn off.
    function _removeRoles(address user, uint256 roles) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the role slot.
            mstore(0x0c, _ROLE_SLOT_SEED)
            mstore(0x00, user)
            let roleSlot := keccak256(0x0c, 0x20)
            // Load the current value.
            let currentRoles := sload(roleSlot)
            // Use `and` to compute the intersection of `currentRoles` and `roles`,
            // `xor` it with `currentRoles` to flip the bits in the intersection.
            roles := xor(currentRoles, and(currentRoles, roles))
            // Then, store the new value.
            sstore(roleSlot, roles)
            // Emit the {RolesUpdated} event.
            log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
        }
    }

    /// @dev Throws if the sender does not have any of the `roles`.
    function _checkRoles(uint256 roles) internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the role slot.
            mstore(0x0c, _ROLE_SLOT_SEED)
            mstore(0x00, caller())
            // Load the stored value, and if the `and` intersection
            // of the value and `roles` is zero, revert.
            if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Throws if the sender is not the owner,
    /// and does not have any of the `roles`.
    /// Checks for ownership first, then lazily checks for roles.
    function _checkOwnerOrRoles(uint256 roles) internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner.
            // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
            if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
                // Compute the role slot.
                mstore(0x0c, _ROLE_SLOT_SEED)
                mstore(0x00, caller())
                // Load the stored value, and if the `and` intersection
                // of the value and `roles` is zero, revert.
                if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                    mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /// @dev Throws if the sender does not have any of the `roles`,
    /// and is not the owner.
    /// Checks for roles first, then lazily checks for ownership.
    function _checkRolesOrOwner(uint256 roles) internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the role slot.
            mstore(0x0c, _ROLE_SLOT_SEED)
            mstore(0x00, caller())
            // Load the stored value, and if the `and` intersection
            // of the value and `roles` is zero, revert.
            if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                // If the caller is not the stored owner.
                // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
                if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
                    mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to grant `user` `roles`.
    /// If the `user` already has a role, then it will be an no-op for the role.
    function grantRoles(address user, uint256 roles) public payable virtual onlyOwner {
        _grantRoles(user, roles);
    }

    /// @dev Allows the owner to remove `user` `roles`.
    /// If the `user` does not have a role, then it will be an no-op for the role.
    function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner {
        _removeRoles(user, roles);
    }

    /// @dev Allow the caller to remove their own roles.
    /// If the caller does not have a role, then it will be an no-op for the role.
    function renounceRoles(uint256 roles) public payable virtual {
        _removeRoles(msg.sender, roles);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `user` has any of `roles`.
    function hasAnyRole(address user, uint256 roles) public view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the role slot.
            mstore(0x0c, _ROLE_SLOT_SEED)
            mstore(0x00, user)
            // Load the stored value, and set the result to whether the
            // `and` intersection of the value and `roles` is not zero.
            result := iszero(iszero(and(sload(keccak256(0x0c, 0x20)), roles)))
        }
    }

    /// @dev Returns whether `user` has all of `roles`.
    function hasAllRoles(address user, uint256 roles) public view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the role slot.
            mstore(0x0c, _ROLE_SLOT_SEED)
            mstore(0x00, user)
            // Whether the stored value is contains all the set bits in `roles`.
            result := eq(and(sload(keccak256(0x0c, 0x20)), roles), roles)
        }
    }

    /// @dev Returns the roles of `user`.
    function rolesOf(address user) public view virtual returns (uint256 roles) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the role slot.
            mstore(0x0c, _ROLE_SLOT_SEED)
            mstore(0x00, user)
            // Load the stored value.
            roles := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`.
    /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
    /// Not recommended to be called on-chain.
    function rolesFromOrdinals(uint8[] memory ordinals) public pure returns (uint256 roles) {
        /// @solidity memory-safe-assembly
        assembly {
            for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } {
                // We don't need to mask the values of `ordinals`, as Solidity
                // cleans dirty upper bits when storing variables into memory.
                roles := or(shl(mload(add(ordinals, i)), 1), roles)
            }
        }
    }

    /// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap.
    /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
    /// Not recommended to be called on-chain.
    function ordinalsFromRoles(uint256 roles) public pure returns (uint8[] memory ordinals) {
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the pointer to the free memory.
            ordinals := mload(0x40)
            let ptr := add(ordinals, 0x20)
            let o := 0
            // The absence of lookup tables, De Bruijn, etc., here is intentional for
            // smaller bytecode, as this function is not meant to be called on-chain.
            for { let t := roles } 1 {} {
                mstore(ptr, o)
                // `shr` 5 is equivalent to multiplying by 0x20.
                // Push back into the ordinals array if the bit is set.
                ptr := add(ptr, shl(5, and(t, 1)))
                o := add(o, 1)
                t := shr(o, roles)
                if iszero(t) { break }
            }
            // Store the length of `ordinals`.
            mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
            // Allocate the memory.
            mstore(0x40, ptr)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by an account with `roles`.
    modifier onlyRoles(uint256 roles) virtual {
        _checkRoles(roles);
        _;
    }

    /// @dev Marks a function as only callable by the owner or by an account
    /// with `roles`. Checks for ownership first, then lazily checks for roles.
    modifier onlyOwnerOrRoles(uint256 roles) virtual {
        _checkOwnerOrRoles(roles);
        _;
    }

    /// @dev Marks a function as only callable by an account with `roles`
    /// or the owner. Checks for roles first, then lazily checks for ownership.
    modifier onlyRolesOrOwner(uint256 roles) virtual {
        _checkRolesOrOwner(roles);
        _;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ROLE CONSTANTS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // IYKYK

    uint256 internal constant _ROLE_0 = 1 << 0;
    uint256 internal constant _ROLE_1 = 1 << 1;
    uint256 internal constant _ROLE_2 = 1 << 2;
    uint256 internal constant _ROLE_3 = 1 << 3;
    uint256 internal constant _ROLE_4 = 1 << 4;
    uint256 internal constant _ROLE_5 = 1 << 5;
    uint256 internal constant _ROLE_6 = 1 << 6;
    uint256 internal constant _ROLE_7 = 1 << 7;
    uint256 internal constant _ROLE_8 = 1 << 8;
    uint256 internal constant _ROLE_9 = 1 << 9;
    uint256 internal constant _ROLE_10 = 1 << 10;
    uint256 internal constant _ROLE_11 = 1 << 11;
    uint256 internal constant _ROLE_12 = 1 << 12;
    uint256 internal constant _ROLE_13 = 1 << 13;
    uint256 internal constant _ROLE_14 = 1 << 14;
    uint256 internal constant _ROLE_15 = 1 << 15;
    uint256 internal constant _ROLE_16 = 1 << 16;
    uint256 internal constant _ROLE_17 = 1 << 17;
    uint256 internal constant _ROLE_18 = 1 << 18;
    uint256 internal constant _ROLE_19 = 1 << 19;
    uint256 internal constant _ROLE_20 = 1 << 20;
    uint256 internal constant _ROLE_21 = 1 << 21;
    uint256 internal constant _ROLE_22 = 1 << 22;
    uint256 internal constant _ROLE_23 = 1 << 23;
    uint256 internal constant _ROLE_24 = 1 << 24;
    uint256 internal constant _ROLE_25 = 1 << 25;
    uint256 internal constant _ROLE_26 = 1 << 26;
    uint256 internal constant _ROLE_27 = 1 << 27;
    uint256 internal constant _ROLE_28 = 1 << 28;
    uint256 internal constant _ROLE_29 = 1 << 29;
    uint256 internal constant _ROLE_30 = 1 << 30;
    uint256 internal constant _ROLE_31 = 1 << 31;
    uint256 internal constant _ROLE_32 = 1 << 32;
    uint256 internal constant _ROLE_33 = 1 << 33;
    uint256 internal constant _ROLE_34 = 1 << 34;
    uint256 internal constant _ROLE_35 = 1 << 35;
    uint256 internal constant _ROLE_36 = 1 << 36;
    uint256 internal constant _ROLE_37 = 1 << 37;
    uint256 internal constant _ROLE_38 = 1 << 38;
    uint256 internal constant _ROLE_39 = 1 << 39;
    uint256 internal constant _ROLE_40 = 1 << 40;
    uint256 internal constant _ROLE_41 = 1 << 41;
    uint256 internal constant _ROLE_42 = 1 << 42;
    uint256 internal constant _ROLE_43 = 1 << 43;
    uint256 internal constant _ROLE_44 = 1 << 44;
    uint256 internal constant _ROLE_45 = 1 << 45;
    uint256 internal constant _ROLE_46 = 1 << 46;
    uint256 internal constant _ROLE_47 = 1 << 47;
    uint256 internal constant _ROLE_48 = 1 << 48;
    uint256 internal constant _ROLE_49 = 1 << 49;
    uint256 internal constant _ROLE_50 = 1 << 50;
    uint256 internal constant _ROLE_51 = 1 << 51;
    uint256 internal constant _ROLE_52 = 1 << 52;
    uint256 internal constant _ROLE_53 = 1 << 53;
    uint256 internal constant _ROLE_54 = 1 << 54;
    uint256 internal constant _ROLE_55 = 1 << 55;
    uint256 internal constant _ROLE_56 = 1 << 56;
    uint256 internal constant _ROLE_57 = 1 << 57;
    uint256 internal constant _ROLE_58 = 1 << 58;
    uint256 internal constant _ROLE_59 = 1 << 59;
    uint256 internal constant _ROLE_60 = 1 << 60;
    uint256 internal constant _ROLE_61 = 1 << 61;
    uint256 internal constant _ROLE_62 = 1 << 62;
    uint256 internal constant _ROLE_63 = 1 << 63;
    uint256 internal constant _ROLE_64 = 1 << 64;
    uint256 internal constant _ROLE_65 = 1 << 65;
    uint256 internal constant _ROLE_66 = 1 << 66;
    uint256 internal constant _ROLE_67 = 1 << 67;
    uint256 internal constant _ROLE_68 = 1 << 68;
    uint256 internal constant _ROLE_69 = 1 << 69;
    uint256 internal constant _ROLE_70 = 1 << 70;
    uint256 internal constant _ROLE_71 = 1 << 71;
    uint256 internal constant _ROLE_72 = 1 << 72;
    uint256 internal constant _ROLE_73 = 1 << 73;
    uint256 internal constant _ROLE_74 = 1 << 74;
    uint256 internal constant _ROLE_75 = 1 << 75;
    uint256 internal constant _ROLE_76 = 1 << 76;
    uint256 internal constant _ROLE_77 = 1 << 77;
    uint256 internal constant _ROLE_78 = 1 << 78;
    uint256 internal constant _ROLE_79 = 1 << 79;
    uint256 internal constant _ROLE_80 = 1 << 80;
    uint256 internal constant _ROLE_81 = 1 << 81;
    uint256 internal constant _ROLE_82 = 1 << 82;
    uint256 internal constant _ROLE_83 = 1 << 83;
    uint256 internal constant _ROLE_84 = 1 << 84;
    uint256 internal constant _ROLE_85 = 1 << 85;
    uint256 internal constant _ROLE_86 = 1 << 86;
    uint256 internal constant _ROLE_87 = 1 << 87;
    uint256 internal constant _ROLE_88 = 1 << 88;
    uint256 internal constant _ROLE_89 = 1 << 89;
    uint256 internal constant _ROLE_90 = 1 << 90;
    uint256 internal constant _ROLE_91 = 1 << 91;
    uint256 internal constant _ROLE_92 = 1 << 92;
    uint256 internal constant _ROLE_93 = 1 << 93;
    uint256 internal constant _ROLE_94 = 1 << 94;
    uint256 internal constant _ROLE_95 = 1 << 95;
    uint256 internal constant _ROLE_96 = 1 << 96;
    uint256 internal constant _ROLE_97 = 1 << 97;
    uint256 internal constant _ROLE_98 = 1 << 98;
    uint256 internal constant _ROLE_99 = 1 << 99;
    uint256 internal constant _ROLE_100 = 1 << 100;
    uint256 internal constant _ROLE_101 = 1 << 101;
    uint256 internal constant _ROLE_102 = 1 << 102;
    uint256 internal constant _ROLE_103 = 1 << 103;
    uint256 internal constant _ROLE_104 = 1 << 104;
    uint256 internal constant _ROLE_105 = 1 << 105;
    uint256 internal constant _ROLE_106 = 1 << 106;
    uint256 internal constant _ROLE_107 = 1 << 107;
    uint256 internal constant _ROLE_108 = 1 << 108;
    uint256 internal constant _ROLE_109 = 1 << 109;
    uint256 internal constant _ROLE_110 = 1 << 110;
    uint256 internal constant _ROLE_111 = 1 << 111;
    uint256 internal constant _ROLE_112 = 1 << 112;
    uint256 internal constant _ROLE_113 = 1 << 113;
    uint256 internal constant _ROLE_114 = 1 << 114;
    uint256 internal constant _ROLE_115 = 1 << 115;
    uint256 internal constant _ROLE_116 = 1 << 116;
    uint256 internal constant _ROLE_117 = 1 << 117;
    uint256 internal constant _ROLE_118 = 1 << 118;
    uint256 internal constant _ROLE_119 = 1 << 119;
    uint256 internal constant _ROLE_120 = 1 << 120;
    uint256 internal constant _ROLE_121 = 1 << 121;
    uint256 internal constant _ROLE_122 = 1 << 122;
    uint256 internal constant _ROLE_123 = 1 << 123;
    uint256 internal constant _ROLE_124 = 1 << 124;
    uint256 internal constant _ROLE_125 = 1 << 125;
    uint256 internal constant _ROLE_126 = 1 << 126;
    uint256 internal constant _ROLE_127 = 1 << 127;
    uint256 internal constant _ROLE_128 = 1 << 128;
    uint256 internal constant _ROLE_129 = 1 << 129;
    uint256 internal constant _ROLE_130 = 1 << 130;
    uint256 internal constant _ROLE_131 = 1 << 131;
    uint256 internal constant _ROLE_132 = 1 << 132;
    uint256 internal constant _ROLE_133 = 1 << 133;
    uint256 internal constant _ROLE_134 = 1 << 134;
    uint256 internal constant _ROLE_135 = 1 << 135;
    uint256 internal constant _ROLE_136 = 1 << 136;
    uint256 internal constant _ROLE_137 = 1 << 137;
    uint256 internal constant _ROLE_138 = 1 << 138;
    uint256 internal constant _ROLE_139 = 1 << 139;
    uint256 internal constant _ROLE_140 = 1 << 140;
    uint256 internal constant _ROLE_141 = 1 << 141;
    uint256 internal constant _ROLE_142 = 1 << 142;
    uint256 internal constant _ROLE_143 = 1 << 143;
    uint256 internal constant _ROLE_144 = 1 << 144;
    uint256 internal constant _ROLE_145 = 1 << 145;
    uint256 internal constant _ROLE_146 = 1 << 146;
    uint256 internal constant _ROLE_147 = 1 << 147;
    uint256 internal constant _ROLE_148 = 1 << 148;
    uint256 internal constant _ROLE_149 = 1 << 149;
    uint256 internal constant _ROLE_150 = 1 << 150;
    uint256 internal constant _ROLE_151 = 1 << 151;
    uint256 internal constant _ROLE_152 = 1 << 152;
    uint256 internal constant _ROLE_153 = 1 << 153;
    uint256 internal constant _ROLE_154 = 1 << 154;
    uint256 internal constant _ROLE_155 = 1 << 155;
    uint256 internal constant _ROLE_156 = 1 << 156;
    uint256 internal constant _ROLE_157 = 1 << 157;
    uint256 internal constant _ROLE_158 = 1 << 158;
    uint256 internal constant _ROLE_159 = 1 << 159;
    uint256 internal constant _ROLE_160 = 1 << 160;
    uint256 internal constant _ROLE_161 = 1 << 161;
    uint256 internal constant _ROLE_162 = 1 << 162;
    uint256 internal constant _ROLE_163 = 1 << 163;
    uint256 internal constant _ROLE_164 = 1 << 164;
    uint256 internal constant _ROLE_165 = 1 << 165;
    uint256 internal constant _ROLE_166 = 1 << 166;
    uint256 internal constant _ROLE_167 = 1 << 167;
    uint256 internal constant _ROLE_168 = 1 << 168;
    uint256 internal constant _ROLE_169 = 1 << 169;
    uint256 internal constant _ROLE_170 = 1 << 170;
    uint256 internal constant _ROLE_171 = 1 << 171;
    uint256 internal constant _ROLE_172 = 1 << 172;
    uint256 internal constant _ROLE_173 = 1 << 173;
    uint256 internal constant _ROLE_174 = 1 << 174;
    uint256 internal constant _ROLE_175 = 1 << 175;
    uint256 internal constant _ROLE_176 = 1 << 176;
    uint256 internal constant _ROLE_177 = 1 << 177;
    uint256 internal constant _ROLE_178 = 1 << 178;
    uint256 internal constant _ROLE_179 = 1 << 179;
    uint256 internal constant _ROLE_180 = 1 << 180;
    uint256 internal constant _ROLE_181 = 1 << 181;
    uint256 internal constant _ROLE_182 = 1 << 182;
    uint256 internal constant _ROLE_183 = 1 << 183;
    uint256 internal constant _ROLE_184 = 1 << 184;
    uint256 internal constant _ROLE_185 = 1 << 185;
    uint256 internal constant _ROLE_186 = 1 << 186;
    uint256 internal constant _ROLE_187 = 1 << 187;
    uint256 internal constant _ROLE_188 = 1 << 188;
    uint256 internal constant _ROLE_189 = 1 << 189;
    uint256 internal constant _ROLE_190 = 1 << 190;
    uint256 internal constant _ROLE_191 = 1 << 191;
    uint256 internal constant _ROLE_192 = 1 << 192;
    uint256 internal constant _ROLE_193 = 1 << 193;
    uint256 internal constant _ROLE_194 = 1 << 194;
    uint256 internal constant _ROLE_195 = 1 << 195;
    uint256 internal constant _ROLE_196 = 1 << 196;
    uint256 internal constant _ROLE_197 = 1 << 197;
    uint256 internal constant _ROLE_198 = 1 << 198;
    uint256 internal constant _ROLE_199 = 1 << 199;
    uint256 internal constant _ROLE_200 = 1 << 200;
    uint256 internal constant _ROLE_201 = 1 << 201;
    uint256 internal constant _ROLE_202 = 1 << 202;
    uint256 internal constant _ROLE_203 = 1 << 203;
    uint256 internal constant _ROLE_204 = 1 << 204;
    uint256 internal constant _ROLE_205 = 1 << 205;
    uint256 internal constant _ROLE_206 = 1 << 206;
    uint256 internal constant _ROLE_207 = 1 << 207;
    uint256 internal constant _ROLE_208 = 1 << 208;
    uint256 internal constant _ROLE_209 = 1 << 209;
    uint256 internal constant _ROLE_210 = 1 << 210;
    uint256 internal constant _ROLE_211 = 1 << 211;
    uint256 internal constant _ROLE_212 = 1 << 212;
    uint256 internal constant _ROLE_213 = 1 << 213;
    uint256 internal constant _ROLE_214 = 1 << 214;
    uint256 internal constant _ROLE_215 = 1 << 215;
    uint256 internal constant _ROLE_216 = 1 << 216;
    uint256 internal constant _ROLE_217 = 1 << 217;
    uint256 internal constant _ROLE_218 = 1 << 218;
    uint256 internal constant _ROLE_219 = 1 << 219;
    uint256 internal constant _ROLE_220 = 1 << 220;
    uint256 internal constant _ROLE_221 = 1 << 221;
    uint256 internal constant _ROLE_222 = 1 << 222;
    uint256 internal constant _ROLE_223 = 1 << 223;
    uint256 internal constant _ROLE_224 = 1 << 224;
    uint256 internal constant _ROLE_225 = 1 << 225;
    uint256 internal constant _ROLE_226 = 1 << 226;
    uint256 internal constant _ROLE_227 = 1 << 227;
    uint256 internal constant _ROLE_228 = 1 << 228;
    uint256 internal constant _ROLE_229 = 1 << 229;
    uint256 internal constant _ROLE_230 = 1 << 230;
    uint256 internal constant _ROLE_231 = 1 << 231;
    uint256 internal constant _ROLE_232 = 1 << 232;
    uint256 internal constant _ROLE_233 = 1 << 233;
    uint256 internal constant _ROLE_234 = 1 << 234;
    uint256 internal constant _ROLE_235 = 1 << 235;
    uint256 internal constant _ROLE_236 = 1 << 236;
    uint256 internal constant _ROLE_237 = 1 << 237;
    uint256 internal constant _ROLE_238 = 1 << 238;
    uint256 internal constant _ROLE_239 = 1 << 239;
    uint256 internal constant _ROLE_240 = 1 << 240;
    uint256 internal constant _ROLE_241 = 1 << 241;
    uint256 internal constant _ROLE_242 = 1 << 242;
    uint256 internal constant _ROLE_243 = 1 << 243;
    uint256 internal constant _ROLE_244 = 1 << 244;
    uint256 internal constant _ROLE_245 = 1 << 245;
    uint256 internal constant _ROLE_246 = 1 << 246;
    uint256 internal constant _ROLE_247 = 1 << 247;
    uint256 internal constant _ROLE_248 = 1 << 248;
    uint256 internal constant _ROLE_249 = 1 << 249;
    uint256 internal constant _ROLE_250 = 1 << 250;
    uint256 internal constant _ROLE_251 = 1 << 251;
    uint256 internal constant _ROLE_252 = 1 << 252;
    uint256 internal constant _ROLE_253 = 1 << 253;
    uint256 internal constant _ROLE_254 = 1 << 254;
    uint256 internal constant _ROLE_255 = 1 << 255;
}

File 16 of 17 : SafeTransferLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Suggested gas stipend for contract receiving ETH
    /// that disallows any storage writes.
    uint256 internal constant _GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    /// Multiply by a small constant (e.g. 2), if needed.
    uint256 internal constant _GAS_STIPEND_NO_GRIEF = 100000;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` (in wei) ETH to `to`.
    /// Reverts upon failure.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
                // Store the function selector of `ETHTransferFailed()`.
                mstore(0x00, 0xb12d13eb)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    /// The `gasStipend` can be set to a low enough value to prevent
    /// storage writes or gas griefing.
    ///
    /// If sending via the normal procedure fails, force sends the ETH by
    /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
    ///
    /// Reverts if the current contract has insufficient balance.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // If insufficient balance, revert.
            if lt(selfbalance(), amount) {
                // Store the function selector of `ETHTransferFailed()`.
                mstore(0x00, 0xb12d13eb)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(gasStipend, to, amount, 0, 0, 0, 0)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                // We can directly use `SELFDESTRUCT` in the contract creation.
                // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
                pop(create(amount, 0x0b, 0x16))
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a gas stipend
    /// equal to `_GAS_STIPEND_NO_GRIEF`. This gas stipend is a reasonable default
    /// for 99% of cases and can be overriden with the three-argument version of this
    /// function if necessary.
    ///
    /// If sending via the normal procedure fails, force sends the ETH by
    /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
    ///
    /// Reverts if the current contract has insufficient balance.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        // Manually inlined because the compiler doesn't inline functions with branches.
        /// @solidity memory-safe-assembly
        assembly {
            // If insufficient balance, revert.
            if lt(selfbalance(), amount) {
                // Store the function selector of `ETHTransferFailed()`.
                mstore(0x00, 0xb12d13eb)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(_GAS_STIPEND_NO_GRIEF, to, amount, 0, 0, 0, 0)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                // We can directly use `SELFDESTRUCT` in the contract creation.
                // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
                pop(create(amount, 0x0b, 0x16))
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    /// The `gasStipend` can be set to a low enough value to prevent
    /// storage writes or gas griefing.
    ///
    /// Simply use `gasleft()` for `gasStipend` if you don't need a gas stipend.
    ///
    /// Note: Does NOT revert upon failure.
    /// Returns whether the transfer of ETH is successful instead.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and check if it succeeded or not.
            success := call(gasStipend, to, amount, 0, 0, 0, 0)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.

            // Store the function selector of `transferFrom(address,address,uint256)`.
            mstore(0x00, 0x23b872dd)
            mstore(0x20, from) // Store the `from` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x60, amount) // Store the `amount` argument.

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFromFailed()`.
                mstore(0x00, 0x7939f424)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.

            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, from) // Store the `from` argument.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                // Store the function selector of `TransferFromFailed()`.
                mstore(0x00, 0x7939f424)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            // Store the function selector of `transferFrom(address,address,uint256)`.
            mstore(0x00, 0x23b872dd)
            mstore(0x40, to) // Store the `to` argument.
            // The `amount` argument is already written to the memory word at 0x6a.
            amount := mload(0x60)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFromFailed()`.
                mstore(0x00, 0x7939f424)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1a, to) // Store the `to` argument.
            mstore(0x3a, amount) // Store the `amount` argument.
            // Store the function selector of `transfer(address,uint256)`,
            // left by 6 bytes (enough for 8tb of memory represented by the free memory pointer).
            // We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL).
            mstore(0x00, 0xa9059cbb000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFailed()`.
                mstore(0x00, 0x90b8ec18)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the part of the free memory pointer that was overwritten,
            // which is guaranteed to be zero, if less than 8tb of memory is used.
            mstore(0x3a, 0)
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x3a, 0x20)
                )
            ) {
                // Store the function selector of `TransferFailed()`.
                mstore(0x00, 0x90b8ec18)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            mstore(0x1a, to) // Store the `to` argument.
            // The `amount` argument is already written to the memory word at 0x3a.
            amount := mload(0x3a)
            // Store the function selector of `transfer(address,uint256)`,
            // left by 6 bytes (enough for 8tb of memory represented by the free memory pointer).
            // We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL).
            mstore(0x00, 0xa9059cbb000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFailed()`.
                mstore(0x00, 0x90b8ec18)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the part of the free memory pointer that was overwritten,
            // which is guaranteed to be zero, if less than 8tb of memory is used.
            mstore(0x3a, 0)
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1a, to) // Store the `to` argument.
            mstore(0x3a, amount) // Store the `amount` argument.
            // Store the function selector of `approve(address,uint256)`,
            // left by 6 bytes (enough for 8tb of memory represented by the free memory pointer).
            // We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL).
            mstore(0x00, 0x095ea7b3000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `ApproveFailed()`.
                mstore(0x00, 0x3e3f8f73)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the part of the free memory pointer that was overwritten,
            // which is guaranteed to be zero, if less than 8tb of memory is used.
            mstore(0x3a, 0)
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, account) // Store the `account` argument.
            amount :=
                mul(
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x1c, 0x24, 0x20, 0x20)
                    )
                )
        }
    }
}

File 17 of 17 : StructuredLinkedList.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IStructureInterface {
    function getValue(uint256 _id) external view returns (uint256);
}

/**
 * @title StructuredLinkedList
 * @author Vittorio Minacori (https://github.com/vittominacori)
 * @dev An utility library for using sorted linked list data structures in your Solidity project.
 */
library StructuredLinkedList {
    uint256 private constant _NULL = 0;
    uint256 private constant _HEAD = 0;

    bool private constant _PREV = false;
    bool private constant _NEXT = true;

    struct List {
        uint256 size;
        mapping(uint256 => mapping(bool => uint256)) list;
    }

    /**
     * @dev Checks if the list exists
     * @param self stored linked list from contract
     * @return bool true if list exists, false otherwise
     */
    function listExists(List storage self) internal view returns (bool) {
        // if the head nodes previous or next pointers both point to itself, then there are no items in the list
        if (self.list[_HEAD][_PREV] != _HEAD || self.list[_HEAD][_NEXT] != _HEAD) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Checks if the node exists
     * @param self stored linked list from contract
     * @param _node a node to search for
     * @return bool true if node exists, false otherwise
     */
    function nodeExists(List storage self, uint256 _node) internal view returns (bool) {
        if (self.list[_node][_PREV] == _HEAD && self.list[_node][_NEXT] == _HEAD) {
            if (self.list[_HEAD][_NEXT] == _node) {
                return true;
            } else {
                return false;
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Returns the number of elements in the list
     * @param self stored linked list from contract
     * @return uint256
     */
    function sizeOf(List storage self) internal view returns (uint256) {
        return self.size;
    }

    /**
     * @dev Returns the links of a node as a tuple
     * @param self stored linked list from contract
     * @param _node id of the node to get
     * @return bool, uint256, uint256 true if node exists or false otherwise, previous node, next node
     */
    function getNode(List storage self, uint256 _node) internal view returns (bool, uint256, uint256) {
        if (!nodeExists(self, _node)) {
            return (false, 0, 0);
        } else {
            return (true, self.list[_node][_PREV], self.list[_node][_NEXT]);
        }
    }

    /**
     * @dev Returns the link of a node `_node` in direction `_direction`.
     * @param self stored linked list from contract
     * @param _node id of the node to step from
     * @param _direction direction to step in
     * @return bool, uint256 true if node exists or false otherwise, node in _direction
     */
    function getAdjacent(List storage self, uint256 _node, bool _direction) internal view returns (bool, uint256) {
        if (!nodeExists(self, _node)) {
            return (false, 0);
        } else {
            return (true, self.list[_node][_direction]);
        }
    }

    /**
     * @dev Returns the link of a node `_node` in direction `_NEXT`.
     * @param self stored linked list from contract
     * @param _node id of the node to step from
     * @return bool, uint256 true if node exists or false otherwise, next node
     */
    function getNextNode(List storage self, uint256 _node) internal view returns (bool, uint256) {
        return getAdjacent(self, _node, _NEXT);
    }

    /**
     * @dev Returns the link of a node `_node` in direction `_PREV`.
     * @param self stored linked list from contract
     * @param _node id of the node to step from
     * @return bool, uint256 true if node exists or false otherwise, previous node
     */
    function getPreviousNode(List storage self, uint256 _node) internal view returns (bool, uint256) {
        return getAdjacent(self, _node, _PREV);
    }

    /**
     * @dev Can be used before `insert` to build an ordered list.
     * @dev Get the node and then `insertBefore` or `insertAfter` basing on your list order.
     * @dev If you want to order basing on other than `structure.getValue()` override this function
     * @param self stored linked list from contract
     * @param _structure the structure instance
     * @param _value value to seek
     * @return uint256 next node with a value less than _value
     */
    function getSortedSpot(List storage self, address _structure, uint256 _value) internal view returns (uint256) {
        if (sizeOf(self) == 0) {
            return 0;
        }

        uint256 next;
        (, next) = getAdjacent(self, _HEAD, _NEXT);
        while ((next != 0) && ((_value < IStructureInterface(_structure).getValue(next)) != _NEXT)) {
            next = self.list[next][_NEXT];
        }
        return next;
    }

    /**
     * @dev Insert node `_new` beside existing node `_node` in direction `_NEXT`.
     * @param self stored linked list from contract
     * @param _node existing node
     * @param _new  new node to insert
     * @return bool true if success, false otherwise
     */
    function insertAfter(List storage self, uint256 _node, uint256 _new) internal returns (bool) {
        return _insert(self, _node, _new, _NEXT);
    }

    /**
     * @dev Insert node `_new` beside existing node `_node` in direction `_PREV`.
     * @param self stored linked list from contract
     * @param _node existing node
     * @param _new  new node to insert
     * @return bool true if success, false otherwise
     */
    function insertBefore(List storage self, uint256 _node, uint256 _new) internal returns (bool) {
        return _insert(self, _node, _new, _PREV);
    }

    /**
     * @dev Removes an entry from the linked list
     * @param self stored linked list from contract
     * @param _node node to remove from the list
     * @return uint256 the removed node
     */
    function remove(List storage self, uint256 _node) internal returns (uint256) {
        if ((_node == _NULL) || (!nodeExists(self, _node))) {
            return 0;
        }
        _createLink(self, self.list[_node][_PREV], self.list[_node][_NEXT], _NEXT);
        delete self.list[_node][_PREV];
        delete self.list[_node][_NEXT];

        self.size -= 1; // NOT: SafeMath library should be used here to decrement.

        return _node;
    }

    /**
     * @dev Pushes an entry to the head of the linked list
     * @param self stored linked list from contract
     * @param _node new entry to push to the head
     * @return bool true if success, false otherwise
     */
    function pushFront(List storage self, uint256 _node) internal returns (bool) {
        return _push(self, _node, _NEXT);
    }

    /**
     * @dev Pushes an entry to the tail of the linked list
     * @param self stored linked list from contract
     * @param _node new entry to push to the tail
     * @return bool true if success, false otherwise
     */
    function pushBack(List storage self, uint256 _node) internal returns (bool) {
        return _push(self, _node, _PREV);
    }

    /**
     * @dev Pops the first entry from the head of the linked list
     * @param self stored linked list from contract
     * @return uint256 the removed node
     */
    function popFront(List storage self) internal returns (uint256) {
        return _pop(self, _NEXT);
    }

    /**
     * @dev Pops the first entry from the tail of the linked list
     * @param self stored linked list from contract
     * @return uint256 the removed node
     */
    function popBack(List storage self) internal returns (uint256) {
        return _pop(self, _PREV);
    }

    /**
     * @dev Pushes an entry to the head of the linked list
     * @param self stored linked list from contract
     * @param _node new entry to push to the head
     * @param _direction push to the head (_NEXT) or tail (_PREV)
     * @return bool true if success, false otherwise
     */
    function _push(List storage self, uint256 _node, bool _direction) private returns (bool) {
        return _insert(self, _HEAD, _node, _direction);
    }

    /**
     * @dev Pops the first entry from the linked list
     * @param self stored linked list from contract
     * @param _direction pop from the head (_NEXT) or the tail (_PREV)
     * @return uint256 the removed node
     */
    function _pop(List storage self, bool _direction) private returns (uint256) {
        uint256 adj;
        (, adj) = getAdjacent(self, _HEAD, _direction);
        return remove(self, adj);
    }

    /**
     * @dev Insert node `_new` beside existing node `_node` in direction `_direction`.
     * @param self stored linked list from contract
     * @param _node existing node
     * @param _new  new node to insert
     * @param _direction direction to insert node in
     * @return bool true if success, false otherwise
     */
    function _insert(List storage self, uint256 _node, uint256 _new, bool _direction) private returns (bool) {
        if (!nodeExists(self, _new) && nodeExists(self, _node)) {
            uint256 c = self.list[_node][_direction];
            _createLink(self, _node, _new, _direction);
            _createLink(self, _new, c, _direction);

            self.size += 1; // NOT: SafeMath library should be used here to increment.

            return true;
        }

        return false;
    }

    /**
     * @dev Creates a bidirectional link between two nodes on direction `_direction`
     * @param self stored linked list from contract
     * @param _node existing node
     * @param _link node to link to in the _direction
     * @param _direction direction to insert node in
     */
    function _createLink(List storage self, uint256 _node, uint256 _link, bool _direction) private {
        self.list[_link][!_direction] = _node;
        self.list[_node][_direction] = _link;
    }
}

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

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"AtlanticPutsPoolError","type":"error"},{"inputs":[],"name":"ContractNotPaused","type":"error"},{"inputs":[],"name":"ContractPaused","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"ReentrancyCall","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum Contracts","name":"_type","type":"uint8"},{"indexed":false,"internalType":"address","name":"_address","type":"address"}],"name":"AddressSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"Bootstrap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"EmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_sender","type":"address"}],"name":"EpochExpired","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"rewardToken","type":"address"}],"name":"EpochRewardsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"NewDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"purchaseId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"premium","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"NewPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pnl","type":"uint256"}],"name":"NewSettle","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalCollateral","type":"uint256"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"RelockCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"roles","type":"uint256"}],"name":"RolesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalCollateral","type":"uint256"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"UnlockCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"Unwind","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum VaultConfig","name":"_type","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"_config","type":"uint256"}],"name":"VaultConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"withdrawableAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowFees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"premium","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"underlying","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"rewards","type":"uint256[]"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_depositId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"_setAs","type":"bool"}],"name":"rolloverDepositSet","type":"event"},{"inputs":[{"internalType":"enum Contracts","name":"","type":"uint8"}],"name":"addresses","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"tickSize","type":"uint256"}],"name":"bootstrap","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_collateralAccess","type":"uint256"},{"internalType":"uint256","name":"_entryTimestamp","type":"uint256"}],"name":"calculateFundingFees","outputs":[{"internalType":"uint256","name":"fees","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calculatePnl","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_strike","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"calculatePremium","outputs":[{"internalType":"uint256","name":"premium","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calculatePurchaseFees","outputs":[{"internalType":"uint256","name":"finalFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"currentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxStrike","type":"uint256"},{"internalType":"uint256","name":"_liquidity","type":"uint256"},{"internalType":"address","name":"_user","type":"address"},{"internalType":"bool","name":"_rollover","type":"bool"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"depositId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositPositionsCounter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"bool","name":"transferNative","type":"bool"}],"name":"emergencyWithdraw","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"expireEpoch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_positionId","type":"uint256"}],"name":"getDepositPosition","outputs":[{"components":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"checkpoint","type":"uint256"},{"internalType":"address","name":"depositor","type":"address"},{"internalType":"bool","name":"rollover","type":"bool"}],"internalType":"struct DepositPosition","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"},{"internalType":"uint256","name":"_maxStrike","type":"uint256"}],"name":"getEpochCheckpoints","outputs":[{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"unlockedCollateral","type":"uint256"},{"internalType":"uint256","name":"premiumAccrued","type":"uint256"},{"internalType":"uint256","name":"borrowFeesAccrued","type":"uint256"},{"internalType":"uint256","name":"underlyingAccrued","type":"uint256"},{"internalType":"uint256","name":"totalLiquidity","type":"uint256"},{"internalType":"uint256","name":"liquidityBalance","type":"uint256"},{"internalType":"uint256","name":"activeCollateral","type":"uint256"}],"internalType":"struct Checkpoint[]","name":"_checkpoints","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"getEpochData","outputs":[{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"expiryTime","type":"uint256"},{"internalType":"uint256","name":"totalLiquidity","type":"uint256"},{"internalType":"uint256","name":"totalActiveCollateral","type":"uint256"},{"internalType":"uint256","name":"fundingRate","type":"uint256"},{"internalType":"uint256","name":"tickSize","type":"uint256"},{"components":[{"internalType":"uint256","name":"highest","type":"uint256"},{"internalType":"uint256","name":"lowest","type":"uint256"}],"internalType":"struct MaxStrikesRange","name":"maxStrikesRange","type":"tuple"},{"internalType":"enum EpochState","name":"state","type":"uint8"}],"internalType":"struct EpochData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"},{"internalType":"uint256","name":"_maxStrike","type":"uint256"},{"internalType":"uint256","name":"_checkpoint","type":"uint256"}],"name":"getEpochMaxStrikeCheckpoint","outputs":[{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"unlockedCollateral","type":"uint256"},{"internalType":"uint256","name":"premiumAccrued","type":"uint256"},{"internalType":"uint256","name":"borrowFeesAccrued","type":"uint256"},{"internalType":"uint256","name":"underlyingAccrued","type":"uint256"},{"internalType":"uint256","name":"totalLiquidity","type":"uint256"},{"internalType":"uint256","name":"liquidityBalance","type":"uint256"},{"internalType":"uint256","name":"activeCollateral","type":"uint256"}],"internalType":"struct Checkpoint","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"},{"internalType":"uint256","name":"_maxStrike","type":"uint256"}],"name":"getEpochMaxStrikeData","outputs":[{"internalType":"uint256","name":"activeCollateral","type":"uint256"},{"internalType":"uint256[]","name":"rewardRates","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"getEpochRewards","outputs":[{"components":[{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"internalType":"struct EpochRewards","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"getEpochStrikes","outputs":[{"internalType":"uint256[]","name":"maxStrikes","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"getEpochTickSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_positionId","type":"uint256"}],"name":"getOptionsPurchase","outputs":[{"components":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"optionStrike","type":"uint256"},{"internalType":"uint256","name":"optionsAmount","type":"uint256"},{"internalType":"uint256","name":"unlockEntryTimestamp","type":"uint256"},{"internalType":"uint256[]","name":"strikes","type":"uint256[]"},{"internalType":"uint256[]","name":"checkpoints","type":"uint256[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"},{"internalType":"enum OptionsState","name":"state","type":"uint8"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"delegate","type":"address"}],"internalType":"struct OptionsPurchase","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_purchaseId","type":"uint256"}],"name":"getOptionsState","outputs":[{"internalType":"enum OptionsState","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_optionStrike","type":"uint256"},{"internalType":"uint256","name":"_optionsAmount","type":"uint256"}],"name":"getUnwindAmount","outputs":[{"internalType":"uint256","name":"unwindAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUsdPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_strike","type":"uint256"}],"name":"getVolatility","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_depositId","type":"uint256"}],"name":"getWithdrawable","outputs":[{"internalType":"uint256","name":"depositAmount","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"borrowFees","type":"uint256"},{"internalType":"uint256","name":"underlying","type":"uint256"},{"internalType":"uint256[]","name":"rewards","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"grantRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAllRoles","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAnyRole","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isWithinBlackoutWindow","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"ordinalsFromRoles","outputs":[{"internalType":"uint8[]","name":"ordinals","type":"uint8[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownershipHandoverValidFor","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_strike","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_delegate","type":"address"},{"internalType":"address","name":"_account","type":"address"}],"name":"purchase","outputs":[{"internalType":"uint256","name":"purchaseId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"purchasePositionsCounter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"purchaseId","type":"uint256"},{"internalType":"uint256","name":"relockAmount","type":"uint256"}],"name":"relockCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"renounceRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"revokeRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"ordinals","type":"uint8[]"}],"name":"rolesFromOrdinals","outputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"rolesOf","outputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_depositIds","type":"uint256[]"},{"internalType":"address","name":"_feeReceiver","type":"address"}],"name":"rolloverDeposits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum Contracts[]","name":"_types","type":"uint8[]"},{"internalType":"address[]","name":"_addresses","type":"address[]"}],"name":"setAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_depositId","type":"uint256"},{"internalType":"bool","name":"_setAs","type":"bool"}],"name":"setDepositRollover","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"setEpochRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum VaultConfig[]","name":"_types","type":"uint8[]"},{"internalType":"uint256[]","name":"_configs","type":"uint256[]"}],"name":"setVaultConfigs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"strikeMulAmount","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"purchaseId","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"unlockCollateral","outputs":[{"internalType":"uint256","name":"unlockedCollateral","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"purchaseId","type":"uint256"},{"internalType":"uint256","name":"unwindAmount","type":"uint256"}],"name":"unwind","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum VaultConfig","name":"","type":"uint8"}],"name":"vaultConfig","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"depositIds","type":"uint256[]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a06040526001805560006002553480156200001a57600080fd5b50600c805460ff191690556001600d556040805163313ce56760e01b8152905173ff970a61a04b1ca14834a43f5de4533ebddb5cc891829163313ce567916004808201926020929091908290030181865afa1580156200007e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000a491906200016c565b60ff16608052620000b533620000e3565b620000c233600162000121565b620000cf33600262000121565b620000dc33600462000121565b5062000198565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b638b78c6d8600c52816000526020600c208181541791508181555080600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a35050565b6000602082840312156200017f57600080fd5b815160ff811681146200019157600080fd5b9392505050565b608051615edb620001c9600039600081816119a9015281816120ec015281816133f6015261422b0152615edb6000f3fe6080604052600436106103805760003560e01c806376671808116101d1578063a750a58511610102578063d2a33ea9116100a0578063e4d2e8471161006f578063e4d2e84714610afb578063f04e283e14610b1b578063f2fde38b14610b2e578063fee81cf414610b4157600080fd5b8063d2a33ea914610a69578063d5fc893e14610a9a578063d7533f0214610ac8578063dfe31b2714610ae657600080fd5b8063bd731b58116100dc578063bd731b58146109dc578063c189c19b146109fc578063cf587fa314610a1c578063d1d6b50b14610a3c57600080fd5b8063a750a5851461097c578063a7bf1b6c1461099c578063b8f4bd7b146109bc57600080fd5b806389518c871161016f578063902c81db11610149578063902c81db146108e25780639437e133146109025780639d186fb31461092f578063a6e67cae1461094f57600080fd5b806389518c87146108895780638b25096a146108a95780638da5cb5b146108c957600080fd5b80638456cb59116101ab5780638456cb591461080757806384ff818c1461081c578063859e7d321461083c57806388e18e131461086957600080fd5b806376671808146107bb5780637c4b52cb146107d15780637e0d0974146107f157600080fd5b806341022fd2116102b657806354d1f13d116102545780636db29f6d116102235780636db29f6d14610744578063715018a61461075957806371dd5300146107615780637359e41f1461078e57600080fd5b806354d1f13d146106d75780635c975abb146106df5780635f0c0f2b146106f7578063695f3f5d1461072457600080fd5b80634a59c3c7116102905780634a59c3c7146105f45780634ff9a4e814610625578063514e62fc1461065257806353459cca1461068957600080fd5b806341022fd2146105a157806343eea6a1146105c15780634a4ee7b1146105e157600080fd5b80631c10893f116103235780632de94807116102fd5780632de94807146105235780633130a4d3146105565780633c1fc384146105765780633f4ba83a1461058c57600080fd5b80631c10893f146104c15780631cd64df4146104d4578063256929621461051b57600080fd5b806313a661ed1161035f57806313a661ed1461044c57806315943b991461046c578063183a4f6e146104995780631acf5582146104ac57600080fd5b806212100c1461038557806302cade38146103b8578063134fc6011461042a575b600080fd5b34801561039157600080fd5b506103a56103a03660046153d4565b610b74565b6040519081526020015b60405180910390f35b3480156103c457600080fd5b506103d86103d33660046153f6565b610c63565b6040516103af9190815181526020808301519082015260408083015190820152606080830151908201526080808301516001600160a01b03169082015260a09182015115159181019190915260c00190565b34801561043657600080fd5b5061044a610445366004615454565b610cd5565b005b34801561045857600080fd5b506103a56104673660046154ec565b610e71565b34801561047857600080fd5b5061048c6104873660046153f6565b610e9a565b6040516103af91906155ec565b61044a6104a73660046153f6565b610f5e565b3480156104b857600080fd5b506103a5610f6b565b61044a6104cf366004615616565b61106b565b3480156104e057600080fd5b5061050b6104ef366004615616565b638b78c6d8600c90815260009290925260209091205481161490565b60405190151581526020016103af565b61044a611081565b34801561052f57600080fd5b506103a561053e366004615640565b638b78c6d8600c908152600091909152602090205490565b34801561056257600080fd5b5061044a61057136600461565b565b6110d1565b34801561058257600080fd5b506103a560015481565b34801561059857600080fd5b5061044a611283565b3480156105ad57600080fd5b5061044a6105bc366004615454565b611296565b3480156105cd57600080fd5b5061044a6105dc3660046156cf565b6113af565b61044a6105ef366004615616565b6117cc565b34801561060057600080fd5b5061061461060f3660046153f6565b6117de565b6040516103af959493929190615723565b34801561063157600080fd5b506106456106403660046153f6565b611b0d565b6040516103af9190615783565b34801561065e57600080fd5b5061050b61066d366004615616565b638b78c6d8600c90815260009290925260209091205416151590565b34801561069557600080fd5b506106bf6106a4366004615852565b6004602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016103af565b61044a611cb8565b3480156106eb57600080fd5b50600c5460ff1661050b565b34801561070357600080fd5b506107176107123660046153f6565b611cf4565b6040516103af9190615873565b34801561073057600080fd5b506103a561073f3660046158ed565b611dd3565b34801561075057600080fd5b5061044a611e4b565b61044a611f2e565b34801561076d57600080fd5b506103a561077c366004615933565b60036020526000908152604090205481565b34801561079a57600080fd5b506107ae6107a93660046153f6565b611f42565b6040516103af9190615954565b3480156107c757600080fd5b506103a560005481565b3480156107dd57600080fd5b5061050b6107ec36600461599b565b611f7b565b3480156107fd57600080fd5b506103a560025481565b34801561081357600080fd5b5061044a6120d4565b34801561082857600080fd5b506103a56108373660046153d4565b6120e7565b34801561084857600080fd5b5061085c6108573660046153f6565b612148565b6040516103af91906159e6565b34801561087557600080fd5b506103a5610884366004615a5f565b6121fc565b34801561089557600080fd5b506103a56108a43660046153d4565b61223c565b3480156108b557600080fd5b506103a56108c4366004615a8b565b6122e5565b3480156108d557600080fd5b50638b78c6d819546106bf565b3480156108ee57600080fd5b5061044a6108fd366004615ab7565b6127a0565b34801561090e57600080fd5b5061092261091d3660046153d4565b612830565b6040516103af9190615b28565b34801561093b57600080fd5b506103a561094a3660046153d4565b612971565b34801561095b57600080fd5b5061096f61096a366004615a5f565b6129a5565b6040516103af9190615b6b565b34801561098857600080fd5b5061044a6109973660046153d4565b612a2d565b3480156109a857600080fd5b506103a56109b7366004615b7a565b612e39565b3480156109c857600080fd5b5061044a6109d73660046156cf565b6130d6565b3480156109e857600080fd5b506103a56109f7366004615bb5565b6133e1565b348015610a0857600080fd5b506103a5610a173660046153f6565b6134c5565b348015610a2857600080fd5b5061044a610a373660046153d4565b6135ae565b348015610a4857600080fd5b50610a5c610a573660046153f6565b613974565b6040516103af9190615be8565b348015610a7557600080fd5b506103a5610a843660046153f6565b6000908152600560208190526040909120015490565b348015610aa657600080fd5b50610aba610ab53660046153d4565b6139bd565b6040516103af929190615bf6565b348015610ad457600080fd5b506040516202a30081526020016103af565b348015610af257600080fd5b5061050b613a97565b348015610b0757600080fd5b5061050b610b163660046153d4565b613af8565b61044a610b29366004615640565b613d2f565b61044a610b3c366004615640565b613d6c565b348015610b4d57600080fd5b506103a5610b5c366004615640565b63389a75e1600c908152600091909152602090205490565b600080610b7f610f6b565b7f1a1e6821cde7d0159c0d293177871e09677b4e42307c7db3ba94f8648a5a050f54600080548152600560205260409020600190810154929350610c5b926001600160a01b0390921691635b7b6d8891908886610bdb826134c5565b6040516001600160e01b031960e088901b1681529415156004860152602485019390935260448401919091526064830152608482015260a401602060405180830381865afa158015610c31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c559190615c0f565b846120e7565b949350505050565b610c6b615211565b50600090815260076020908152604091829020825160c08101845281548152600182015492810192909252600281015492820192909252600382015460608201526004909101546001600160a01b0381166080830152600160a01b900460ff16151560a082015290565b6001610ce081613d93565b610ced8483146000613db9565b60005b84811015610e6957610d366000858584818110610d0f57610d0f615c28565b9050602002016020810190610d249190615640565b6001600160a01b031614156001613db9565b838382818110610d4857610d48615c28565b9050602002016020810190610d5d9190615640565b60046000888885818110610d7357610d73615c28565b9050602002016020810190610d889190615852565b6007811115610d9957610d99615759565b6007811115610daa57610daa615759565b815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055507fd62439407fa4dc0b1a83747f5f139b50c16c44dab05e472e1cdeb0f3900fb2b1868683818110610e0f57610e0f615c28565b9050602002016020810190610e249190615852565b858584818110610e3657610e36615c28565b9050602002016020810190610e4b9190615640565b604051610e59929190615c3e565b60405180910390a1600101610cf0565b505050505050565b6000815160051b5b8015610e9457828101516001901b90911790601f1901610e79565b50919050565b6000818152600e602052604090205460609067ffffffffffffffff811115610ec457610ec46154c0565b604051908082528060200260200182016040528015610eed578160200160208202803683370190505b506000838152600560205260408120600601549192505b8115610f575781838281518110610f1d57610f1d615c28565b602090810291909101015280610f3281615c80565b6000868152600e60205260409020909250610f4e915083613dde565b9250610f049050565b5050919050565b610f683382613df9565b50565b6000610f796008601e615c99565b610f8490600a615d90565b600460208181527f04cde762ef08b6b6c5ded8e8c4c0b3f4e5c9ad7342c88fcc93681b4588b73f0554600160009081527fabd6e7cb50984ff9c2f3e18a2660c3353dadf4e3291deeb275dae2cd1e44fe0554604080516317e1d38560e11b81526001600160a01b0392831696810196909652602486018390526044860183905260648601929092529051911692632fc3a70a9260848083019391928290030181865afa158015611038573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105c9190615c0f565b6110669190615db2565b905090565b611073613e48565b61107d8282613e63565b5050565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b60016110dc81613d93565b6110e98584146000613db9565b60005b8581101561127a5761113e87878381811061110957611109615c28565b905060200201602081019061111e9190615640565b333088888681811061113257611132615c28565b90506020020135613eae565b600083815260066020526040902087878381811061115e5761115e615c28565b90506020020160208101906111739190615640565b8154600180820184556000938452602080852090920180546001600160a01b0319166001600160a01b039490941693909317909255858352600690526040909120018585838181106111c7576111c7615c28565b835460018101855560009485526020948590209190940292909201359190920155507f7ed061f2bc8591b4b7f3d75c0e045beef7e1f5d4ce3d3b861b641ab0e70cf5b78386868481811061121d5761121d615c28565b9050602002013589898581811061123657611236615c28565b905060200201602081019061124b9190615640565b6040805193845260208401929092526001600160a01b03169082015260600160405180910390a16001016110ec565b50505050505050565b600161128e81613d93565b610f68613ec0565b60016112a181613d93565b6112ae8483146000613db9565b60005b84811015610e69578383828181106112cb576112cb615c28565b90506020020135600360008888858181106112e8576112e8615c28565b90506020020160208101906112fd9190615933565b600581111561130e5761130e615759565b600581111561131f5761131f615759565b81526020810191909152604001600020557f313d1112b36b5f39a16a758ed9c767d12e0e9b3e3d3456700e5f83aa9abc3f2a86868381811061136357611363615c28565b90506020020160208101906113789190615933565b85858481811061138a5761138a615c28565b9050602002013560405161139f929190615dc6565b60405180910390a16001016112b1565b6113b7615211565b606060008060008060005b888110156117c057600760008b8b848181106113e0576113e0615c28565b60209081029290920135835250818101929092526040908101600020815160c0810183528154815260018201549381019390935260028101549183019190915260038101546060830152600401546001600160a01b0381166080830152600160a01b900460ff1615801560a08301529097506117b85761148b6002885160009081526005602052604090206008015460ff16600381111561148357611483615759565b146004613db9565b61149e6000548860000151106015613db9565b6114bf8a8a838181106114b3576114b3615c28565b905060200201356117de565b99509197509195509093509150600760008b8b848181106114e2576114e2615c28565b60209081029290920135835250810191909152604001600090812081815560018101829055600280820183905560038201839055600490910180546001600160a81b031916905560808901518691629896809161153f9190613f08565b61154c9062989680615de4565b6115569088615df7565b6115609190615db2565b61156a9190615c99565b600560005260036020527f405aad32e1adbac89bb7f176e338b8fc6e994ca210c9bb7bdca249b465942250549091508111156115ce5750600560005260036020527f405aad32e1adbac89bb7f176e338b8fc6e994ca210c9bb7bdca249b465942250545b6115f4600054896020015183886115e59190615c99565b8b608001518c60a00151613ff6565b50821561164057600160005260046020527fabd6e7cb50984ff9c2f3e18a2660c3353dadf4e3291deeb275dae2cd1e44fe05546080890151611640916001600160a01b031690856140d1565b61164a8487615de4565b1561168a57600080526004602052600080516020615e8683398151915254608089015161168a916001600160a01b031690611685878a615de4565b6140d1565b60005b8751811015611728578781815181106116a8576116a8615c28565b6020026020010151600014611720578851600090815260066020526040902080546117209190839081106116de576116de615c28565b9060005260206000200160009054906101000a90046001600160a01b03168a608001518a848151811061171357611713615c28565b60200260200101516140d1565b60010161168d565b50600080526004602052600080516020615e8683398151915254611756906001600160a01b03168a836140d1565b7f58e36523aa3ba8011c478b3f463bf4fa154a4c2b0084b7e43f43d076184629358b8b8481811061178957611789615c28565b9050602002013589608001516000878a888d6040516117ae9796959493929190615e0e565b60405180910390a1505b6001016113c2565b50505050505050505050565b6117d4613e48565b61107d8282613df9565b6000818152600760209081526040808320815160c081018352815480825260018301548286019081526002808501548487015260038501546060808601919091526004909501546001600160a01b0381166080860152600160a01b900460ff16151560a085015291875260098652848720905187529094529184209092015483928392839267ffffffffffffffff81111561187b5761187b6154c0565b6040519080825280602002602001820160405280156118a4578160200160208202803683370190505b5081516000908152600960209081526040808320828601518452825291829020600201805483518184028101840190945280845293955091929083018282801561190d57602002820191906000526020600020905b8154815260200190600101908083116118f9575b50508451600090815260096020908152604080832082890151845282528083206060808a015185526003918201845282852083516101008101855281548152600182015495810195909552600281015493850193909352908201549083015260048101546080830152600581015460a0830152600681015460c08301526007015460e0820152949650925050505b8351811015611a45576119cf7f0000000000000000000000000000000000000000000000000000000000000000600a615d90565b8482815181106119e1576119e1615c28565b60200260200101518360a001518460e001518660400151611a029190615df7565b611a0c9190615db2565b611a169190615df7565b611a209190615db2565b848281518110611a3257611a32615c28565b602090810291909101015260010161199b565b508060a0015181606001518360400151611a5f9190615df7565b611a699190615db2565b611a739086615de4565b94508060a0015181604001518360400151611a8e9190615df7565b611a989190615db2565b611aa29087615de4565b95508060a0015181608001518360400151611abd9190615df7565b611ac79190615db2565b611ad19085615de4565b93508060a001518160c001518360400151611aec9190615df7565b611af69190615db2565b611b009088615de4565b9650505091939590929450565b611b15615252565b60008281526008602090815260409182902082516101408101845281548152600182015481840152600282015481850152600382015460608201526004820180548551818602810186019096528086529194929360808601939290830182828015611b9f57602002820191906000526020600020905b815481526020019060010190808311611b8b575b5050505050815260200160058201805480602002602001604051908101604052809291908181526020018280548015611bf757602002820191906000526020600020905b815481526020019060010190808311611be3575b5050505050815260200160068201805480602002602001604051908101604052809291908181526020018280548015611c4f57602002820191906000526020600020905b815481526020019060010190808311611c3b575b5050509183525050600782015460209091019060ff166002811115611c7657611c76615759565b6002811115611c8757611c87615759565b815260078201546001600160a01b036101009091048116602083015260089092015490911660409091015292915050565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b6040805180820182526060808252602080830182905260008581526006825284902084518154928302810184018652948501828152939493909284928491840182828015611d6b57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611d4d575b5050505050815260200160018201805480602002602001604051908101604052809291908181526020018280548015611dc357602002820191906000526020600020905b815481526020019060010190808311611daf575b5050505050815250509050919050565b60006002600d5403611df85760405163414f764b60e11b815260040160405180910390fd5b6002600d55600054611e0d9086868686613ff6565b600080526004602052600080516020615e8683398151915254909150611e3e906001600160a01b0316333087613eae565b6001600d55949350505050565b6002600d5403611e6e5760405163414f764b60e11b815260040160405180910390fd5b6002600d81905560005490611eac9060008381526005602052604090206008015460ff166003811115611ea357611ea3615759565b14156011613db9565b600081815260056020526040902060010154611ecc428211156012613db9565b611ed5826140e1565b600082815260056020908152604091829020600801805460ff1916600217905590513381527f6a4de20bb9fa8fea199f1022f29eff6be1752c446674d16913f2afc2b3c5a8a5910160405180910390a150506001600d55565b611f36613e48565b611f4060006142e8565b565b604051602081016000835b81835260051b6020169091019060010183811c80611f4d575050601f198282030160051c8252604052919050565b60006001611f8881613d93565b611f90614326565b8215611fc45760405133904780156108fc02916000818181858888f19350505050158015611fc2573d6000803e3d6000fd5b505b60005b848110156120935761208b868683818110611fe457611fe4615c28565b9050602002016020810190611ff99190615640565b3388888581811061200c5761200c615c28565b90506020020160208101906120219190615640565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015612067573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116859190615c0f565b600101611fc7565b506040513381527f5e7b34819cd91b239220bec92fcfd3c10da2214ba13e4e2b1f6c9cfdbd68a9a29060200160405180910390a1600191505b509392505050565b60016120df81613d93565b610f68614349565b6000807f000000000000000000000000000000000000000000000000000000000000000061211760126008615de4565b6121219190615c99565b905061212e81600a615d90565b6121388486615df7565b610c5b9190615db2565b92915050565b6121506152b6565b600082815260056020818152604092839020835161010081018552815481526001820154818401526002820154818601526003808301546060830152600483015460808301529382015460a082015284518086019095526006820154855260078201549285019290925260c0820193909352600883015490929160e084019160ff16908111156121e2576121e2615759565b60038111156121f3576121f3615759565b90525092915050565b6000836000036122115761220e610f6b565b93505b83831161221f576000612232565b61223261222c8585615c99565b836120e7565b90505b9392505050565b7fcbc4e5fb02c3d1de23a9f1e014b4d2ee5aeaea9505df5e855c9210bf472495af547fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d5460008054815260056020526040812060010154909291906122a2908590615c99565b6122ac9190615db2565b6122b69190615df7565b905082629896806122c78382615de4565b6122d19086615df7565b6122db9190615db2565b6122359190615c99565b60006002600d540361230a5760405163414f764b60e11b815260040160405180910390fd5b6002600d81905561231a81613d93565b61232261438e565b6123376123306000546143b2565b6007613db9565b600084815260086020908152604080832081516101408101835281548152600182015481850152600282015481840152600382015460608201526004820180548451818702810187019095528085529194929360808601939092908301828280156123c157602002820191906000526020600020905b8154815260200190600101908083116123ad575b505050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801561241957602002820191906000526020600020905b815481526020019060010190808311612405575b505050505081526020016006820180548060200260200160405190810160405280929190818152602001828054801561247157602002820191906000526020600020905b81548152602001906001019080831161245d575b5050509183525050600782015460209091019060ff16600281111561249857612498615759565b60028111156124a9576124a9615759565b815260078201546001600160a01b0361010090910481166020808401919091526008909301541660409182015290820151908201519192506124ea916120e7565b9250612510336001600160a01b03168261012001516001600160a01b0316146009613db9565b612537600161251e87613974565b600281111561252f5761252f615759565b14600a613db9565b600260e0820152426060820181905260009061255490859061223c565b905060005b82608001515181101561262f5761262783600001518460800151838151811061258457612584615c28565b6020026020010151670de0b6b3a7640000888760c0015186815181106125ac576125ac615c28565b60200260200101516125be9190615df7565b6125c89190615db2565b670de0b6b3a7640000868860c0015187815181106125e8576125e8615c28565b60200260200101516125fa9190615df7565b6126049190615db2565b8760a00151868151811061261a5761261a615c28565b60200260200101516143fd565b600101612559565b5060008681526008602090815260409182902084518155818501516001820155918401516002830155606084015160038301556080840151805185939261267d926004850192910190615315565b5060a08201518051612699916005840191602090910190615315565b5060c082015180516126b5916006840191602090910190615315565b5060e082015160078201805460ff191660018360028111156126d9576126d9615759565b021790555061010082810151600783018054610100600160a81b0319166001600160a01b039283169093029290921790915561012090920151600890910180546001600160a01b031916919092161790556127646004600080600781111561274357612743615759565b81526020810191909152604001600020546001600160a01b031686866140d1565b600080526004602052600080516020615e8683398151915254612792906001600160a01b0316333084613eae565b50506001600d555092915050565b6000828152600760205260409020600401546127c8906001600160a01b031633146010613db9565b600082815260076020908152604091829020600401805460ff60a01b1916600160a01b851515908102919091179091558251858152918201527f94d4ebac570b7e51b033a8ea25e744f5f3cabf1f43aeda01a5ef3cf37c54848a910160405180910390a15050565b6000828152600a6020908152604080832084845290915290205460609067ffffffffffffffff811115612865576128656154c0565b60405190808252806020026020018201604052801561289e57816020015b61288b615360565b8152602001906001900390816128835790505b50905060005b6000848152600a6020908152604080832086845290915290205481101561296a576000848152600960209081526040808320868452825280832084845260039081018352928190208151610100810183528154815260018201549381019390935260028101549183019190915291820154606082015260048201546080820152600582015460a0820152600682015460c082015260079091015460e0820152825183908390811061295757612957615c28565b60209081029190910101526001016128a4565b5092915050565b600061297b610f6b565b831015610e945761298a610f6b565b6129948484615df7565b61299e9190615db2565b9050612142565b6129ad615360565b506000928352600960209081526040808520938552928152828420918452600391820181529282902082516101008101845281548152600182015494810194909452600281015492840192909252810154606083015260048101546080830152600581015460a0830152600681015460c08301526007015460e082015290565b6002612a3881613d93565b612a4061438e565b612a4e6123306000546143b2565b60008381526008602090815260408083208151610140810183528154815260018201548185015260028201548184015260038201546060820152600482018054845181870281018701909552808552919492936080860193909290830182828015612ad857602002820191906000526020600020905b815481526020019060010190808311612ac4575b5050505050815260200160058201805480602002602001604051908101604052809291908181526020018280548015612b3057602002820191906000526020600020905b815481526020019060010190808311612b1c575b5050505050815260200160068201805480602002602001604051908101604052809291908181526020018280548015612b8857602002820191906000526020600020905b815481526020019060010190808311612b74575b5050509183525050600782015460209091019060ff166002811115612baf57612baf615759565b6002811115612bc057612bc0615759565b815260078201546001600160a01b03610100909104811660208301526008909201548216604090910152610120820151919250612c01911633146009613db9565b612c1c60028260e00151600281111561252f5761252f615759565b6000612c3082602001518360400151612971565b90506000612c46836020015184604001516120e7565b905060005b836080015151811015612d2f57612d27846000015185608001518381518110612c7657612c76615c28565b6020026020010151670de0b6b3a76400008760c001518581518110612c9d57612c9d615c28565b6020026020010151878b11612cb2578a612cb4565b875b612cbe9190615df7565b612cc89190615db2565b670de0b6b3a76400008860c001518681518110612ce757612ce7615c28565b602002602001015187612cfa9190615df7565b612d049190615db2565b8860a001518681518110612d1a57612d1a615c28565b60200260200101516144fc565b600101612c4b565b5081851115612d8057612d806004600060015b6007811115612d5357612d53615759565b81526020810191909152604001600020546101008501516001600160a01b03909116906116858589615c99565b612dbd6004600060015b6007811115612d9b57612d9b615759565b81526020810191909152604001600020546001600160a01b0316333088613eae565b600086815260086020526040812081815560018101829055600281018290556003810182905590612df160048301826153a5565b612dff6005830160006153a5565b612e0d6006830160006153a5565b506007810180546001600160a81b031916905560080180546001600160a01b0319169055505050505050565b60006002600d5403612e5e5760405163414f764b60e11b815260040160405180910390fd5b6002600d819055612e6e81613d93565b612e7661438e565b612e89612e81613a97565b156014613db9565b600054612e98612330826143b2565b612eae6001600160a01b03851615156001613db9565b612eba878783886145b8565b6000612ec688886120e7565b600083815260056020526040902060020154909150612ee990821115600b613db9565b6000612ef58989610b74565b90506000612f04878b8b6133e1565b9050612f8887898c8c8860018054600081815260086020819052604090912060078101805492820180546001600160a01b0319166001600160a01b039a8b16179055818501979097556002810195909555929093556001600160a81b031990911660ff19610100969095169590950293909316939093178217905580548101905590565b9550612f97848b85858a61460e565b60008481526005602052604081206002018054859290612fb8908490615c99565b909155505060008481526005602052604081206003018054859290612fde908490615de4565b9091555050600080526004602052600080516020615e8683398151915254613011906001600160a01b0316333085613eae565b6004602052600080516020615e868339815191525460026000527f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a754613066916001600160a01b039081169133911684613eae565b6040805185815260208101889052908101839052606081018290526001600160a01b03881660808201523360a08201527f9039a4c733a9179a327c244f2d657df5bc5bcafb45e79a5e155b24ea8ad26b249060c00160405180910390a150506001600d5550919695505050505050565b6002600d54036130f95760405163414f764b60e11b815260040160405180910390fd5b6002600d5561310661438e565b6000606060008060008060005b888110156133d057600760008b8b8481811061313157613131615c28565b90506020020135815260200190815260200160002060000154965061319d336001600160a01b0316600760008d8d8681811061316f5761316f615c28565b60209081029290920135835250810191909152604001600020600401546001600160a01b0316146010613db9565b6131c8600260008981526005602052604090206008015460ff16600381111561148357611483615759565b6131dd8a8a838181106114b3576114b3615c28565b99509197509195509093509150600760008b8b8481811061320057613200615c28565b6020908102929092013583525081019190915260400160009081208181556001810182905560028101829055600381019190915560040180546001600160a81b0319169055811561328b57600160005260046020527fabd6e7cb50984ff9c2f3e18a2660c3353dadf4e3291deeb275dae2cd1e44fe055461328b906001600160a01b031689846140d1565b826132968587615de4565b6132a09190615de4565b156132e157600080526004602052600080516020615e86833981519152546132e1906001600160a01b031689856132d7888a615de4565b6116859190615de4565b60005b865181101561336c578681815181106132ff576132ff615c28565b6020026020010151600014613364576000888152600660205260409020805461336491908390811061333357613333615c28565b9060005260206000200160009054906101000a90046001600160a01b03168a89848151811061171357611713615c28565b6001016132e4565b507f58e36523aa3ba8011c478b3f463bf4fa154a4c2b0084b7e43f43d076184629358a8a838181106133a0576133a0615c28565b9050602002013589868689878c6040516133c09796959493929190615e0e565b60405180910390a1600101613113565b50506001600d555050505050505050565b6000806133ef600086613f08565b905061341c7f00000000000000000000000000000000000000000000000000000000000000006012615c99565b61342790600a615d90565b83629896806134368482615de4565b6134409087615df7565b61344a9190615db2565b6134549190615c99565b61345e9190615db2565b915083613469610f6b565b10156120cc576000613479610f6b565b6134856008600a615d90565b61348f9087615df7565b6134999190615db2565b90506134a76008600a615d90565b6134b18285615df7565b6134bb9190615db2565b9695505050505050565b600080805260036020527f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff5462989680906135009082615de4565b6006600052600460208181527fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f546040805163c189c19b60e01b8152938401889052516001600160a01b039091169263c189c19b9260248083019391928290030181865afa158015613576573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061359a9190615c0f565b6135a49190615df7565b6121429190615db2565b60026135b981613d93565b6135c161438e565b6135cf6123306000546143b2565b6000838152600860209081526040808320815161014081018352815481526001820154818501526002820154818401526003820154606082015260048201805484518187028101870190955280855291949293608086019390929083018282801561365957602002820191906000526020600020905b815481526020019060010190808311613645575b50505050508152602001600582018054806020026020016040519081016040528092919081815260200182805480156136b157602002820191906000526020600020905b81548152602001906001019080831161369d575b505050505081526020016006820180548060200260200160405190810160405280929190818152602001828054801561370957602002820191906000526020600020905b8154815260200190600101908083116136f5575b5050509183525050600782015460209091019060ff16600281111561373057613730615759565b600281111561374157613741615759565b815260078201546001600160a01b03610100909104811660208301526008909201548216604090910152610120820151919250613782911633146009613db9565b6137a560028260e00151600281111561379d5761379d615759565b14600d613db9565b60006137b9826020015183604001516120e7565b905060006137cb82846060015161223c565b90506137d7824261223c565b6137e19082615c99565b6137eb9082615c99565b90508482111561381457506000858152600860205260408120600701805460ff19169055613831565b6000868152600860205260409020600701805460ff191660011790555b60005b8360800151518110156139185761391084600001518560800151838151811061385f5761385f615c28565b6020026020010151670de0b6b3a76400008760c00151858151811061388657613886615c28565b6020026020010151878b1161389b578a61389d565b875b6138a79190615df7565b6138b19190615db2565b670de0b6b3a76400008860c0015186815181106138d0576138d0615c28565b6020026020010151876138e39190615df7565b6138ed9190615db2565b8860a00151868151811061390357613903615c28565b60200260200101516146c3565b600101613834565b508482101561392e5761392e6004600080612d42565b61393b6004600080612d8a565b8015610e6957600080526004602052600080516020615e8683398151915254610100840151610e69916001600160a01b031690836140d1565b600081815260086020908152604080832054808452600590925282206001015442106139a35750600092915050565b505060009081526008602052604090206007015460ff1690565b60008281526009602090815260408083208484529091529020600181015460029091015460609067ffffffffffffffff8111156139fc576139fc6154c0565b604051908082528060200260200182016040528015613a25578160200160208202803683370190505b5060008581526009602090815260408083208784528252918290206002018054835181840281018401909452808452939450919290830182828015613a8957602002820191906000526020600020905b815481526020019060010190808311613a75575b505050505090509250929050565b60008054815260056020526040812060010154808203613ab957600091505090565b600160005260036020527fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c54613aef9082615c99565b42101591505090565b60006002600d5403613b1d5760405163414f764b60e11b815260040160405180910390fd5b6002600d556004613b2d81613d93565b613b3a4285116002613db9565b613b478315156003613db9565b60008054613b56906001615de4565b6000818152600560208181526040808420815161010081018352815481526001820154818501526002820154818401526003808301546060830152600483015460808301529482015460a082015282518084019093526006820154835260078201549383019390935260c08301919091526008810154949550929390929160e084019160ff1690811115613bec57613bec615759565b6003811115613bfd57613bfd615759565b90525060005490915015613c4757613c47600260056000613c1f600187615c99565b815260208101919091526040016000206008015460ff16600381111561148357611483615759565b42815260a081018581526020808301888152600160e08501818152600087815587815260058086526040918290208851815594518585015590870151600285015560608701516003808601919091556080880151600486015595519084015560c0860151805160068501559093015160078301559151600882018054869593949293919260ff19909116918490811115613ce357613ce3615759565b0217905550506040518381527fb5ca1ca1b7b47549eb8af476f3ef702fc63bcd8b8c01dc163b009bb818f97997915060200160405180910390a1600193505050506001600d5592915050565b613d37613e48565b63389a75e1600c52806000526020600c208054421115613d5f57636f5e88186000526004601cfd5b60009055610f68816142e8565b613d74613e48565b8060601b613d8a57637448fbae6000526004601cfd5b610f68816142e8565b638b78c6d8600c5233600052806020600c205416610f68576382b429006000526004601cfd5b8161107d57604051636f9963a160e11b81526004810182905260240160405180910390fd5b600080613ded848460016147bb565b915091505b9250929050565b638b78c6d8600c52816000526020600c20805482811681189250508181555080600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a35050565b638b78c6d819543314611f40576382b429006000526004601cfd5b638b78c6d8600c52816000526020600c208181541791508181555080600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a35050565b613eba84848484614802565b50505050565b613ec8614326565b600c805460ff191690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b7f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa0546004600090815260036020527f83ec6a1f0257b830b5e016457c9cf1435391bf56cc98f369a58a54fe937724655490916001600160a01b03169063303eadfb9085908590600114613f7c576000613f7f565b60015b6040516001600160e01b031960e086901b16815260048101939093526001600160a01b03909116602483015215156044820152606401602060405180830381865afa158015613fd2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122359190615c0f565b6000614000614850565b61400861438e565b614014612330876143b2565b614020858588866145b8565b600061402d878787614861565b905061403d878688848888614b7b565b9150846005600089815260200190815260200160002060020160008282546140659190615de4565b90915550506040805188815260208101849052908101879052606081018690526001600160a01b03851660808201523360a08201527f6fa330b6e4da8eb081261834b2c05817b24b88caf5f7fcdb1a54e73caf206e119060c00160405180910390a15095945050505050565b6140dc838383614bed565b505050565b6000818152600560209081526040808320600690810154908352818420825181546060958102820186018552938101848152929594859491938492909184919084018282801561415a57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161413c575b50505050508152602001600182018054806020026020016040519081016040528092919081815260200182805480156141b257602002820191906000526020600020905b81548152602001906001019080831161419e575b50505091909252505050600085815260056020526040812060030154919250905b8415610e6957600086815260096020908152604080832088845290915281206001015492505b8351518110156142b85782156142b05760008781526009602090815260408083208984529091529020600201836142517f0000000000000000000000000000000000000000000000000000000000000000600a615d90565b848760200151858151811061426857614268615c28565b60200260200101518761427b9190615df7565b6142859190615db2565b61428f9190615df7565b6142999190615db2565b815460018101835560009283526020909220909101555b6001016141f9565b50836142c381615c80565b6000888152600e602052604090209095506142df915086613dde565b95506141d39050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b600c5460ff16611f405760405163dcdde9dd60e01b815260040160405180910390fd5b61435161438e565b600c805460ff191660011790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25890602001613efe565b600c5460ff1615611f405760405163ab35696f60e01b815260040160405180910390fd5b6000600160008381526005602052604090206008015460ff1660038111156143dc576143dc615759565b14801561214257505060009081526005602052604090206001015442111590565b6000858152600960209081526040808320878452825280832084845260030190915281206001018054859290614434908490615de4565b90915550506000858152600960209081526040808320878452825280832084845260039081019092528220018054849290614470908490615de4565b909155505060008581526009602090815260408083208784528252808320848452600301909152812060060180548592906144ac908490615c99565b9091555050604080518681526020810185905233918101919091527f6df3166c311c515f6d954b1144d8a8fe1575d42d23369a699f97024033caf1ef906060015b60405180910390a15050505050565b6000858152600960209081526040808320878452825280832084845260030190915281206004018054859290614533908490615de4565b9091555050600085815260096020908152604080832087845282528083208484526003019091528120600101805484929061456f908490615c99565b909155505060408051868152602081018690529081018490523360608201527fdb338dac4e401648bcae51c564238a2161f21cf257ba09e51d20fd068ca79691906080016144ed565b6145ce6001600160a01b03821615156001613db9565b6145db8315156003613db9565b613eba84158015906146075750600083815260056020819052604090912001546146059086615e5a565b155b6005613db9565b6000858152600560205260408120600601548190815b8684146146b8576146358488615c99565b905061464582891115600c613db9565b614653898389848a8a614c36565b60008a8152600960209081526040808320868452909152812060010180549295508592909190614684908490615de4565b9091555061469490508385615de4565b60008a8152600e602052604090209094506146af9083613dde565b92506146249050565b505050505050505050565b60008581526009602090815260408083208784528252808320848452600301909152812060060180548592906146fa908490615de4565b90915550506000858152600960209081526040808320878452825280832084845260030190915281206001018054859290614736908490615c99565b90915550506000858152600960209081526040808320878452825280832084845260039081019092528220018054849290614772908490615c99565b909155505060408051868152602081018690529081018490523360608201527fe2c0b0d5b60b3365ee30759e6358dc333ed40ece550937d37b33951f84a4eb4a906080016144ed565b6000806147c88585614f68565b6147d7575060009050806147fa565b505060008281526001848101602090815260408084208515158552909152909120545b935093915050565b6040516323b872dd600052836020528260405281606052602060006064601c6000895af13d15600160005114171661484257637939f4246000526004601cfd5b600060605260405250505050565b333214611f4057611f406008613d93565b6000838152600a60209081526040808320858452825280832054868452600983528184208685529092528220549091036148bc5761489f8385614fec565b600084815260096020908152604080832086845290915290208390555b8060000361498157604080516101008101825242815260006020808301828152838501838152606085018481526080860185815260a087018a815260c088018b815260e089018881528e8952600988528a89208e8a5288528a89208c8a52600390810189528b8a209a518b5596516001808c0191909155955160028b015593519589019590955590516004880155516005870155915160068601559051600790940193909355878252600a815283822087835290529190912080549091019055612235565b60008481526009602090815260408083208684529091528120600301816149a9600185615c99565b8152602001908152602001600020604051806101000160405290816000820154815260200160018201548152602001600282015481526020016003820154815260200160048201548152602001600582015481526020016006820154815260200160078201548152505090508060e00151600014614acb576000858152600960209081526040808320878452825280832085845260030190915281204281556005018054859290614a5b908490615de4565b90915550506000858152600960209081526040808320878452825280832085845260030190915281206006018054859290614a97908490615de4565b90915550506000858152600a602090815260408083208784529091528120805491614ac183615c80565b91905055506120cc565b614ad482615e6e565b9150828160a001818151614ae89190615de4565b90525060c081018051849190614aff908390615de4565b9052506000858152600960209081526040808320878452825280832085845260039081018352928190208451815591840151600183015583015160028201556060830151918101919091556080820151600482015560a0820151600582015560c0820151600682015560e0820151600790910155509392505050565b60028054906000614b8b83615c80565b90915550600081815260076020526040902096875560018701949094556002860194909455600385019190915560049093018054921515600160a01b026001600160a81b03199093166001600160a01b03909416939093179190911790915590565b81601a5280603a5269a9059cbb00000000000060005260206000604460166000875af13d156001600051141716614c2c576390b8ec186000526004601cfd5b6000603a52505050565b6000868152600b602090815260408083208884529091528120548015801590614cd1575060008881526009602090815260408083208a8452909152812060030190614c82600184615c99565b815260208082019290925260409081016000908120600701548b8252600984528282208b835290935290812060030190614cbd600185615c99565b815260200190815260200160002060050154115b15614ce257614cdf81615e6e565b90505b6000888152600a602090815260408083208a8452909152812054614d0890600190615c99565b905060005b818311158015614d1d5750868414155b15614f5b5760008a81526009602090815260408083208c8452825280832086845260030190915281206007810154600590910154614d5b9190615c99565b90506000614d69868a615c99565b9050808210614e7c5760008c81526009602090815260408083208e845282528083208884526003019091529020600701805482019055948501949150818988820281614db757614db7615d9c565b04600960008e815260200190815260200160002060008d8152602001908152602001600020600301600087815260200190815260200160002060020160008282540192505081905550614e77878c878d670de0b6b3a764000088614e1b9190615df7565b614e259190615db2565b6000938452600860209081526040852060048101805460018181018355918852838820019590955560058101805480870182559087528287200193909355600690920180549384018155845292200155565b614f4e565b8115614f4e5760008c81526009602090815260408083208e8452825280832088845260030190915290206007018054830190559481019490915081908988830281614ec957614ec9615d9c565b04600960008e815260200190815260200160002060008d8152602001908152602001600020600301600087815260200190815260200160002060020160008282540192505081905550614f2d878c878d670de0b6b3a764000088614e1b9190615df7565b60008c8152600b602090815260408083208e84529091529020805460010190555b8460010194505050614d0d565b5050509695505050505050565b60008181526001830160209081526040808320838052909152812054158015614fab57506000828152600180850160209081526040808420928452919052902054155b15614fe4576000808052600180850160209081526040808420928452919052902054829003614fdc57506001612142565b506000612142565b506001612142565b60008181526005602052604090206006810154600790910154818411156150225760008381526005602052604090206006018490555b8084108061502e575080155b156150485760008381526005602052604090206007018490555b6000615054858561509c565b90508060000361507c576000848152600e60205260409020615076908661513e565b50615095565b6000848152600e60205260409020610e6990828761514c565b5050505050565b6000818152600e60205260408120546000036150ba57506000612142565b6000828152600e602052604081206150d4908260016147bb565b9150505b801580159061510e57506000838152600960209081526040808320848452909152812054900361510957600061510b565b805b84105b15612235576000838152600e602090815260408083209383526001938401825280832093835292905220546150d8565b60006122358383600061515b565b60006122328484846000615166565b600061223284600085855b60006151728584614f68565b15801561518457506151848585614f68565b156152065760008481526001868101602081815260408085208715801580885291845282872080548b8952868652848920838a52808752858a208e9055918c90558089529585528388209188529084528287208a905590865290915283208190558754909288916151f6908490615de4565b9091555060019250610c5b915050565b506000949350505050565b6040518060c001604052806000815260200160008152602001600081526020016000815260200160006001600160a01b031681526020016000151581525090565b60405180610140016040528060008152602001600081526020016000815260200160008152602001606081526020016060815260200160608152602001600060028111156152a2576152a2615759565b815260006020820181905260409091015290565b604051806101000160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001615309604051806040016040528060008152602001600081525090565b81526020016000905290565b828054828255906000526020600020908101928215615350579160200282015b82811115615350578251825591602001919060010190615335565b5061535c9291506153bf565b5090565b60405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b5080546000825590600052602060002090810190610f6891905b5b8082111561535c57600081556001016153c0565b600080604083850312156153e757600080fd5b50508035926020909101359150565b60006020828403121561540857600080fd5b5035919050565b60008083601f84011261542157600080fd5b50813567ffffffffffffffff81111561543957600080fd5b6020830191508360208260051b8501011115613df257600080fd5b6000806000806040858703121561546a57600080fd5b843567ffffffffffffffff8082111561548257600080fd5b61548e8883890161540f565b909650945060208701359150808211156154a757600080fd5b506154b48782880161540f565b95989497509550505050565b634e487b7160e01b600052604160045260246000fd5b803560ff811681146154e757600080fd5b919050565b600060208083850312156154ff57600080fd5b823567ffffffffffffffff8082111561551757600080fd5b818501915085601f83011261552b57600080fd5b81358181111561553d5761553d6154c0565b8060051b604051601f19603f83011681018181108582111715615562576155626154c0565b60405291825284820192508381018501918883111561558057600080fd5b938501935b828510156155a557615596856154d6565b84529385019392850192615585565b98975050505050505050565b600081518084526020808501945080840160005b838110156155e1578151875295820195908201906001016155c5565b509495945050505050565b60208152600061223560208301846155b1565b80356001600160a01b03811681146154e757600080fd5b6000806040838503121561562957600080fd5b615632836155ff565b946020939093013593505050565b60006020828403121561565257600080fd5b612235826155ff565b60008060008060006060868803121561567357600080fd5b853567ffffffffffffffff8082111561568b57600080fd5b61569789838a0161540f565b909750955060208801359150808211156156b057600080fd5b506156bd8882890161540f565b96999598509660400135949350505050565b6000806000604084860312156156e457600080fd5b833567ffffffffffffffff8111156156fb57600080fd5b6157078682870161540f565b909450925061571a9050602085016155ff565b90509250925092565b85815284602082015283604082015282606082015260a06080820152600061574e60a08301846155b1565b979650505050505050565b634e487b7160e01b600052602160045260246000fd5b6003811061577f5761577f615759565b9052565b6020815281516020820152602082015160408201526040820151606082015260608201516080820152600060808301516101408060a08501526157ca6101608501836155b1565b915060a0850151601f19808685030160c08701526157e884836155b1565b935060c08701519150808685030160e08701525061580683826155b1565b92505060e085015161010061581d8187018361576f565b8601519050610120615839868201836001600160a01b03169052565b909501516001600160a01b031693019290925250919050565b60006020828403121561586457600080fd5b81356008811061223557600080fd5b6020808252825160408383015280516060840181905260009291820190839060808601905b808310156158c15783516001600160a01b03168252928401926001929092019190840190615898565b5092860151858403601f190160408701529261574e81856155b1565b803580151581146154e757600080fd5b6000806000806080858703121561590357600080fd5b843593506020850135925061591a604086016155ff565b9150615928606086016158dd565b905092959194509250565b60006020828403121561594557600080fd5b81356006811061223557600080fd5b6020808252825182820181905260009190848201906040850190845b8181101561598f57835160ff1683529284019291840191600101615970565b50909695505050505050565b6000806000604084860312156159b057600080fd5b833567ffffffffffffffff8111156159c757600080fd5b6159d38682870161540f565b909450925061571a9050602085016158dd565b600061012082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c0830151805160c0840152602081015160e08401525060e083015160048110615a5157615a51615759565b806101008401525092915050565b600080600060608486031215615a7457600080fd5b505081359360208301359350604090920135919050565b60008060408385031215615a9e57600080fd5b82359150615aae602084016155ff565b90509250929050565b60008060408385031215615aca57600080fd5b82359150615aae602084016158dd565b805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e08301525050565b6020808252825182820181905260009190848201906040850190845b8181101561598f57615b57838551615ada565b928401926101009290920191600101615b44565b61010081016121428284615ada565b60008060008060808587031215615b9057600080fd5b8435935060208501359250615ba7604086016155ff565b9150615928606086016155ff565b600080600060608486031215615bca57600080fd5b615bd3846155ff565b95602085013595506040909401359392505050565b60208101612142828461576f565b82815260406020820152600061223260408301846155b1565b600060208284031215615c2157600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b6040810160088410615c5257615c52615759565b9281526001600160a01b039190911660209091015290565b634e487b7160e01b600052601160045260246000fd5b600060018201615c9257615c92615c6a565b5060010190565b8181038181111561214257612142615c6a565b600181815b80851115615ce7578160001904821115615ccd57615ccd615c6a565b80851615615cda57918102915b93841c9390800290615cb1565b509250929050565b600082615cfe57506001612142565b81615d0b57506000612142565b8160018114615d215760028114615d2b57615d47565b6001915050612142565b60ff841115615d3c57615d3c615c6a565b50506001821b612142565b5060208310610133831016604e8410600b8410161715615d6a575081810a612142565b615d748383615cac565b8060001904821115615d8857615d88615c6a565b029392505050565b60006122358383615cef565b634e487b7160e01b600052601260045260246000fd5b600082615dc157615dc1615d9c565b500490565b6040810160068410615dda57615dda615759565b9281526020015290565b8082018082111561214257612142615c6a565b808202811582820484141761214257612142615c6a565b87815260018060a01b03871660208201528560408201528460608201528360808201528260a082015260e060c08201526000615e4d60e08301846155b1565b9998505050505050505050565b600082615e6957615e69615d9c565b500690565b600081615e7d57615e7d615c6a565b50600019019056fe17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3eca2646970667358221220570b73e0507a2024e257e1ad1b33d47281d5a452f8f4d0b1fab9c940bad4d55964736f6c63430008110033

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.