ETH Price: $3,355.82 (+5.10%)
Gas: 7 Gwei

Contract

0x392afD7B606b2113aD676412E72b1623f4268459
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Collect198614962024-05-13 13:49:1163 days ago1715608151IN
0x392afD7B...3f4268459
0 ETH0.0006006510.06050836
Collect198614722024-05-13 13:44:2363 days ago1715607863IN
0x392afD7B...3f4268459
0 ETH0.0010508110.76988439
Deposit195716162024-04-03 0:16:59103 days ago1712103419IN
0x392afD7B...3f4268459
0 ETH0.0032073521.59559527
Deposit195712352024-04-02 23:00:11103 days ago1712098811IN
0x392afD7B...3f4268459
0 ETH0.0033891425.93703267
Deposit195710062024-04-02 22:14:23103 days ago1712096063IN
0x392afD7B...3f4268459
0 ETH0.0042721832.69999115
Deposit195705982024-04-02 20:51:47103 days ago1712091107IN
0x392afD7B...3f4268459
0 ETH0.0051850637.3275935
Deposit195698292024-04-02 18:16:47103 days ago1712081807IN
0x392afD7B...3f4268459
0 ETH0.0063505943.70286559
Deposit195666822024-04-02 7:38:35104 days ago1712043515IN
0x392afD7B...3f4268459
0 ETH0.0099706467.13018871
Deposit195650362024-04-02 2:07:11104 days ago1712023631IN
0x392afD7B...3f4268459
0 ETH0.0024194218.51861981
Deposit195642022024-04-01 23:18:47104 days ago1712013527IN
0x392afD7B...3f4268459
0 ETH0.0033818523.52967887
Deposit195641992024-04-01 23:18:11104 days ago1712013491IN
0x392afD7B...3f4268459
0 ETH0.0033041122.99204158
Deposit195635852024-04-01 21:14:35104 days ago1712006075IN
0x392afD7B...3f4268459
0 ETH0.0036747524.34545488
Deposit195561992024-03-31 20:20:11105 days ago1711916411IN
0x392afD7B...3f4268459
0 ETH0.0037902525.11268628
Deposit195546882024-03-31 15:12:47105 days ago1711897967IN
0x392afD7B...3f4268459
0 ETH0.0032890323.81808674
Deposit195474222024-03-30 14:40:23106 days ago1711809623IN
0x392afD7B...3f4268459
0 ETH0.0045107431.03734042
Deposit195455172024-03-30 8:13:59107 days ago1711786439IN
0x392afD7B...3f4268459
0 ETH0.0025350717.44564686
Deposit195455112024-03-30 8:12:47107 days ago1711786367IN
0x392afD7B...3f4268459
0 ETH0.0027585419.52203782
Deposit195446362024-03-30 5:15:35107 days ago1711775735IN
0x392afD7B...3f4268459
0 ETH0.0028479819.27493931
Deposit195400572024-03-29 13:48:59108 days ago1711720139IN
0x392afD7B...3f4268459
0 ETH0.0040873427.66279352
Deposit195389122024-03-29 9:57:11108 days ago1711706231IN
0x392afD7B...3f4268459
0 ETH0.0032313321.40494485
Deposit195362092024-03-29 0:52:11108 days ago1711673531IN
0x392afD7B...3f4268459
0 ETH0.0042770529.75822139
Deposit195320432024-03-28 10:33:47109 days ago1711622027IN
0x392afD7B...3f4268459
0 ETH0.0033318225.82289188
Deposit195312142024-03-28 7:46:47109 days ago1711612007IN
0x392afD7B...3f4268459
0 ETH0.0035243524.24819809
Deposit195311412024-03-28 7:31:59109 days ago1711611119IN
0x392afD7B...3f4268459
0 ETH0.0025741320.32652442
Deposit195299942024-03-28 3:39:59109 days ago1711597199IN
0x392afD7B...3f4268459
0 ETH0.0032538421.4499333
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Sale

Compiler Version
v0.8.4+commit.c7e474f2

Optimization Enabled:
Yes with 500 runs

Other Settings:
default evmVersion, None license
File 1 of 63 : Sale.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;
pragma abicoder v2;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "./interfaces/ITierable.sol";
import "./libraries/Depositable.sol";
import "./libraries/MaxDepositable.sol";
import "./libraries/MaxTierDepositable.sol";
import "./libraries/Schedulable.sol";
import "./libraries/Suspendable.sol";
import "./libraries/Collectable.sol";
import "./libraries/Authorizable.sol";

contract Sale is
    Initializable,
    AccessControlUpgradeable,
    Depositable,
    MaxDepositable,
    MaxTierDepositable,
    Schedulable,
    Suspendable,
    Collectable,
    Authorizable
{
    struct SaleConfiguration {
        IERC20Upgradeable depositToken; // token used to deposit
        uint256 maxTotalDeposit; // max total deposited in the sale
        ITierable lockedToken; // locked token used to get the user tier
        uint256[] tiersMaxDeposit; // max amount deposited per tier (per user)
        uint256 startDate; // start date of the sale
        uint256 endDate; // end date of the sale
        address authorizer; // authorizer account, used to verify signed deposit
        address collector; // collector account
        address pauser; // pauser account
    }

    /**
     * @notice Initializer
     * @param configuration: see {Sale.SaleConfiguration}
     */
    function initialize(SaleConfiguration memory configuration)
        external
        initializer
    {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __Depositable_init_unchained(configuration.depositToken);
        __MaxDepositable_init_unchained(configuration.maxTotalDeposit);
        __MaxTierDepositable_init_unchained(
            configuration.lockedToken,
            configuration.tiersMaxDeposit
        );
        __Schedulable_init_unchained(
            configuration.startDate,
            configuration.endDate
        );
        __Pausable_init_unchained();
        __Suspendable_init_unchained(configuration.pauser);
        __Collectable_init_unchained(configuration.collector);
        __EIP712_init_unchained("Launchblock", "1.0");
        __Authorizable_init_unchained(configuration.authorizer);
        __Sale_init_unchained();
    }

    function __Sale_init_unchained() internal onlyInitializing {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    /**
     * @dev see {Depositable._deposit}
     */
    function _deposit(
        address from,
        address to,
        uint256 amount
    )
        internal
        virtual
        override(MaxTierDepositable, MaxDepositable, Depositable)
        whenMaxDepositNotReached(amount)
        returns (uint256)
    {
        return MaxTierDepositable._deposit(from, to, amount);
    }

    /**
     * @notice Deposit amount token to the sender address balance
     * must be signed by a member of `AUTHORIZER_ROLE`
     */
    function deposit(uint256 amount, bytes memory signature)
        external
        whenOpened
        whenNotPaused
        whenAuthorized(amount, _msgSender(), signature)
    {
        require(amount > 0, "Sale: amount must be > 0");
        _deposit(_msgSender(), _msgSender(), amount);
    }

    /**
     * @notice Collect all tokens deposited and send them to the caller's address
     * only callable by members of the collector role
     */
    function collect() external whenClosed whenNotPaused {
        uint256 amount = depositToken.balanceOf(address(this));
        _collect(_msgSender(), amount);
    }

    uint256[50] private __gap;
}

File 2 of 63 : Farm.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "./libraries/Suspendable.sol";
import "./libraries/PoolFarmDepositable.sol";

/** @title Farm.
 * @dev PoolFarmDepositable contract implementation with tiers
 */
contract Farm is Initializable, Suspendable, PoolFarmDepositable {
    /**
     * @notice Initializer
     * @param _depositToken: the address of the token to use for deposit, withdraw and interest
     * @param _tier: the address of the tier contract
     * @param _interestWallet: the wallet to get the interest token from
     * @param _pauser: the address of the account granted with PAUSER_ROLE
     */
    function initialize(
        IERC20Upgradeable _depositToken,
        ITierable _tier,
        address _interestWallet,
        address _pauser
    ) external initializer {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __Pausable_init_unchained();
        __Suspendable_init_unchained(_pauser);
        __PoolFarmable_init_unchained();
        __Depositable_init_unchained(_depositToken);
        __PoolFarmDepositable_init_unchained(_tier, _interestWallet);
        __Farm_init_unchained();
    }

    function __Farm_init_unchained() internal onlyInitializing {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    /**
     * @notice Deposit into a farm pool
     */
    function deposit(uint256 amount, uint256 poolIndex) external whenNotPaused {
        PoolFarmDepositable._deposit(
            _msgSender(),
            _msgSender(),
            amount,
            poolIndex
        );
    }

    /**
     * @notice Withdraw from a farm pool
     */
    function withdraw(uint256 amount, uint256 poolIndex)
        external
        whenNotPaused
    {
        PoolFarmDepositable._withdraw(
            _msgSender(),
            _msgSender(),
            amount,
            poolIndex
        );
    }

    uint256[50] private __gap;
}

File 3 of 63 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/utils/Initializable.sol)

pragma solidity ^0.8.0;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
 * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() initializer {}
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

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

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        // If the contract is initializing we ignore whether _initialized is set in order to support multiple
        // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
        // contract may have been reentered.
        require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");

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

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} modifier, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    function _isConstructor() private view returns (bool) {
        return !AddressUpgradeable.isContract(address(this));
    }
}

File 4 of 63 : Suspendable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";

/** @title RoleBasedPausable.
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 */
abstract contract Suspendable is
    Initializable,
    AccessControlUpgradeable,
    PausableUpgradeable
{
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

    /**
     * @notice Initializer
     * @param _pauser: the address of the account granted with PAUSER_ROLE
     */
    function __Suspendable_init(address _pauser) internal onlyInitializing {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __Pausable_init_unchained();
        __Suspendable_init_unchained(_pauser);
    }

    function __Suspendable_init_unchained(address _pauser)
        internal
        onlyInitializing
    {
        _setupRole(PAUSER_ROLE, _pauser);
    }

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

    /**
     * @notice suspend/pause the contract.
     * Only callable by members of PAUSER_ROLE
     */
    function suspend() external onlyRole(PAUSER_ROLE) {
        _pause();
    }

    /**
     * @notice resume/unpause the contract.
     * Only callable by members of PAUSER_ROLE
     */
    function resume() external onlyRole(PAUSER_ROLE) {
        _unpause();
    }

    uint256[50] private __gap;
}

File 5 of 63 : PoolFarmDepositable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;
pragma abicoder v2;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "./PoolFarmable.sol";
import "./Depositable.sol";
import "../interfaces/ITierable.sol";

/** @title PoolFarmDepositable.
@dev This contract manage deposits in farm pools
*/
abstract contract PoolFarmDepositable is
    Initializable,
    AccessControlUpgradeable,
    PoolFarmable,
    Depositable
{
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using SafeMathUpgradeable for uint256;

    struct UserFarmPoolDeposit {
        uint256 amount; // amount deposited in the pool
        uint256 date; // date of the deposit
    }

    // mapping of deposits for a user
    // user -> pool index -> array of user deposit
    mapping(address => mapping(uint256 => UserFarmPoolDeposit[]))
        private _poolDeposits;

    // mapping of total deposits per pool
    // pool index -> total deposit
    mapping(uint256 => uint256) private _poolTotalDeposits;

    // tier contract
    ITierable public tier;

    // wallet to take interest from
    address public interestWallet;

    /**
     * @dev Emitted when a user deposit in a pool
     */
    event FarmPoolDeposit(
        address indexed from,
        address indexed to,
        uint256 indexed poolIndex,
        uint256 depositIndex,
        uint256 amount
    );

    /**
     * @dev Emitted when a user withdraw from a pool
     */
    event FarmPoolWithdraw(
        address indexed from,
        address indexed to,
        uint256 indexed poolIndex,
        uint256 depositIndex,
        uint256 amount,
        uint256 interest
    );

    /**
     * @notice Initializer
     * @param _depositToken: the deposited token
     */
    function __PoolFarmDepositable_init(
        IERC20Upgradeable _depositToken,
        ITierable _tier,
        address _interestWallet
    ) internal onlyInitializing {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __PoolFarmable_init_unchained();
        __Depositable_init_unchained(_depositToken);
        __PoolFarmDepositable_init_unchained(_tier, _interestWallet);
    }

    function __PoolFarmDepositable_init_unchained(
        ITierable _tier,
        address _interestWallet
    ) internal onlyInitializing {
        tier = _tier;
        interestWallet = _interestWallet;
    }

    /**
     * @dev returns the deposit of an account in a pool
     */
    function farmPoolDepositOf(address account, uint256 poolIndex)
        public
        view
        checkFarmPoolIndex(poolIndex)
        returns (UserFarmPoolDeposit memory)
    {
        // Hardcoded deposit index of zero for now. Multi-deposit feature will come later.
        uint256 depositIndex = 0;

        // assert deposit exist
        require(
            _poolDeposits[account][poolIndex].length > depositIndex,
            "PoolFarmDepositable: Deposit in this pool not found"
        );

        return _poolDeposits[account][poolIndex][depositIndex];
    }

    /**
     * @dev returns the interest of a user deposit
     */
    function farmPoolInterestOf(address account, uint256 poolIndex)
        public
        view
        returns (uint256)
    {
        FarmPool memory pool = getFarmPool(poolIndex);
        UserFarmPoolDeposit memory deposit = farmPoolDepositOf(
            account,
            poolIndex
        );

        // deposit duration = diff between now and deposit date
        // capped to pool max deposit duration
        uint256 depositDuration = MathUpgradeable.min(
            block.timestamp.sub(deposit.date),
            pool.maxDepositDuration
        );

        // interest = deposit amount * deposit duration * pool interest numerator / pool interest denominator
        uint256 interest = deposit
            .amount
            .mul(depositDuration)
            .mul(pool.interestNumerator)
            .div(pool.interestDenominator);
        return interest;
    }

    /**
     * @dev returns the total amount deposited in a pool
     */
    function farmPoolTotalDepositOfPool(uint256 poolIndex)
        public
        view
        checkFarmPoolIndex(poolIndex)
        returns (uint256)
    {
        return _poolTotalDeposits[poolIndex];
    }

    /**
     * @dev Batch deposit tokens to pool at `poolIndex`
     */
    function _deposit(
        address from,
        address to,
        uint256 amount,
        uint256 poolIndex
    ) internal virtual checkFarmPoolIndex(poolIndex) returns (uint256) {
        // get farm pool
        FarmPool memory pool = getFarmPool(poolIndex);

        // check opened
        require(pool.opened, "PoolFarmDepositable: Pool is closed");

        // check maxUserDepositAmount
        require(
            amount <= pool.maxUserDepositAmount,
            "PoolFarmDepositable: Amount to deposit is more than the pool max deposit per user"
        );

        // check tier of to address
        int256 userTier = tier.tierOf(to);
        require(
            userTier >= pool.minTier,
            "PoolFarmDepositable: Tier of the to address is less than required by the pool"
        );

        // transfer amount
        uint256 transferredAmount = Depositable._deposit(from, to, amount);

        if (_poolDeposits[to][poolIndex].length > 0) {
            // Hardcoded deposit index of zero for now. Multi-deposit feature will come later.
            uint256 depositIndex = 0;

            // update user deposit
            UserFarmPoolDeposit storage deposit = _poolDeposits[to][poolIndex][
                depositIndex
            ];

            // assert no previous deposit or previous deposit was fully withdraw
            require(
                deposit.amount == 0,
                "PoolFarmDepositable: Already deposited in this pool"
            );

            deposit.amount = deposit.amount.add(transferredAmount);
            deposit.date = block.timestamp;
        } else {
            // add user deposit
            _poolDeposits[to][poolIndex].push(
                UserFarmPoolDeposit({
                    amount: transferredAmount,
                    date: block.timestamp
                })
            );
        }

        // update total amount deposited in pool
        _poolTotalDeposits[poolIndex] = _poolTotalDeposits[poolIndex].add(
            transferredAmount
        );

        // check maxTotalDepositAmount. requires transferredAmount.
        require(
            _poolTotalDeposits[poolIndex] <= pool.maxTotalDepositAmount,
            "PoolFarmDepositable: Pool max total deposit amount surpassed with this deposit"
        );

        // emit event
        emit FarmPoolDeposit(
            from,
            to,
            poolIndex,
            _poolDeposits[to][poolIndex].length - 1,
            transferredAmount
        );

        // return transferred amount
        return transferredAmount;
    }

    /**
     * @dev Withdraw tokens from a pool with interest
     */
    function _withdraw(
        address from,
        address to,
        uint256 amount,
        uint256 poolIndex
    ) internal virtual checkFarmPoolIndex(poolIndex) returns (uint256) {
        // get farm pool
        FarmPool memory pool = getFarmPool(poolIndex);

        // Hardcoded deposit index of zero for now. Multi-deposit feature will come later.
        uint256 depositIndex = 0;

        // assert deposit exist
        require(
            _poolDeposits[to][poolIndex].length > depositIndex,
            "PoolFarmDepositable: Deposit in this pool not found"
        );

        // get user deposit
        UserFarmPoolDeposit storage deposit = _poolDeposits[to][poolIndex][
            depositIndex
        ];

        // check deposit duration is more than or equal to pool's min deposit duration
        uint256 depositDuration = block.timestamp.sub(deposit.date);
        require(
            depositDuration >= pool.minDepositDuration,
            "PoolFarmDepositable: Deposit duration is less than pool min deposit duration"
        );

        // check amount is less or equal to deposit amount
        require(
            amount <= deposit.amount,
            "PoolFarmDepositable: Amount to withdraw is more than the deposited amount"
        );

        // calculate interest
        uint256 interest = farmPoolInterestOf(from, poolIndex);

        // transfer amount
        uint256 withdrawAmount = Depositable._withdraw(to, amount);

        // transfer interest
        depositToken.safeTransferFrom(interestWallet, to, interest);

        // subtract amount from user deposit
        deposit.amount = deposit.amount.sub(withdrawAmount);

        // subtract amount from total pool deposit
        _poolTotalDeposits[poolIndex] = _poolTotalDeposits[poolIndex].sub(
            withdrawAmount
        );

        // emit event
        emit FarmPoolWithdraw(
            from,
            to,
            poolIndex,
            depositIndex,
            withdrawAmount,
            interest
        );

        // return withdraw amount
        return withdrawAmount;
    }

    /**
     * @dev Update the interest wallet
     */
    function updateInterestWallet(address _interestWallet)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        interestWallet = _interestWallet;
    }

    uint256[50] private __gap;
}

File 6 of 63 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

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

File 7 of 63 : AccessControlUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
    function __AccessControl_init() internal onlyInitializing {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role, _msgSender());
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        StringsUpgradeable.toHexString(uint160(account), 20),
                        " is missing role ",
                        StringsUpgradeable.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
    uint256[49] private __gap;
}

File 8 of 63 : PausableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /**
     * @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.
     */
    function __Pausable_init() internal onlyInitializing {
        __Context_init_unchained();
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        _paused = false;
    }

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

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

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

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
    uint256[49] private __gap;
}

File 9 of 63 : IAccessControlUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControlUpgradeable {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

File 10 of 63 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

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

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
    uint256[50] private __gap;
}

File 11 of 63 : StringsUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

File 12 of 63 : ERC165Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
    function __ERC165_init() internal onlyInitializing {
        __ERC165_init_unchained();
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165Upgradeable).interfaceId;
    }
    uint256[50] private __gap;
}

File 13 of 63 : IERC165Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165Upgradeable {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 14 of 63 : SafeMathUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
library SafeMathUpgradeable {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

File 15 of 63 : MathUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library MathUpgradeable {
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

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

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a / b + (a % b == 0 ? 0 : 1);
    }
}

File 16 of 63 : SafeERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";
import "../../../utils/AddressUpgradeable.sol";

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

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

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

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

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

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

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

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

File 17 of 63 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 18 of 63 : PoolFarmable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;
pragma abicoder v2;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";

/** @title PoolFarmable.
@dev This contract manage configuration of farm pools
*/
abstract contract PoolFarmable is Initializable, AccessControlUpgradeable {
    using SafeMathUpgradeable for uint256;

    struct FarmPool {
        bool opened;
        int256 minTier;
        uint256 maxTotalDepositAmount;
        uint256 maxUserDepositAmount;
        uint256 minDepositDuration; // in seconds
        uint256 maxDepositDuration; // in seconds
        uint256 interestNumerator; // interest per seconds
        uint256 interestDenominator; // interest per seconds
    }

    // pools
    FarmPool[] private _pools;

    /**
     * @dev Emitted when a pool is created
     */
    event FarmPoolAdd(uint256 poolIndex, FarmPool pool);

    /**
     * @dev Emitted when a pool is updated
     */
    event FarmPoolUpdate(uint256 poolIndex, FarmPool pool);

    /**
     * @dev Modifier that checks pool is valid
     */
    modifier checkFarmPool(FarmPool calldata pool) {
        require(
            pool.maxUserDepositAmount <= pool.maxTotalDepositAmount,
            "PoolFarmable: maxUserDepositAmount must be less than or equal to maxTotalDepositAmount"
        );

        require(
            pool.minDepositDuration <= pool.maxDepositDuration,
            "PoolFarmable: minDepositDuration must be less than or equal to maxDepositDuration"
        );

        require(
            pool.interestNumerator > 0,
            "PoolFarmable: interestNumerator must be greater than 0"
        );

        require(
            pool.interestDenominator > 0,
            "PoolFarmable: interestDenominator must be greater than 0"
        );

        require(
            pool.interestNumerator <= pool.interestDenominator,
            "PoolFarmable: interestNumerator must be less than or equal to interestDenominator"
        );

        _;
    }

    /**
     * @dev Modifier that checks pool index is valid
     */
    modifier checkFarmPoolIndex(uint256 poolIndex) {
        require(poolIndex < _pools.length, "PoolFarmable: Invalid poolIndex");
        _;
    }

    /**
     * @notice Initializer
     */
    function __PoolFarmable_init() internal onlyInitializing {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __PoolFarmable_init_unchained();
    }

    /**
     * @notice Initializer
     */
    function __PoolFarmable_init_unchained() internal onlyInitializing {}

    /**
     * @dev Return a pool from its index
     */
    function getFarmPool(uint256 poolIndex)
        public
        view
        checkFarmPoolIndex(poolIndex)
        returns (FarmPool memory)
    {
        return _pools[poolIndex];
    }

    /**
     * @dev Return the number of pools
     */
    function farmPoolsLength() public view returns (uint256) {
        return _pools.length;
    }

    /**
     * @dev Add a new pool
     */
    function addFarmPool(FarmPool calldata pool)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
        checkFarmPool(pool)
    {
        _pools.push(pool);

        emit FarmPoolAdd(_pools.length - 1, _pools[_pools.length - 1]);
    }

    /**
     * @dev Update an existing pool
     */
    function updateFarmPool(uint256 poolIndex, FarmPool calldata pool)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
        checkFarmPoolIndex(poolIndex)
        checkFarmPool(pool)
    {
        FarmPool storage editedPool = _pools[poolIndex];

        editedPool.opened = pool.opened;
        editedPool.minTier = pool.minTier;
        editedPool.maxTotalDepositAmount = pool.maxTotalDepositAmount;
        editedPool.maxUserDepositAmount = pool.maxUserDepositAmount;
        editedPool.minDepositDuration = pool.minDepositDuration;
        editedPool.maxDepositDuration = pool.maxDepositDuration;
        editedPool.interestNumerator = pool.interestNumerator;
        editedPool.interestDenominator = pool.interestDenominator;

        emit FarmPoolUpdate(poolIndex, editedPool);
    }

    uint256[50] private __gap;
}

File 19 of 63 : Depositable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";

/** @title Depositable.
@dev It is a contract that allow to deposit an ERC20 token
*/
abstract contract Depositable is Initializable, AccessControlUpgradeable {
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using SafeMathUpgradeable for uint256;

    // Map of deposits per address
    mapping(address => uint256) private _deposits;

    // the deposited token
    IERC20Upgradeable public depositToken;

    // the total amount deposited
    uint256 public totalDeposit;

    /**
     * @dev Emitted when `amount` tokens are deposited to account (`to`)
     * Note that `amount` may be zero.
     */
    event Deposit(address indexed from, address indexed to, uint256 amount);

    /**
     * @dev Emitted when `amount` tokens are withdrawn to account (`to`)
     * Note that `amount` may be zero.
     */
    event Withdraw(address indexed to, uint256 amount);

    /**
     * @dev Emitted when the deposited token is changed by the admin
     */
    event DepositTokenChange(address indexed token);

    /**
     * @notice Intializer
     * @param _depositToken: the deposited token
     */
    function __Depositable_init(IERC20Upgradeable _depositToken)
        internal
        onlyInitializing
    {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __Depositable_init_unchained(_depositToken);
    }

    function __Depositable_init_unchained(IERC20Upgradeable _depositToken)
        internal
        onlyInitializing
    {
        depositToken = _depositToken;
    }

    /**
     * @dev Handle the deposit (transfer) of `amount` tokens from the `from` address
     * The contract must be approved to spend the tokens from the `from` address before calling this function
     * @param from: the depositor address
     * @param to: the credited address
     * @param amount: amount of token to deposit
     * @return the amount deposited
     */
    function _deposit(
        address from,
        address to,
        uint256 amount
    ) internal virtual returns (uint256) {
        // transfer tokens and check the real amount received
        uint256 balance = depositToken.balanceOf(address(this));
        depositToken.safeTransferFrom(from, address(this), amount);
        uint256 newBalance = depositToken.balanceOf(address(this));

        // replace amount by the real transferred amount
        amount = newBalance.sub(balance);

        // save deposit
        _deposits[to] = _deposits[to].add(amount);
        totalDeposit = totalDeposit.add(amount);
        emit Deposit(from, to, amount);

        return amount;
    }

    /**
     * @dev Remove `amount` tokens from the `to` address deposit balance, and transfer the tokens to the `to` address
     * @param to: the destination address
     * @param amount: amount of token to deposit
     * @return the amount withdrawn
     */
    function _withdraw(address to, uint256 amount)
        internal
        virtual
        returns (uint256)
    {
        require(amount <= _deposits[to], "Depositable: amount too high");

        _deposits[to] = _deposits[to].sub(amount);
        totalDeposit = totalDeposit.sub(amount);
        depositToken.safeTransfer(to, amount);

        emit Withdraw(to, amount);
        return amount;
    }

    /**
     * @notice get the total amount deposited by an address
     */
    function depositOf(address _address) public view virtual returns (uint256) {
        return _deposits[_address];
    }

    /**
     * @notice Change the deposited token
     */
    function changeDepositToken(IERC20Upgradeable _depositToken)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(totalDeposit == 0, "Depositable: total deposit != 0");
        depositToken = _depositToken;

        emit DepositTokenChange(address(_depositToken));
    }

    uint256[50] private __gap;
}

File 20 of 63 : ITierable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

/** @title ITierable contract interface.
 */
interface ITierable {
    function tierOf(address account) external returns (int256);
}

File 21 of 63 : MaxDepositable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "./Depositable.sol";

/** @title MaxDepositable.
@dev It is a contract that extends Depositable to enforce a max deposited total amount
*/
abstract contract MaxDepositable is
    Initializable,
    AccessControlUpgradeable,
    Depositable
{
    // the total amount deposited
    uint256 public maxTotalDeposit;

    /**
     * @dev Emitted when maxTotalDeposit is changed
     * Note that `amount` may be zero.
     */
    event MaxTotalDepositChange(uint256 amount);

    /**
     * @dev Modifier that checks that totalDeposit + amount <= maxTotalDeposit
     */
    modifier whenMaxDepositNotReached(uint256 amount) {
        require(
            maxTotalDeposit == 0 || totalDeposit + amount <= maxTotalDeposit,
            "MaxDepositable: max reached"
        );
        _;
    }

    /**
     * @notice Initializer
     * @param _depositToken: the deposited token
     * @param _maxTotalDeposit: the max total deposit
     */
    function __MaxDepositable_init(
        IERC20Upgradeable _depositToken,
        uint256 _maxTotalDeposit
    ) internal onlyInitializing {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __Depositable_init_unchained(_depositToken);
        __MaxDepositable_init_unchained(_maxTotalDeposit);
    }

    function __MaxDepositable_init_unchained(uint256 _maxTotalDeposit)
        internal
        onlyInitializing
    {
        maxTotalDeposit = _maxTotalDeposit;
    }

    /**
     * @dev See {Depositable-_deposit}
     * overriden to enforce maxTotalDeposit
     * @param from: the depositor address
     * @param to: the credited address
     * @param amount: amount of token to deposit
     */
    function _deposit(
        address from,
        address to,
        uint256 amount
    )
        internal
        virtual
        override
        whenMaxDepositNotReached(amount)
        returns (uint256)
    {
        return Depositable._deposit(from, to, amount);
    }

    /**
     * @notice Change the max total amount deposited
     */
    function changeMaxTotalDeposited(uint256 amount)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(
            amount == 0 || amount >= totalDeposit,
            "Depositable: max < total"
        );
        maxTotalDeposit = amount;

        emit MaxTotalDepositChange(amount);
    }

    uint256[50] private __gap;
}

File 22 of 63 : MaxTierDepositable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;
pragma abicoder v2;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
import "./Depositable.sol";
import "../interfaces/ITierable.sol";

/** @title MaxTierDepositable.
@dev This contract extends Depositable to enforce a max deposit per tier for a locked token
*/
abstract contract MaxTierDepositable is
    Initializable,
    AccessControlUpgradeable,
    Depositable
{
    using SafeMathUpgradeable for uint256;

    // max tiers
    uint256[] private _tiersMaxDepositAmount;
    // locked token (tierable)
    ITierable public lockedToken;

    /**
     * @dev Emitted when max deposit amount per tiers are changed
     */
    event TiersMaxAmountDepositChange(uint256[] amounts);

    /**
     * @dev Modifier that checks that the total deposit for `account` is not over the max allowed for his tier
     */
    modifier whenLessThanMaxTierDeposit(address account, uint256 amount) {
        int256 tier = lockedToken.tierOf(account);
        require(tier > -1, "MaxTierDepositable: minimum tier not reached");

        uint256 utier = uint256(tier);
        // use the last max tier configured if the tier of the user is greater or equal to the length of the array
        if (utier >= _tiersMaxDepositAmount.length) {
            utier = _tiersMaxDepositAmount.length - 1;
        }
        require(
            depositOf(account).add(amount) <= _tiersMaxDepositAmount[utier],
            "MaxTierDepositable: amount reached tier's maximum"
        );
        _;
    }

    /**
     * @notice Initializer
     * @param _depositToken: the deposited token
     * @param _lockedToken: the locked tierable token
     * @param _tiersMaxDeposit: the max deposit per tier
     */
    function __MaxTierDepositable_init(
        IERC20Upgradeable _depositToken,
        ITierable _lockedToken,
        uint256[] memory _tiersMaxDeposit
    ) internal onlyInitializing {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __Depositable_init_unchained(_depositToken);
        __MaxTierDepositable_init_unchained(_lockedToken, _tiersMaxDeposit);
    }

    function __MaxTierDepositable_init_unchained(
        ITierable _lockedToken,
        uint256[] memory _tiersMaxDeposit
    ) internal onlyInitializing {
        lockedToken = _lockedToken;
        _tiersMaxDepositAmount = _tiersMaxDeposit;
    }

    /**
     * @dev See {Depositable-_deposit}
     * overridden to enforce max deposit per tier
     * @param from: the depositor address
     * @param amount: amount of token to deposit
     */
    function _deposit(
        address from,
        address to,
        uint256 amount
    )
        internal
        virtual
        override
        whenLessThanMaxTierDeposit(to, amount)
        returns (uint256)
    {
        return Depositable._deposit(from, to, amount);
    }

    /**
     * @notice update the tiers max deposit brackets
     * Only callable by owners
     */
    function changeTiersMaxDepositAmount(uint256[] memory _amounts)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        _tiersMaxDepositAmount = _amounts;
        emit TiersMaxAmountDepositChange(_tiersMaxDepositAmount);
    }

    /**
     * @notice returns the list of max deposit amount per tier
     */
    function getTiersMaxDepositAmount()
        external
        view
        returns (uint256[] memory)
    {
        return _tiersMaxDepositAmount;
    }

    uint256[50] private __gap;
}

File 23 of 63 : Schedulable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";

/** @title Schedulable.
@dev It is a contract that allow member from the scheduler role
to configure a start and end date
*/
abstract contract Schedulable is Initializable, AccessControlUpgradeable {
    // the start date
    uint256 public startDate;

    // the end date
    uint256 public endDate;

    // Change Start date event
    event StartDateChanged(uint256 indexed date);

    // Change End date event
    event EndDateChanged(uint256 indexed date);

    /**
     * @dev Modifier that checks that startDate <= current block timestamp <= endDate
     */
    modifier whenOpened() {
        require(
            block.timestamp >= startDate && block.timestamp <= endDate,
            "Schedulable: Not opened"
        );
        _;
    }

    /**
     * @dev Modifier that checks that block timestamp > endDate
     */
    modifier whenClosed() {
        require(block.timestamp > endDate, "Schedulable: Not closed");
        _;
    }

    /**
     * @notice Initializer
     * @param _startDate: the start date in unix time
     * @param _endDate: the end date in unix time
     */
    function __Schedulable_init(uint256 _startDate, uint256 _endDate)
        internal
        onlyInitializing
    {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __Schedulable_init_unchained(_startDate, _endDate);
    }

    function __Schedulable_init_unchained(uint256 _startDate, uint256 _endDate)
        internal
        onlyInitializing
    {
        startDate = _startDate;
        endDate = _endDate;
    }

    /**
     * @notice Change the start date
     * @param _startDate: the new start date in unix time
     */
    function changeStartDate(uint256 _startDate)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(_startDate < endDate, "Schedulable: invalid start date");
        startDate = _startDate;
        emit StartDateChanged(startDate);
    }

    /**
     * @notice Change the endDate date
     * @param _endDate: the new end date in unix time
     */
    function changeEndDate(uint256 _endDate)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(_endDate > startDate, "Schedulable: invalid end date");
        endDate = _endDate;
        emit EndDateChanged(endDate);
    }

    uint256[50] private __gap;
}

File 24 of 63 : Collectable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
import "./Depositable.sol";

/** @title Collectable.
@dev It is a contract that allow members of the collector role to collect deposits
*/
abstract contract Collectable is
    Initializable,
    AccessControlUpgradeable,
    Depositable
{
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using SafeMathUpgradeable for uint256;

    // collector role
    bytes32 public constant COLLECTOR_ROLE = keccak256("COLLECTOR_ROLE");

    // the total amount collected
    uint256 public totalCollected;

    /**
     * @dev Emitted when tokens are collected
     */
    event Collect(address indexed destination, uint256 amount);

    /**
     * @notice Initializer
     * @param _depositToken: the deposited token
     * @param _collector: the address of the account granted with COLLECTOR_ROLE
     */
    function __Collectable_init(
        IERC20Upgradeable _depositToken,
        address _collector
    ) internal onlyInitializing {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __Depositable_init_unchained(_depositToken);
        __Collectable_init_unchained(_collector);
    }

    function __Collectable_init_unchained(address _collector)
        internal
        onlyInitializing
    {
        _setupRole(COLLECTOR_ROLE, _collector);
    }

    /**
     * @dev Handle the collect (transfer) of `amount` deposited tokens
     * @param destination: destination depositor address
     * @param amount: amount of token to collect
     */
    function _collect(address destination, uint256 amount)
        internal
        virtual
        onlyRole(COLLECTOR_ROLE)
    {
        require(
            depositToken.balanceOf(address(this)) >= amount,
            "Collectable: not enough balance"
        );

        depositToken.safeTransfer(destination, amount);
        totalCollected = totalCollected.add(amount);

        emit Collect(destination, amount);
    }

    uint256[50] private __gap;
}

File 25 of 63 : Authorizable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/SignatureCheckerUpgradeable.sol";

/** @title Authorizable.
@dev It is a contract that allow to verify the signature of a message.
* Uses EIP-1271 for validation
*/
abstract contract Authorizable is
    Initializable,
    AccessControlUpgradeable,
    EIP712Upgradeable
{
    using AddressUpgradeable for address;
    using ECDSAUpgradeable for bytes32;

    // authorizer role
    bytes32 public constant AUTHORIZER_ROLE = keccak256("AUTHORIZER_ROLE");

    /**
     * @dev Modifier that checks that hash was signed by a valid Authorizer
     */
    modifier whenAuthorized(
        uint256 amount,
        address from,
        bytes memory signature
    ) {
        bytes32 structHash = hashDepositPayload(amount, from);
        require(
            isAuthorized(structHash, signature),
            "Authorizable: not authorized"
        );
        _;
    }

    /**
     * @notice Initializer
     * @param _authorizer: the address of the account granted with AUTHORIZER_ROLE
     * @param _eipName: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * @param _eipVersion: the current major version of the signing domain.
     */
    function __Authorizable_init(
        address _authorizer,
        string memory _eipName,
        string memory _eipVersion
    ) internal onlyInitializing {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __EIP712_init_unchained(_eipName, _eipVersion);
        __Authorizable_init_unchained(_authorizer);
    }

    function __Authorizable_init_unchained(address _authorizer)
        internal
        onlyInitializing
    {
        _setupRole(AUTHORIZER_ROLE, _authorizer);
    }

    /**
     * @notice verifify signature is valid for `structHash` and signers is a member of role `AUTHORIZER_ROLE`
     * @param structHash: hash of the structure to verify the signature against
     */
    function isAuthorized(bytes32 structHash, bytes memory signature)
        internal
        view
        returns (bool)
    {
        bytes32 hash = _hashTypedDataV4(structHash);
        (
            address recovered,
            ECDSAUpgradeable.RecoverError error
        ) = ECDSAUpgradeable.tryRecover(hash, signature);
        if (
            error == ECDSAUpgradeable.RecoverError.NoError &&
            hasRole(AUTHORIZER_ROLE, recovered)
        ) {
            return true;
        }

        return false;
    }

    bytes32 public constant DEPOSIT_TYPEHASH =
        keccak256("Deposit(uint256 amount,address account)");

    /**
     * @dev abi encode the payload of a deposit and returns the hash
     */
    function hashDepositPayload(uint256 _amount, address _from)
        internal
        pure
        returns (bytes32)
    {
        return keccak256(abi.encode(DEPOSIT_TYPEHASH, _amount, _from));
    }

    uint256[50] private __gap;
}

File 26 of 63 : draft-EIP712Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)

pragma solidity ^0.8.0;

import "./ECDSAUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * _Available since v3.4._
 */
abstract contract EIP712Upgradeable is Initializable {
    /* solhint-disable var-name-mixedcase */
    bytes32 private _HASHED_NAME;
    bytes32 private _HASHED_VERSION;
    bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    /* solhint-enable var-name-mixedcase */

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
        __EIP712_init_unchained(name, version);
    }

    function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
        bytes32 hashedName = keccak256(bytes(name));
        bytes32 hashedVersion = keccak256(bytes(version));
        _HASHED_NAME = hashedName;
        _HASHED_VERSION = hashedVersion;
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
    }

    function _buildDomainSeparator(
        bytes32 typeHash,
        bytes32 nameHash,
        bytes32 versionHash
    ) private view returns (bytes32) {
        return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev The hash of the name parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712NameHash() internal virtual view returns (bytes32) {
        return _HASHED_NAME;
    }

    /**
     * @dev The hash of the version parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712VersionHash() internal virtual view returns (bytes32) {
        return _HASHED_VERSION;
    }
    uint256[50] private __gap;
}

File 27 of 63 : ECDSAUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../StringsUpgradeable.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSAUpgradeable {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        } else if (error == RecoverError.InvalidSignatureV) {
            revert("ECDSA: invalid signature 'v' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        // Check the signature length
        // - case 65: r,s,v signature (standard)
        // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 vs;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                vs := mload(add(signature, 0x40))
            }
            return tryRecover(hash, r, vs);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s;
        uint8 v;
        assembly {
            s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
            v := add(shr(255, vs), 27)
        }
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }
        if (v != 27 && v != 28) {
            return (address(0), RecoverError.InvalidSignatureV);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", StringsUpgradeable.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 28 of 63 : SignatureCheckerUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/SignatureChecker.sol)

pragma solidity ^0.8.0;

import "./ECDSAUpgradeable.sol";
import "../AddressUpgradeable.sol";
import "../../interfaces/IERC1271Upgradeable.sol";

/**
 * @dev Signature verification helper: Provide a single mechanism to verify both private-key (EOA) ECDSA signature and
 * ERC1271 contract signatures. Using this instead of ECDSA.recover in your contract will make them compatible with
 * smart contract wallets such as Argent and Gnosis.
 *
 * Note: unlike ECDSA signatures, contract signature's are revocable, and the outcome of this function can thus change
 * through time. It could return true at block N and false at block N+1 (or the opposite).
 *
 * _Available since v4.1._
 */
library SignatureCheckerUpgradeable {
    function isValidSignatureNow(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal view returns (bool) {
        (address recovered, ECDSAUpgradeable.RecoverError error) = ECDSAUpgradeable.tryRecover(hash, signature);
        if (error == ECDSAUpgradeable.RecoverError.NoError && recovered == signer) {
            return true;
        }

        (bool success, bytes memory result) = signer.staticcall(
            abi.encodeWithSelector(IERC1271Upgradeable.isValidSignature.selector, hash, signature)
        );
        return (success && result.length == 32 && abi.decode(result, (bytes4)) == IERC1271Upgradeable.isValidSignature.selector);
    }
}

File 29 of 63 : IERC1271Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 *
 * _Available since v4.1._
 */
interface IERC1271Upgradeable {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with _data
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}

File 30 of 63 : AuthorizableTest.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "../libraries/Authorizable.sol";

contract AuthorizableTest is Initializable, Authorizable {
    function initialize(address authorizer) external initializer {
        __Authorizable_init(authorizer, "Launchblock", "1.0");
    }

    function validateAuthorization(
        uint256 amount,
        address depositor,
        bytes memory signature
    )
        external
        view
        whenAuthorized(amount, depositor, signature)
        returns (bool)
    {
        return true;
    }
}

File 31 of 63 : SuspendableTest.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "../libraries/Suspendable.sol";

contract SuspendableTest is
    Initializable,
    AccessControlUpgradeable,
    Suspendable
{
    bool public state;

    function initialize() external initializer {
        __Suspendable_init(_msgSender());
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    function executeWhenNotPaused() external whenNotPaused {
        // should succeed only when contract is not paused
        state = !state;
    }

    function executeWhenPaused() external whenPaused {
        // should succeed only when contract is paused
        state = !state;
    }
}

File 32 of 63 : LockedLBToken.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "./libraries/PoolDepositable.sol";
import "./libraries/Tierable.sol";
import "./libraries/Suspendable.sol";
import "./libraries/PoolVestingDepositable.sol";

/** @title LockedLBToken.
 * @dev PoolDepositable contract implementation with tiers
 */
contract LockedLBToken is
    Initializable,
    PoolDepositable,
    Tierable,
    Suspendable,
    PoolVestingDepositable
{
    /**
     * @notice Initializer
     * @param _depositToken: the deposited token
     * @param tiersMinAmount: the tiers min amount
     * @param _pauser: the address of the account granted with PAUSER_ROLE
     */
    function initialize(
        IERC20Upgradeable _depositToken,
        uint256[] memory tiersMinAmount,
        address _pauser
    ) external initializer {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __Poolable_init_unchained();
        __Depositable_init_unchained(_depositToken);
        __PoolDepositable_init_unchained();
        __Tierable_init_unchained(tiersMinAmount);
        __Pausable_init_unchained();
        __Suspendable_init_unchained(_pauser);
        __PoolVestingable_init_unchained();
        __PoolVestingDepositable_init_unchained();
        __LockedLBToken_init_unchained();
    }

    function __LockedLBToken_init_unchained() internal onlyInitializing {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    function _deposit(
        address,
        address,
        uint256
    )
        internal
        pure
        override(PoolDepositable, Depositable, PoolVestingDepositable)
        returns (uint256)
    {
        revert("LockedLBToken: Must deposit with poolIndex");
    }

    function _withdraw(address, uint256)
        internal
        pure
        override(PoolDepositable, Depositable, PoolVestingDepositable)
        returns (uint256)
    {
        revert("LockedLBToken: Must withdraw with poolIndex");
    }

    function _withdraw(
        address,
        uint256,
        uint256
    )
        internal
        pure
        override(PoolDepositable, PoolVestingDepositable)
        returns (uint256)
    {
        revert("LockedLBToken: Must withdraw with on a specific pool type");
    }

    /**
     * @notice Deposit amount token in pool at index `poolIndex` to the sender address balance
     */
    function deposit(uint256 amount, uint256 poolIndex) external whenNotPaused {
        PoolDepositable._deposit(_msgSender(), _msgSender(), amount, poolIndex);
    }

    /**
     * @notice Withdraw amount token in pool at index `poolIndex` from the sender address balance
     */
    function withdraw(uint256 amount, uint256 poolIndex)
        external
        whenNotPaused
    {
        PoolDepositable._withdraw(_msgSender(), amount, poolIndex);
    }

    /**
     * @notice Batch deposits into a vesting pool
     */
    function vestingBatchDeposits(
        address from,
        address[] memory to,
        uint256[] memory amounts,
        uint256 poolIndex
    ) external whenNotPaused onlyRole(DEFAULT_ADMIN_ROLE) {
        PoolVestingDepositable._batchDeposits(from, to, amounts, poolIndex);
    }

    /**
     * @notice Withdraw from a vesting pool
     */
    function vestingWithdraw(uint256 amount, uint256 poolIndex)
        external
        whenNotPaused
    {
        PoolVestingDepositable._withdraw(_msgSender(), amount, poolIndex);
    }

    /**
     * @notice Batch transfer amount from one vesting pool deposit to another
     */
    function transferVestingPoolDeposits(
        address[] calldata accounts,
        uint256[] calldata amounts,
        uint256 fromPoolIndex,
        uint256 toPoolIndex
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(
            accounts.length == amounts.length,
            "LockedLBToken: account and amounts length are not equal"
        );
        for (uint256 i = 0; i < accounts.length; i++) {
            PoolVestingDepositable._transferVestingPoolDeposit(
                accounts[i],
                amounts[i],
                fromPoolIndex,
                toPoolIndex
            );
        }
    }

    uint256[50] private __gap;
}

File 33 of 63 : PoolDepositable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;
pragma abicoder v2;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
import "./Poolable.sol";
import "./Depositable.sol";

/** @title PoolDepositable.
@dev This contract manage pool of deposits
*/
abstract contract PoolDepositable is
    Initializable,
    AccessControlUpgradeable,
    Poolable,
    Depositable
{
    using SafeMathUpgradeable for uint256;

    struct UserPoolDeposit {
        uint256 poolIndex; // index of the pool
        uint256 amount; // amount deposited in the pool
        uint256 depositDate; // date of last deposit
    }

    struct BatchDeposit {
        address to; // destination address
        uint256 amount; // amount deposited
        uint256 poolIndex; // index of the pool
    }

    // mapping of deposits for a user
    mapping(address => UserPoolDeposit[]) private _poolDeposits;

    /**
     * @dev Emitted when a user deposit in a pool
     */
    event PoolDeposit(
        address indexed from,
        address indexed to,
        uint256 amount,
        uint256 poolIndex
    );

    /**
     * @dev Emitted when a user withdraw from a pool
     */
    event PoolWithdraw(address indexed to, uint256 amount, uint256 poolIndex);

    /**
     * @notice Initializer
     * @param _depositToken: the deposited token
     */
    function __PoolDepositable_init(IERC20Upgradeable _depositToken)
        internal
        onlyInitializing
    {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __Poolable_init_unchained();
        __Depositable_init_unchained(_depositToken);
        __PoolDepositable_init_unchained();
    }

    function __PoolDepositable_init_unchained() internal onlyInitializing {}

    /**
     * @dev returns the index of a user's pool deposit (`UserPoolDeposit`) for the specified pool at index `poolIndex`
     */
    function _indexOfPoolDeposit(address account, uint256 poolIndex)
        private
        view
        returns (int256)
    {
        for (uint256 i = 0; i < _poolDeposits[account].length; i++) {
            if (_poolDeposits[account][i].poolIndex == poolIndex) {
                return int256(i);
            }
        }
        return -1;
    }

    /**
     * @dev returns the list of pool deposits for an account
     */
    function poolDepositsOf(address account)
        public
        view
        returns (UserPoolDeposit[] memory)
    {
        return _poolDeposits[account];
    }

    /**
     * @dev returns the list of pool deposits for an account
     */
    function poolDepositOf(address account, uint256 poolIndex)
        external
        view
        returns (UserPoolDeposit memory poolDeposit)
    {
        int256 depositIndex = _indexOfPoolDeposit(account, poolIndex);
        if (depositIndex > -1) {
            poolDeposit = _poolDeposits[account][uint256(depositIndex)];
        }
    }

    // block the default implementation
    function _deposit(
        address,
        address,
        uint256
    ) internal pure virtual override returns (uint256) {
        revert("PoolDepositable: Must deposit with poolIndex");
    }

    // block the default implementation
    function _withdraw(address, uint256)
        internal
        pure
        virtual
        override
        returns (uint256)
    {
        revert("PoolDepositable: Must withdraw with poolIndex");
    }

    /**
     * @dev Deposit tokens to pool at `poolIndex`
     */
    function _deposit(
        address from,
        address to,
        uint256 amount,
        uint256 poolIndex
    ) internal virtual whenPoolOpened(poolIndex) returns (uint256) {
        uint256 depositAmount = Depositable._deposit(from, to, amount);

        int256 depositIndex = _indexOfPoolDeposit(to, poolIndex);
        if (depositIndex > -1) {
            UserPoolDeposit storage pool = _poolDeposits[to][
                uint256(depositIndex)
            ];
            pool.amount = pool.amount.add(depositAmount);
            pool.depositDate = block.timestamp; // update date to last deposit
        } else {
            _poolDeposits[to].push(
                UserPoolDeposit({
                    poolIndex: poolIndex,
                    amount: depositAmount,
                    depositDate: block.timestamp
                })
            );
        }

        emit PoolDeposit(from, to, amount, poolIndex);
        return depositAmount;
    }

    /**
     * @dev Withdraw tokens from a specific pool
     */
    function _withdrawPoolDeposit(
        address to,
        uint256 amount,
        UserPoolDeposit storage poolDeposit
    )
        private
        whenUnlocked(poolDeposit.poolIndex, poolDeposit.depositDate)
        returns (uint256)
    {
        require(
            poolDeposit.amount >= amount,
            "PoolDepositable: Pool deposit less than amount"
        );
        require(poolDeposit.amount > 0, "PoolDepositable: No deposit in pool");

        uint256 withdrawAmount = Depositable._withdraw(to, amount);
        poolDeposit.amount = poolDeposit.amount.sub(withdrawAmount);

        emit PoolWithdraw(to, amount, poolDeposit.poolIndex);
        return withdrawAmount;
    }

    /**
     * @dev Withdraw tokens from pool at `poolIndex`
     */
    function _withdraw(
        address to,
        uint256 amount,
        uint256 poolIndex
    ) internal virtual returns (uint256) {
        int256 depositIndex = _indexOfPoolDeposit(to, poolIndex);
        require(depositIndex > -1, "PoolDepositable: Not deposited");
        return
            _withdrawPoolDeposit(
                to,
                amount,
                _poolDeposits[to][uint256(depositIndex)]
            );
    }

    /**
     * @dev Batch deposits token in pools
     */
    function batchDeposits(address from, BatchDeposit[] memory deposits)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        for (uint256 i = 0; i < deposits.length; i++) {
            _deposit(
                from,
                deposits[i].to,
                deposits[i].amount,
                deposits[i].poolIndex
            );
        }
    }

    uint256[50] private __gap;
}

File 34 of 63 : Tierable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "./Depositable.sol";
import "../interfaces/ITierable.sol";

/** @title Tierable.
 * @dev Depositable contract implementation with tiers
 */
abstract contract Tierable is
    Initializable,
    AccessControlUpgradeable,
    Depositable,
    ITierable
{
    using EnumerableSet for EnumerableSet.AddressSet;

    uint256[] private _tiersMinAmount;
    EnumerableSet.AddressSet private _whitelist;

    /**
     * @dev Emitted when tiers amount are changed
     */
    event TiersMinAmountChange(uint256[] amounts);

    /**
     * @dev Emitted when a new account is added to the whitelist
     */
    event AddToWhitelist(address account);

    /**
     * @dev Emitted when an account is removed from the whitelist
     */
    event RemoveFromWhitelist(address account);

    /**
     * @notice Initializer
     * @param _depositToken: the deposited token
     * @param tiersMinAmount: the tiers min amount
     */
    function __Tierable_init(
        IERC20Upgradeable _depositToken,
        uint256[] memory tiersMinAmount
    ) internal onlyInitializing {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __Depositable_init_unchained(_depositToken);
        __Tierable_init_unchained(tiersMinAmount);
    }

    function __Tierable_init_unchained(uint256[] memory tiersMinAmount)
        internal
        onlyInitializing
    {
        _tiersMinAmount = tiersMinAmount;
    }

    /**
     * @dev Returns the index of the tier for `account`
     * @notice returns -1 if the total deposit of `account` is below the first tier
     */
    function tierOf(address account) public view override returns (int256) {
        // set max tier
        uint256 max = _tiersMinAmount.length;

        // check if account in whitelist
        if (_whitelist.contains(account)) {
            // return max tier
            return int256(max) - 1;
        }

        // check balance of account
        uint256 balance = depositOf(account);
        for (uint256 i = 0; i < max; i++) {
            // return its tier
            if (balance < _tiersMinAmount[i]) return int256(i) - 1;
        }
        // return max tier if balance more than last tiersMinAmount
        return int256(max) - 1;
    }

    /**
     * @notice update the tiers brackets
     * Only callable by owners
     */
    function changeTiersMinAmount(uint256[] memory tiersMinAmount)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        _tiersMinAmount = tiersMinAmount;
        emit TiersMinAmountChange(_tiersMinAmount);
    }

    /**
     * @notice returns the list of min amount per tier
     */
    function getTiersMinAmount() external view returns (uint256[] memory) {
        return _tiersMinAmount;
    }

    /**
     * @notice Add new accounts to the whitelist
     * Only callable by owners
     */
    function addToWhitelist(address[] memory accounts)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        for (uint256 i = 0; i < accounts.length; i++) {
            bool result = _whitelist.add(accounts[i]);
            if (result) emit AddToWhitelist(accounts[i]);
        }
    }

    /**
     * @notice Remove an account from the whitelist
     * Only callable by owners
     */
    function removeFromWhitelist(address[] memory accounts)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        for (uint256 i = 0; i < accounts.length; i++) {
            bool result = _whitelist.remove(accounts[i]);
            if (result) emit RemoveFromWhitelist(accounts[i]);
        }
    }

    /**
     * @notice Remove accounts from whitelist
     * Only callable by owners
     */
    function getWhitelist()
        external
        view
        onlyRole(DEFAULT_ADMIN_ROLE)
        returns (address[] memory)
    {
        return _whitelist.values();
    }

    uint256[50] private __gap;
}

File 35 of 63 : PoolVestingDepositable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;
pragma abicoder v2;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
import "./PoolVestingable.sol";
import "./Depositable.sol";

/** @title PoolVestingDepositable.
@dev This contract manage deposits in vesting pools
*/
abstract contract PoolVestingDepositable is
    Initializable,
    PoolVestingable,
    Depositable
{
    using SafeMathUpgradeable for uint256;

    struct UserVestingPoolDeposit {
        uint256 initialAmount; // initial amount deposited in the pool
        uint256 withdrawnAmount; // amount already withdrawn from the pool
    }

    // mapping of deposits for a user
    // user -> pool index -> user deposit
    mapping(address => mapping(uint256 => UserVestingPoolDeposit))
        private _poolDeposits;

    /**
     * @dev Emitted when a user deposit in a pool
     */
    event VestingPoolDeposit(
        address indexed from,
        address indexed to,
        uint256 amount,
        uint256 poolIndex
    );

    /**
     * @dev Emitted when a user withdraw from a pool
     */
    event VestingPoolWithdraw(
        address indexed to,
        uint256 amount,
        uint256 poolIndex
    );

    /**
     * @dev Emitted when a user deposit is transferred to another pool
     */
    event VestingPoolTransfer(
        address indexed account,
        uint256 amount,
        uint256 fromPoolIndex,
        uint256 toPoolIndex
    );

    /**
     * @notice Initializer
     * @param _depositToken: the deposited token
     */
    function __PoolVestingDepositable_init(IERC20Upgradeable _depositToken)
        internal
        onlyInitializing
    {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __PoolVestingable_init_unchained();
        __Depositable_init_unchained(_depositToken);
        __PoolVestingDepositable_init_unchained();
    }

    function __PoolVestingDepositable_init_unchained()
        internal
        onlyInitializing
    {}

    /**
     * @dev returns the vested amount of a pool deposit
     */
    function _vestedAmountOf(address account, uint256 poolIndex)
        private
        view
        returns (uint256 vestedAmount)
    {
        VestingPool memory pool = getVestingPool(poolIndex);
        for (uint256 i = 0; i < pool.timestamps.length; i++) {
            if (block.timestamp >= pool.timestamps[i]) {
                // this schedule is reached, calculate its amount
                uint256 scheduleAmount = _poolDeposits[account][poolIndex]
                    .initialAmount
                    .mul(pool.ratiosPerHundredThousand[i])
                    .div(100000);
                // add it to vested amount
                vestedAmount = vestedAmount.add(scheduleAmount);
            }
        }
    }

    /**
     * @dev returns the amount that can be withdraw from a pool deposit
     */
    function _withdrawableAmountOf(address account, uint256 poolIndex)
        private
        view
        returns (uint256)
    {
        require(
            poolIndex < vestingPoolsLength(),
            "PoolVestingDepositable: Invalid poolIndex"
        );
        return
            _vestedAmountOf(account, poolIndex).sub(
                _poolDeposits[account][poolIndex].withdrawnAmount
            );
    }

    /**
     * @dev returns the list of pool deposits for an account
     */
    function vestingPoolDepositOf(address account, uint256 poolIndex)
        external
        view
        returns (UserVestingPoolDeposit memory)
    {
        require(
            poolIndex < vestingPoolsLength(),
            "PoolVestingDepositable: Invalid poolIndex"
        );
        return _poolDeposits[account][poolIndex];
    }

    /**
     * @dev returns vested amount of an account for a specific pool. Public version
     */
    function vestingPoolVestedAmountOf(address account, uint256 poolIndex)
        external
        view
        returns (uint256)
    {
        return _vestedAmountOf(account, poolIndex);
    }

    /**
     * @dev returns the amount that can be withdraw from a pool
     */
    function vestingPoolWithdrawableAmountOf(address account, uint256 poolIndex)
        external
        view
        returns (uint256)
    {
        return _withdrawableAmountOf(account, poolIndex);
    }

    // block the default implementation
    function _deposit(
        address,
        address,
        uint256
    ) internal pure virtual override returns (uint256) {
        revert("PoolVestingDepositable: Must deposit with poolIndex");
    }

    // block the default implementation
    function _withdraw(address, uint256)
        internal
        pure
        virtual
        override
        returns (uint256)
    {
        revert("PoolVestingDepositable: Must withdraw with poolIndex");
    }

    /**
     * @dev Deposit tokens to pool at `poolIndex`
     */
    function _savePoolDeposit(
        address from,
        address to,
        uint256 amount,
        uint256 poolIndex
    ) private {
        require(
            poolIndex < vestingPoolsLength(),
            "PoolVestingDepositable: Invalid poolIndex"
        );
        UserVestingPoolDeposit storage poolDeposit = _poolDeposits[to][
            poolIndex
        ];
        poolDeposit.initialAmount = poolDeposit.initialAmount.add(amount);
        emit VestingPoolDeposit(from, to, amount, poolIndex);
    }

    /**
     * @dev Batch deposit tokens to pool at `poolIndex`
     */
    function _batchDeposits(
        address from,
        address[] memory to,
        uint256[] memory amounts,
        uint256 poolIndex
    ) internal virtual returns (uint256) {
        require(
            to.length == amounts.length,
            "PoolVestingDepositable: arrays to and amounts have different length"
        );

        uint256 totalTransferredAmount = 0;
        for (uint256 i = 0; i < amounts.length; i++) {
            uint256 transferredAmount = Depositable._deposit(
                from,
                to[i],
                amounts[i]
            );
            _savePoolDeposit(from, to[i], transferredAmount, poolIndex);
            totalTransferredAmount = totalTransferredAmount.add(
                transferredAmount
            );
        }

        return totalTransferredAmount;
    }

    /**
     * @dev Withdraw tokens from pool at `poolIndex`
     */
    function _withdraw(
        address to,
        uint256 amount,
        uint256 poolIndex
    ) internal virtual returns (uint256) {
        require(
            poolIndex < vestingPoolsLength(),
            "PoolVestingDepositable: Invalid poolIndex"
        );
        UserVestingPoolDeposit storage poolDeposit = _poolDeposits[to][
            poolIndex
        ];
        uint256 withdrawableAmount = _withdrawableAmountOf(to, poolIndex);

        require(
            withdrawableAmount >= amount,
            "PoolVestingDepositable: Withdrawable amount less than amount to withdraw"
        );
        require(
            withdrawableAmount > 0,
            "PoolVestingDepositable: No withdrawable amount to withdraw"
        );

        uint256 withdrawAmount = Depositable._withdraw(to, amount);
        poolDeposit.withdrawnAmount = poolDeposit.withdrawnAmount.add(
            withdrawAmount
        );

        emit VestingPoolWithdraw(to, amount, poolIndex);
        return withdrawAmount;
    }

    /**
     * @dev Transfer amount from one vesting pool deposit to another
     */
    function _transferVestingPoolDeposit(
        address account,
        uint256 amount,
        uint256 fromPoolIndex,
        uint256 toPoolIndex
    ) internal {
        require(
            fromPoolIndex < vestingPoolsLength(),
            "PoolVestingDepositable: Invalid fromPoolIndex"
        );
        require(
            toPoolIndex < vestingPoolsLength(),
            "PoolVestingDepositable: Invalid toPoolIndex"
        );

        UserVestingPoolDeposit storage poolDepositFrom = _poolDeposits[account][
            fromPoolIndex
        ];
        UserVestingPoolDeposit storage poolDepositTo = _poolDeposits[account][
            toPoolIndex
        ];

        require(
            poolDepositTo.withdrawnAmount == 0,
            "PoolVestingDepositable: Cannot transfer amount if withdrawnAmount is not equal to 0"
        );

        // update initial amount
        poolDepositTo.initialAmount = poolDepositTo.initialAmount.add(amount);
        poolDepositFrom.initialAmount = poolDepositFrom.initialAmount.sub(
            amount
        );

        emit VestingPoolTransfer(account, amount, fromPoolIndex, toPoolIndex);
    }

    uint256[50] private __gap;
}

File 36 of 63 : Poolable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;
pragma abicoder v2;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";

/** @title Poolable.
@dev This contract manage configuration of pools
*/
abstract contract Poolable is Initializable, AccessControlUpgradeable {
    using SafeMathUpgradeable for uint256;

    struct Pool {
        uint256 lockDuration; // locked timespan
        bool opened; // flag indicating if the pool is open
    }

    // pools mapping
    mapping(uint256 => Pool) private _pools;
    uint256 public poolsLength;

    /**
     * @dev Emitted when a pool is created
     */
    event PoolAdded(uint256 poolIndex, Pool pool);

    /**
     * @dev Emitted when a pool is updated
     */
    event PoolUpdated(uint256 poolIndex, Pool pool);

    /**
     * @dev Modifier that checks that the pool at index `poolIndex` is open
     */
    modifier whenPoolOpened(uint256 poolIndex) {
        require(poolIndex < poolsLength, "Poolable: Invalid poolIndex");
        require(_pools[poolIndex].opened, "Poolable: Pool is closed");
        _;
    }

    /**
     * @dev Modifier that checks that the now() - `depositDate` is above or equal to the min lock duration for pool at index `poolIndex`
     */
    modifier whenUnlocked(uint256 poolIndex, uint256 depositDate) {
        require(poolIndex < poolsLength, "Poolable: Invalid poolIndex");
        require(
            depositDate < block.timestamp,
            "Poolable: Invalid deposit date"
        );
        require(
            block.timestamp - depositDate >= _pools[poolIndex].lockDuration,
            "Poolable: Not unlocked"
        );
        _;
    }

    /**
     * @notice Initializer
     */
    function __Poolable_init() internal onlyInitializing {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __Poolable_init_unchained();
    }

    function __Poolable_init_unchained() internal onlyInitializing {}

    function getPool(uint256 poolIndex) public view returns (Pool memory) {
        require(poolIndex < poolsLength, "Poolable: Invalid poolIndex");
        return _pools[poolIndex];
    }

    function addPool(Pool calldata pool) external onlyRole(DEFAULT_ADMIN_ROLE) {
        uint256 poolIndex = poolsLength;

        _pools[poolIndex] = Pool({
            lockDuration: pool.lockDuration,
            opened: pool.opened
        });
        poolsLength = poolsLength + 1;

        emit PoolAdded(poolIndex, _pools[poolIndex]);
    }

    function updatePool(uint256 poolIndex, Pool calldata pool)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(poolIndex < poolsLength, "Poolable: Invalid poolIndex");
        Pool storage editedPool = _pools[poolIndex];

        editedPool.lockDuration = pool.lockDuration;
        editedPool.opened = pool.opened;

        emit PoolUpdated(poolIndex, editedPool);
    }

    uint256[50] private __gap;
}

File 37 of 63 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastvalue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastvalue;
                // Update the index for the moved value
                set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly {
            result := store
        }

        return result;
    }
}

File 38 of 63 : PoolVestingable.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;
pragma abicoder v2;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";

/** @title PoolVestingable.
@dev This contract manage configuration of vesting pools
*/
abstract contract PoolVestingable is Initializable, AccessControlUpgradeable {
    using SafeMathUpgradeable for uint256;

    struct VestingPool {
        uint256[] timestamps; // Timestamp at which the associated ratio is available.
        uint256[] ratiosPerHundredThousand; // Ratio of initial amount to be available at the associated timestamp in / 100,000 (100% = 100,000, 1% = 1,000)
    }

    // pools
    VestingPool[] private _pools;

    /**
     * @dev Emitted when a pool is created
     */
    event VestingPoolAdded(uint256 poolIndex, VestingPool pool);

    /**
     * @dev Emitted when a pool is updated
     */
    event VestingPoolUpdated(uint256 poolIndex, VestingPool pool);

    /**
     * @dev Modifier that checks pool is valid
     */
    modifier checkVestingPool(VestingPool calldata pool) {
        // check length of timestamps and ratiosPerHundredThousand are equal
        require(
            pool.timestamps.length == pool.ratiosPerHundredThousand.length,
            "PoolVestingable: Number of timestamps is not equal to number of ratios"
        );

        // check the timestamps are increasing
        // start at i = 1
        for (uint256 i = 1; i < pool.timestamps.length; i++) {
            require(
                pool.timestamps[i - 1] < pool.timestamps[i],
                "PoolVestingable: Timestamps be asc ordered"
            );
        }

        // check sum of ratios = 100,000
        uint256 totalRatio = 0;
        for (uint256 i = 0; i < pool.ratiosPerHundredThousand.length; i++) {
            totalRatio = totalRatio.add(pool.ratiosPerHundredThousand[i]);
        }
        require(
            totalRatio == 100000,
            "PoolVestingable: Sum of ratios per thousand must be equal to 100,000"
        );

        _;
    }

    /**
     * @notice Initializer
     */
    function __PoolVestingable_init() internal onlyInitializing {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __PoolVestingable_init_unchained();
    }

    function __PoolVestingable_init_unchained() internal onlyInitializing {}

    function getVestingPool(uint256 poolIndex)
        public
        view
        returns (VestingPool memory)
    {
        require(
            poolIndex < _pools.length,
            "PoolVestingable: Invalid poolIndex"
        );
        return _pools[poolIndex];
    }

    function vestingPoolsLength() public view returns (uint256) {
        return _pools.length;
    }

    function addVestingPool(VestingPool calldata pool)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
        checkVestingPool(pool)
    {
        _pools.push(
            VestingPool({
                timestamps: pool.timestamps,
                ratiosPerHundredThousand: pool.ratiosPerHundredThousand
            })
        );

        emit VestingPoolAdded(_pools.length - 1, _pools[_pools.length - 1]);
    }

    function updateVestingPool(uint256 poolIndex, VestingPool calldata pool)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
        checkVestingPool(pool)
    {
        require(
            poolIndex < _pools.length,
            "PoolVestingable: Invalid poolIndex"
        );
        VestingPool storage editedPool = _pools[poolIndex];

        editedPool.timestamps = pool.timestamps;
        editedPool.ratiosPerHundredThousand = pool.ratiosPerHundredThousand;

        emit VestingPoolUpdated(poolIndex, editedPool);
    }

    uint256[50] private __gap;
}

File 39 of 63 : PoolVestingDepositableTest.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "../libraries/PoolVestingDepositable.sol";

contract PoolVestingDepositableTest is Initializable, PoolVestingDepositable {
    function initialize(IERC20Upgradeable _depositToken) external initializer {
        __PoolVestingDepositable_init(_depositToken);
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    function withdraw(uint256 amount, uint256 poolIndex) external {
        _withdraw(_msgSender(), amount, poolIndex);
    }

    function depositNoPool(uint256 amount) external view {
        // should fail
        _deposit(_msgSender(), _msgSender(), amount);
    }

    function withdrawNoPool(uint256 amount) external view {
        // should fail
        _withdraw(_msgSender(), amount);
    }

    function batchDeposits(
        address from,
        address[] memory to,
        uint256[] memory amounts,
        uint256 poolIndex
    ) external {
        _batchDeposits(from, to, amounts, poolIndex);
    }

    function transferVestingPoolDeposit(
        address account,
        uint256 amount,
        uint256 fromPoolIndex,
        uint256 toPoolIndex
    ) external {
        _transferVestingPoolDeposit(
            account,
            amount,
            fromPoolIndex,
            toPoolIndex
        );
    }
}

File 40 of 63 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role, _msgSender());
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(uint160(account), 20),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

File 41 of 63 : IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

File 42 of 63 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

File 43 of 63 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Strings.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

File 44 of 63 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 45 of 63 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 46 of 63 : TierableTest.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "../libraries/Tierable.sol";

contract TierableTest is Tierable {
    function initialize(
        IERC20Upgradeable _depositToken,
        uint256[] memory _tiersMinAmount
    ) external initializer {
        __Tierable_init(_depositToken, _tiersMinAmount);
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    function deposit(uint256 amount) external {
        _deposit(_msgSender(), _msgSender(), amount);
    }
}

File 47 of 63 : MaxDepositableTest.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "../libraries/Depositable.sol";
import "../libraries/MaxDepositable.sol";

contract MaxDepositableTest is
    Initializable,
    AccessControlUpgradeable,
    MaxDepositable
{
    function initialize(
        IERC20Upgradeable _depositToken,
        uint256 _maxTotalDeposit
    ) external initializer {
        __MaxDepositable_init(_depositToken, _maxTotalDeposit);
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    function deposit(uint256 amount) external {
        _deposit(_msgSender(), _msgSender(), amount);
    }

    function withdraw(uint256 amount) external {
        _withdraw(_msgSender(), amount);
    }
}

File 48 of 63 : MaxTierDepositableTest.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "../libraries/MaxTierDepositable.sol";
import "../interfaces/ITierable.sol";

contract MaxTierDepositableTest is Initializable, MaxTierDepositable {
    function initialize(
        IERC20Upgradeable _depositToken,
        ITierable _lockedToken,
        uint256[] memory _tiersMaxDeposit
    ) external initializer {
        __MaxTierDepositable_init(
            _depositToken,
            _lockedToken,
            _tiersMaxDeposit
        );
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    function deposit(uint256 amount) external {
        _deposit(_msgSender(), _msgSender(), amount);
    }
}

File 49 of 63 : PoolableV2TestBase.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;
pragma abicoder v2;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";

/** @title PoolableV2TestBase.
@dev TEST ONLY, DO NOT USE IN PRODUCTION
*/
abstract contract PoolableV2TestBase is
    Initializable,
    AccessControlUpgradeable
{
    using SafeMathUpgradeable for uint256;

    struct Pool {
        uint256 lockDuration; // locked timespan
        bool opened; // flag indicating if the pool is open
        uint256 depositFee;
        string name;
    }

    // pools mapping
    mapping(uint256 => Pool) private _pools;
    uint256 public poolsLength;

    /**
     * @dev Emitted when a pool is created
     */
    event PoolAdded(uint256 poolIndex, Pool pool);

    /**
     * @dev Emitted when a pool is updated
     */
    event PoolUpdated(uint256 poolIndex, Pool pool);

    /**
     * @dev Modifier that checks that the pool at index `poolIndex` is open
     */
    modifier whenPoolOpened(uint256 poolIndex) {
        require(poolIndex < poolsLength, "Poolable: Invalid poolIndex");
        require(_pools[poolIndex].opened, "Poolable: Pool is closed");
        _;
    }

    /**
     * @dev Modifier that checks that the now() - `depositDate` is above or equal to the min lock duration for pool at index `poolIndex`
     */
    modifier whenUnlocked(uint256 poolIndex, uint256 depositDate) {
        require(poolIndex < poolsLength, "Poolable: Invalid poolIndex");
        require(
            depositDate < block.timestamp,
            "Poolable: Invalid deposit date"
        );
        require(
            block.timestamp - depositDate >= _pools[poolIndex].lockDuration,
            "Poolable: Not unlocked"
        );
        _;
    }

    /**
     * @notice Initializer
     */
    function __Poolable_init() internal onlyInitializing {
        __AccessControl_init_unchained();
        __Poolable_init_unchained();
    }

    function __Poolable_init_unchained() internal onlyInitializing {}

    function getPool(uint256 poolIndex) public view returns (Pool memory) {
        require(poolIndex < poolsLength, "Poolable: Invalid poolIndex");
        return _pools[poolIndex];
    }

    function addPool(Pool calldata pool) external onlyRole(DEFAULT_ADMIN_ROLE) {
        uint256 poolIndex = poolsLength;

        _pools[poolIndex] = Pool({
            lockDuration: pool.lockDuration,
            opened: pool.opened,
            depositFee: pool.depositFee,
            name: pool.name
        });
        poolsLength = poolsLength + 1;

        emit PoolAdded(poolIndex, _pools[poolIndex]);
    }

    function updatePool(uint256 poolIndex, Pool calldata pool)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(poolIndex < poolsLength, "Poolable: Invalid poolIndex");
        Pool storage editedPool = _pools[poolIndex];

        editedPool.lockDuration = pool.lockDuration;
        editedPool.opened = pool.opened;
        editedPool.depositFee = pool.depositFee;
        editedPool.name = pool.name;

        emit PoolUpdated(poolIndex, editedPool);
    }

    uint256[50] private __gap;
}

File 50 of 63 : SchedulableTest.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "../libraries/Schedulable.sol";

contract SchedulableTest is
    Initializable,
    AccessControlUpgradeable,
    Schedulable
{
    bool public whenOpenExecuted;
    bool public whenCloseExecuted;

    function initialize(uint256 _startDate, uint256 _endDate)
        external
        initializer
    {
        __Schedulable_init(_startDate, _endDate);
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    function executeWhenOpened() external whenOpened {
        whenOpenExecuted = true;
    }

    function executeWhenClosed() external whenClosed {
        whenCloseExecuted = true;
    }
}

File 51 of 63 : PoolVestingableTest.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "../libraries/PoolVestingable.sol";

contract PoolVestingableTest is
    Initializable,
    AccessControlUpgradeable,
    PoolVestingable
{
    function initialize() external initializer {
        __PoolVestingable_init();
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }
}

File 52 of 63 : PoolFarmableTest.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "../libraries/PoolFarmable.sol";

contract PoolFarmableTest is
    Initializable,
    AccessControlUpgradeable,
    PoolFarmable
{
    function initialize() external initializer {
        __PoolFarmable_init();
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }
}

File 53 of 63 : PoolDepositableTest.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "../libraries/PoolDepositable.sol";

contract PoolDepositableTest is
    Initializable,
    AccessControlUpgradeable,
    PoolDepositable
{
    function initialize(IERC20Upgradeable _depositToken) external initializer {
        __PoolDepositable_init(_depositToken);
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    function deposit(uint256 amount, uint256 poolIndex) external {
        _deposit(_msgSender(), _msgSender(), amount, poolIndex);
    }

    function withdraw(uint256 amount, uint256 poolIndex) external {
        _withdraw(_msgSender(), amount, poolIndex);
    }

    function depositNoPool(uint256 amount) external view {
        // should fail
        _deposit(_msgSender(), _msgSender(), amount);
    }

    function withdrawNoPool(uint256 amount) external view {
        // should fail
        _withdraw(_msgSender(), amount);
    }
}

File 54 of 63 : PoolableTest.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "../libraries/Poolable.sol";

contract PoolableTest is Initializable, AccessControlUpgradeable, Poolable {
    function initialize() external initializer {
        __Poolable_init();
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    function executeWhenPoolOpened(uint256 poolIndex)
        external
        view
        whenPoolOpened(poolIndex)
        returns (bool)
    {
        return true;
    }

    function executeWhenUnlocked(uint256 poolIndex, uint256 depositDate)
        external
        view
        whenUnlocked(poolIndex, depositDate)
        returns (bool)
    {
        return true;
    }
}

File 55 of 63 : PoolableTestV2.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "./PoolableV2TestBase.sol";

contract PoolableTestV2 is
    Initializable,
    AccessControlUpgradeable,
    PoolableV2TestBase
{
    function initialize() external initializer {
        __Poolable_init();
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    function executeWhenPoolOpened(uint256 poolIndex)
        external
        view
        whenPoolOpened(poolIndex)
        returns (bool)
    {
        return true;
    }

    function executeWhenUnlocked(uint256 poolIndex, uint256 depositDate)
        external
        view
        whenUnlocked(poolIndex, depositDate)
        returns (bool)
    {
        return true;
    }
}

File 56 of 63 : DepositableTest.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "../libraries/Depositable.sol";

contract DepositableTest is
    Initializable,
    AccessControlUpgradeable,
    Depositable
{
    function initialize(IERC20Upgradeable _depositToken) external initializer {
        __Depositable_init(_depositToken);
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    function deposit(uint256 amount) external {
        _deposit(_msgSender(), _msgSender(), amount);
    }

    function withdraw(uint256 amount) external {
        _withdraw(_msgSender(), amount);
    }
}

File 57 of 63 : CollectableTest.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "../libraries/Depositable.sol";
import "../libraries/Collectable.sol";

contract CollectableTest is Collectable {
    function initialize(IERC20Upgradeable _depositToken) external initializer {
        __Collectable_init(_depositToken, _msgSender());
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

    function deposit(uint256 amount) external {
        _deposit(_msgSender(), _msgSender(), amount);
    }

    function collect(uint256 amount) external {
        _collect(_msgSender(), amount);
    }
}

File 58 of 63 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

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

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

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

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

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

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

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

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

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

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

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

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

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

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

        return true;
    }

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

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

        return true;
    }

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

        _beforeTokenTransfer(sender, recipient, amount);

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

        emit Transfer(sender, recipient, amount);

        _afterTokenTransfer(sender, recipient, amount);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

File 59 of 63 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 60 of 63 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

File 61 of 63 : ERC20Test.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

// ERC20 Token that let every body mint tokens for test
contract ERC20Test is ERC20 {
    constructor(string memory _name, string memory _symbol)
        ERC20(_name, _symbol)
    {}

    function mint(address to, uint256 amount) external {
        _mint(to, amount);
    }

    function mint(uint256 amount) external {
        _mint(msg.sender, amount);
    }
}

File 62 of 63 : LBToken.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "./ERC20Test.sol";

contract LBToken is ERC20Test("LB Token", "LBT") {}

File 63 of 63 : DepositToken.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "./ERC20Test.sol";

contract DepositToken is ERC20Test("Deposit Token", "DPT") {}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 500
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  }
}

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"destination","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Collect","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"DepositTokenChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"date","type":"uint256"}],"name":"EndDateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"MaxTotalDepositChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"date","type":"uint256"}],"name":"StartDateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"TiersMaxAmountDepositChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"AUTHORIZER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COLLECTOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEPOSIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Upgradeable","name":"_depositToken","type":"address"}],"name":"changeDepositToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_endDate","type":"uint256"}],"name":"changeEndDate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"changeMaxTotalDeposited","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_startDate","type":"uint256"}],"name":"changeStartDate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"changeTiersMaxDepositAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collect","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"depositOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"depositToken","outputs":[{"internalType":"contract IERC20Upgradeable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTiersMaxDepositAmount","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20Upgradeable","name":"depositToken","type":"address"},{"internalType":"uint256","name":"maxTotalDeposit","type":"uint256"},{"internalType":"contract ITierable","name":"lockedToken","type":"address"},{"internalType":"uint256[]","name":"tiersMaxDeposit","type":"uint256[]"},{"internalType":"uint256","name":"startDate","type":"uint256"},{"internalType":"uint256","name":"endDate","type":"uint256"},{"internalType":"address","name":"authorizer","type":"address"},{"internalType":"address","name":"collector","type":"address"},{"internalType":"address","name":"pauser","type":"address"}],"internalType":"struct Sale.SaleConfiguration","name":"configuration","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockedToken","outputs":[{"internalType":"contract ITierable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTotalDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resume","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"suspend","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"suspended","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCollected","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b50612795806100206000396000f3fe608060405234801561001057600080fd5b50600436106101ef5760003560e01c80636273ab931161010f578063c24a0f8b116100a2578063e522538111610071578063e522538114610483578063e63ab1e91461048b578063e6400bbe146104b2578063f6153ccd146104ba57600080fd5b8063c24a0f8b14610449578063c89039c514610453578063d547741f14610466578063e29eb8361461047957600080fd5b80639ca39ae9116100de5780639ca39ae9146103e0578063a217fddf14610407578063bc06946f1461040f578063c0acafa51461043657600080fd5b80636273ab9314610383578063702efdf31461039657806371a6cab71461039e57806391d14854146103a757600080fd5b8063248a9ca31161018757806348825e941161015657806348825e941461032a5780635988764d146103515780635c975abb146103645780635d3035191461037057600080fd5b8063248a9ca3146102ce5780632f2ff15d146102f157806336568abe1461030457806345737b1e1461031757600080fd5b80630b97bc86116101c35780630b97bc861461024c5780630f45cc811461026457806315e592ae1461029057806323e3fbd5146102a557600080fd5b8062739f2a146101f457806301ffc9a714610209578063046f7da21461023157806307ad088d14610239575b600080fd5b610207610202366004612296565b6104c3565b005b61021c6102173660046122dd565b61055b565b60405190151581526020015b60405180910390f35b610207610592565b610207610247366004612243565b6105c8565b6102566101335481565b604051908152602001610228565b61010054610278906001600160a01b031681565b6040516001600160a01b039091168152602001610228565b610298610624565b6040516102289190612535565b6102566102b3366004612227565b6001600160a01b031660009081526097602052604090205490565b6102566102dc366004612296565b60009081526065602052604090206001015490565b6102076102ff3660046122ae565b61067c565b6102076103123660046122ae565b6106a7565b610207610325366004612296565b610733565b6102567fb64321feb52d8980c1d31ee858f482f2f5cab15642d381cafe591f755eb50a2981565b61020761035f36600461231d565b6107c6565b6101675460ff1661021c565b61020761037e3660046123ff565b610962565b610207610391366004612227565b610ad6565b61021c610b7d565b61025660cc5481565b61021c6103b53660046122ae565b60009182526065602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6102567f14cf45180c3fcf249a5a305e9657ea05c14fd4f4e1800ee0216a8213091711d281565b610256600081565b6102567f14dd327f3834be9d0f7cf44f6cf11c96ded83bd68d1a1b3926d35739e7bb88d081565b610207610444366004612296565b610b91565b6102566101345481565b609854610278906001600160a01b031681565b6102076104743660046122ae565b610c2d565b6102566101cb5481565b610207610c53565b6102567f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b610207610d74565b61025660995481565b60006104cf8133610da7565b6101345482106105265760405162461bcd60e51b815260206004820152601f60248201527f5363686564756c61626c653a20696e76616c696420737461727420646174650060448201526064015b60405180910390fd5b61013382905560405182907f340656eb96ec8f1ab4ba468e243fe2191f09ec5e2a832f96985750414221a7a590600090a25050565b60006001600160e01b03198216637965db0b60e01b148061058c57506301ffc9a760e01b6001600160e01b03198316145b92915050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a6105bd8133610da7565b6105c5610e27565b50565b60006105d48133610da7565b81516105e79060ff906020850190612134565b507ff96d80dccbfade17503389247513551c253bf8144572cbe56f4da1d6833d019f60ff6040516106189190612579565b60405180910390a15050565b606060ff80548060200260200160405190810160405280929190818152602001828054801561067257602002820191906000526020600020905b81548152602001906001019080831161065e575b5050505050905090565b6000828152606560205260409020600101546106988133610da7565b6106a28383610ec5565b505050565b6001600160a01b03811633146107255760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c660000000000000000000000000000000000606482015260840161051d565b61072f8282610f67565b5050565b600061073f8133610da7565b6101335482116107915760405162461bcd60e51b815260206004820152601d60248201527f5363686564756c61626c653a20696e76616c696420656e642064617465000000604482015260640161051d565b61013482905560405182907f78f20fcb2c7d71918cb4f6b5e6c6eb0bf4ed26c77d48c64a29c6f448c40d9ae890600090a25050565b600054610100900460ff166107e15760005460ff16156107e5565b303b155b6108575760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840161051d565b600054610100900460ff16158015610879576000805461ffff19166101011790555b610881610fea565b610889610fea565b610891610fea565b815161089c90611013565b6108a9826020015161105c565b6108bb82604001518360600151611088565b6108cd82608001518360a001516110de565b6108d5611112565b6108e3826101000151611146565b6108f08260e00151611197565b6109386040518060400160405280600b81526020016a4c61756e6368626c6f636b60a81b815250604051806040016040528060038152602001620312e360ec1b8152506111e8565b6109458260c0015161122b565b61094d61127c565b801561072f576000805461ff00191690555050565b6101335442101580156109785750610134544211155b6109c45760405162461bcd60e51b815260206004820152601760248201527f5363686564756c61626c653a204e6f74206f70656e6564000000000000000000604482015260640161051d565b6101675460ff1615610a0b5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015260640161051d565b8133826000610a1a84846112ae565b9050610a268183611312565b610a725760405162461bcd60e51b815260206004820152601c60248201527f417574686f72697a61626c653a206e6f7420617574686f72697a656400000000604482015260640161051d565b60008611610ac25760405162461bcd60e51b815260206004820152601860248201527f53616c653a20616d6f756e74206d757374206265203e20300000000000000000604482015260640161051d565b610acd3333886113b5565b50505050505050565b6000610ae28133610da7565b60995415610b325760405162461bcd60e51b815260206004820152601f60248201527f4465706f73697461626c653a20746f74616c206465706f73697420213d203000604482015260640161051d565b609880546001600160a01b0319166001600160a01b0384169081179091556040517fa59a70f0dc526699eeba221f83942d7eb396e238fb52deb2e0fdf9e8044ec50390600090a25050565b6000610b8c6101675460ff1690565b905090565b6000610b9d8133610da7565b811580610bac57506099548210155b610bf85760405162461bcd60e51b815260206004820152601860248201527f4465706f73697461626c653a206d6178203c20746f74616c0000000000000000604482015260640161051d565b60cc8290556040518281527fdca7855fb2f3586876bce97c855aa88e8e3f83edd1a6c9c09444bd884284aabe90602001610618565b600082815260656020526040902060010154610c498133610da7565b6106a28383610f67565b610134544211610ca55760405162461bcd60e51b815260206004820152601760248201527f5363686564756c61626c653a204e6f7420636c6f736564000000000000000000604482015260640161051d565b6101675460ff1615610cec5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015260640161051d565b6098546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b158015610d3057600080fd5b505afa158015610d44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d689190612305565b90506105c53382611438565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610d9f8133610da7565b6105c5611598565b60008281526065602090815260408083206001600160a01b038516845290915290205460ff1661072f57610de5816001600160a01b03166014611615565b610df0836020611615565b604051602001610e019291906124b4565b60408051601f198184030181529082905262461bcd60e51b825261051d916004016125b4565b6101675460ff16610e7a5760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f7420706175736564000000000000000000000000604482015260640161051d565b610167805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b60008281526065602090815260408083206001600160a01b038516845290915290205460ff1661072f5760008281526065602090815260408083206001600160a01b03851684529091529020805460ff19166001179055610f233390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60008281526065602090815260408083206001600160a01b038516845290915290205460ff161561072f5760008281526065602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b600054610100900460ff166110115760405162461bcd60e51b815260040161051d906125e7565b565b600054610100900460ff1661103a5760405162461bcd60e51b815260040161051d906125e7565b609880546001600160a01b0319166001600160a01b0392909216919091179055565b600054610100900460ff166110835760405162461bcd60e51b815260040161051d906125e7565b60cc55565b600054610100900460ff166110af5760405162461bcd60e51b815260040161051d906125e7565b61010080546001600160a01b0319166001600160a01b03841617905580516106a29060ff906020840190612134565b600054610100900460ff166111055760405162461bcd60e51b815260040161051d906125e7565b6101339190915561013455565b600054610100900460ff166111395760405162461bcd60e51b815260040161051d906125e7565b610167805460ff19169055565b600054610100900460ff1661116d5760405162461bcd60e51b815260040161051d906125e7565b6105c57f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a8261180b565b600054610100900460ff166111be5760405162461bcd60e51b815260040161051d906125e7565b6105c57f14cf45180c3fcf249a5a305e9657ea05c14fd4f4e1800ee0216a8213091711d28261180b565b600054610100900460ff1661120f5760405162461bcd60e51b815260040161051d906125e7565b8151602092830120815191909201206101fe919091556101ff55565b600054610100900460ff166112525760405162461bcd60e51b815260040161051d906125e7565b6105c57f14dd327f3834be9d0f7cf44f6cf11c96ded83bd68d1a1b3926d35739e7bb88d08261180b565b600054610100900460ff166112a35760405162461bcd60e51b815260040161051d906125e7565b61101160003361180b565b604080517fb64321feb52d8980c1d31ee858f482f2f5cab15642d381cafe591f755eb50a2960208201529081018390526001600160a01b03821660608201526000906080015b60405160208183030381529060405280519060200120905092915050565b60008061131e84611815565b905060008061132d8386611828565b9092509050600081600481111561135457634e487b7160e01b600052602160045260246000fd5b14801561139857506001600160a01b03821660009081527fa7be7fbe46bfa3352d5cc2e1b2f219afdd2a5488af76c0aca49ef1a2f42d03e0602052604090205460ff165b156113a9576001935050505061058c565b50600095945050505050565b60008160cc54600014806113d8575060cc54816099546113d5919061268d565b11155b6114245760405162461bcd60e51b815260206004820152601b60248201527f4d61784465706f73697461626c653a206d617820726561636865640000000000604482015260640161051d565b61142f858585611898565b95945050505050565b7f14cf45180c3fcf249a5a305e9657ea05c14fd4f4e1800ee0216a8213091711d26114638133610da7565b6098546040516370a0823160e01b815230600482015283916001600160a01b0316906370a082319060240160206040518083038186803b1580156114a657600080fd5b505afa1580156114ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114de9190612305565b101561152c5760405162461bcd60e51b815260206004820152601f60248201527f436f6c6c65637461626c653a206e6f7420656e6f7567682062616c616e636500604482015260640161051d565b609854611543906001600160a01b03168484611a84565b6101cb546115519083611afc565b6101cb556040518281526001600160a01b038416907f4256a058fa2b123d727576d3d31e3a272db98ee5fe264e229610ce43dc8499999060200160405180910390a2505050565b6101675460ff16156115df5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015260640161051d565b610167805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258610ea83390565b606060006116248360026126a5565b61162f90600261268d565b67ffffffffffffffff81111561165557634e487b7160e01b600052604160045260246000fd5b6040519080825280601f01601f19166020018201604052801561167f576020820181803683370190505b509050600360fc1b816000815181106116a857634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106116e557634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a90535060006117098460026126a5565b61171490600161268d565b90505b60018111156117b5577f303132333435363738396162636465660000000000000000000000000000000085600f166010811061176357634e487b7160e01b600052603260045260246000fd5b1a60f81b82828151811061178757634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a90535060049490941c936117ae81612707565b9050611717565b5083156118045760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161051d565b9392505050565b61072f8282610ec5565b600061058c611822611b08565b83611b85565b60008082516041141561185f5760208301516040840151606085015160001a61185387828585611bac565b94509450505050611891565b825160401415611889576020830151604084015161187e868383611c99565b935093505050611891565b506000905060025b9250929050565b6101005460405163191ee97760e31b81526001600160a01b038085166004830152600092859285928592169063c8f74bb890602401602060405180830381600087803b1580156118e757600080fd5b505af11580156118fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061191f9190612305565b905060001981136119875760405162461bcd60e51b815260206004820152602c60248201527f4d6178546965724465706f73697461626c653a206d696e696d756d207469657260448201526b081b9bdd081c995858da195960a21b606482015260840161051d565b60ff54819081106119a45760ff546119a1906001906126c4565b90505b60ff81815481106119c557634e487b7160e01b600052603260045260246000fd5b90600052602060002001546119f9846119f3876001600160a01b031660009081526097602052604090205490565b90611afc565b1115611a6d5760405162461bcd60e51b815260206004820152603160248201527f4d6178546965724465706f73697461626c653a20616d6f756e7420726561636860448201527f656420746965722773206d6178696d756d000000000000000000000000000000606482015260840161051d565b611a78888888611ce1565b98975050505050505050565b6040516001600160a01b0383166024820152604481018290526106a290849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b031990931692909217909152611ea0565b6000611804828461268d565b6000610b8c7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f611b386101fe5490565b6101ff546040805160208101859052908101839052606081018290524660808201523060a082015260009060c0016040516020818303038152906040528051906020012090509392505050565b60405161190160f01b602082015260228101839052604281018290526000906062016112f4565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115611be35750600090506003611c90565b8460ff16601b14158015611bfb57508460ff16601c14155b15611c0c5750600090506004611c90565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611c60573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116611c8957600060019250925050611c90565b9150600090505b94509492505050565b6000807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831660ff84901c601b01611cd387828885611bac565b935093505050935093915050565b6098546040516370a0823160e01b815230600482015260009182916001600160a01b03909116906370a082319060240160206040518083038186803b158015611d2957600080fd5b505afa158015611d3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d619190612305565b609854909150611d7c906001600160a01b0316863086611f72565b6098546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b158015611dc057600080fd5b505afa158015611dd4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611df89190612305565b9050611e048183611fb0565b6001600160a01b038616600090815260976020526040902054909450611e2a9085611afc565b6001600160a01b038616600090815260976020526040902055609954611e509085611afc565b6099556040518481526001600160a01b0380871691908816907f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f629060200160405180910390a35091949350505050565b6000611ef5826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611fbc9092919063ffffffff16565b8051909150156106a25780806020019051810190611f139190612276565b6106a25760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161051d565b6040516001600160a01b0380851660248301528316604482015260648101829052611faa9085906323b872dd60e01b90608401611ab0565b50505050565b600061180482846126c4565b6060611fcb8484600085611fd3565b949350505050565b6060824710156120345760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840161051d565b843b6120825760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161051d565b600080866001600160a01b0316858760405161209e9190612498565b60006040518083038185875af1925050503d80600081146120db576040519150601f19603f3d011682016040523d82523d6000602084013e6120e0565b606091505b50915091506120f08282866120fb565b979650505050505050565b6060831561210a575081611804565b82511561211a5782518084602001fd5b8160405162461bcd60e51b815260040161051d91906125b4565b82805482825590600052602060002090810192821561216f579160200282015b8281111561216f578251825591602001919060010190612154565b5061217b92915061217f565b5090565b5b8082111561217b5760008155600101612180565b803561219f8161274a565b919050565b600082601f8301126121b4578081fd5b8135602067ffffffffffffffff8211156121d0576121d0612734565b8160051b6121df82820161265c565b8381528281019086840183880185018910156121f9578687fd5b8693505b8584101561221b5780358352600193909301929184019184016121fd565b50979650505050505050565b600060208284031215612238578081fd5b81356118048161274a565b600060208284031215612254578081fd5b813567ffffffffffffffff81111561226a578182fd5b611fcb848285016121a4565b600060208284031215612287578081fd5b81518015158114611804578182fd5b6000602082840312156122a7578081fd5b5035919050565b600080604083850312156122c0578081fd5b8235915060208301356122d28161274a565b809150509250929050565b6000602082840312156122ee578081fd5b81356001600160e01b031981168114611804578182fd5b600060208284031215612316578081fd5b5051919050565b60006020828403121561232e578081fd5b813567ffffffffffffffff80821115612345578283fd5b908301906101208286031215612359578283fd5b612361612632565b61236a83612194565b81526020830135602082015261238260408401612194565b6040820152606083013582811115612398578485fd5b6123a4878286016121a4565b6060830152506080830135608082015260a083013560a08201526123ca60c08401612194565b60c08201526123db60e08401612194565b60e082015261010091506123f0828401612194565b91810191909152949350505050565b60008060408385031215612411578182fd5b8235915060208084013567ffffffffffffffff80821115612430578384fd5b818601915086601f830112612443578384fd5b81358181111561245557612455612734565b612467601f8201601f1916850161265c565b9150808252878482850101111561247c578485fd5b8084840185840137810190920192909252919491935090915050565b600082516124aa8184602087016126db565b9190910192915050565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516124ec8160178501602088016126db565b7f206973206d697373696e6720726f6c652000000000000000000000000000000060179184019182015283516125298160288401602088016126db565b01602801949350505050565b6020808252825182820181905260009190848201906040850190845b8181101561256d57835183529284019291840191600101612551565b50909695505050505050565b6020808252825482820181905260008481528281209092916040850190845b8181101561256d57835483526001938401939285019201612598565b60208152600082518060208401526125d38160408501602087016126db565b601f01601f19169190910160400192915050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b604051610120810167ffffffffffffffff8111828210171561265657612656612734565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561268557612685612734565b604052919050565b600082198211156126a0576126a061271e565b500190565b60008160001904831182151516156126bf576126bf61271e565b500290565b6000828210156126d6576126d661271e565b500390565b60005b838110156126f65781810151838201526020016126de565b83811115611faa5750506000910152565b6000816127165761271661271e565b506000190190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146105c557600080fdfea26469706673582212206210265436bae3986d7d31a1391d1aa8a5b1e55bdac2df77dc1ea866ba81c71164736f6c63430008040033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101ef5760003560e01c80636273ab931161010f578063c24a0f8b116100a2578063e522538111610071578063e522538114610483578063e63ab1e91461048b578063e6400bbe146104b2578063f6153ccd146104ba57600080fd5b8063c24a0f8b14610449578063c89039c514610453578063d547741f14610466578063e29eb8361461047957600080fd5b80639ca39ae9116100de5780639ca39ae9146103e0578063a217fddf14610407578063bc06946f1461040f578063c0acafa51461043657600080fd5b80636273ab9314610383578063702efdf31461039657806371a6cab71461039e57806391d14854146103a757600080fd5b8063248a9ca31161018757806348825e941161015657806348825e941461032a5780635988764d146103515780635c975abb146103645780635d3035191461037057600080fd5b8063248a9ca3146102ce5780632f2ff15d146102f157806336568abe1461030457806345737b1e1461031757600080fd5b80630b97bc86116101c35780630b97bc861461024c5780630f45cc811461026457806315e592ae1461029057806323e3fbd5146102a557600080fd5b8062739f2a146101f457806301ffc9a714610209578063046f7da21461023157806307ad088d14610239575b600080fd5b610207610202366004612296565b6104c3565b005b61021c6102173660046122dd565b61055b565b60405190151581526020015b60405180910390f35b610207610592565b610207610247366004612243565b6105c8565b6102566101335481565b604051908152602001610228565b61010054610278906001600160a01b031681565b6040516001600160a01b039091168152602001610228565b610298610624565b6040516102289190612535565b6102566102b3366004612227565b6001600160a01b031660009081526097602052604090205490565b6102566102dc366004612296565b60009081526065602052604090206001015490565b6102076102ff3660046122ae565b61067c565b6102076103123660046122ae565b6106a7565b610207610325366004612296565b610733565b6102567fb64321feb52d8980c1d31ee858f482f2f5cab15642d381cafe591f755eb50a2981565b61020761035f36600461231d565b6107c6565b6101675460ff1661021c565b61020761037e3660046123ff565b610962565b610207610391366004612227565b610ad6565b61021c610b7d565b61025660cc5481565b61021c6103b53660046122ae565b60009182526065602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6102567f14cf45180c3fcf249a5a305e9657ea05c14fd4f4e1800ee0216a8213091711d281565b610256600081565b6102567f14dd327f3834be9d0f7cf44f6cf11c96ded83bd68d1a1b3926d35739e7bb88d081565b610207610444366004612296565b610b91565b6102566101345481565b609854610278906001600160a01b031681565b6102076104743660046122ae565b610c2d565b6102566101cb5481565b610207610c53565b6102567f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b610207610d74565b61025660995481565b60006104cf8133610da7565b6101345482106105265760405162461bcd60e51b815260206004820152601f60248201527f5363686564756c61626c653a20696e76616c696420737461727420646174650060448201526064015b60405180910390fd5b61013382905560405182907f340656eb96ec8f1ab4ba468e243fe2191f09ec5e2a832f96985750414221a7a590600090a25050565b60006001600160e01b03198216637965db0b60e01b148061058c57506301ffc9a760e01b6001600160e01b03198316145b92915050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a6105bd8133610da7565b6105c5610e27565b50565b60006105d48133610da7565b81516105e79060ff906020850190612134565b507ff96d80dccbfade17503389247513551c253bf8144572cbe56f4da1d6833d019f60ff6040516106189190612579565b60405180910390a15050565b606060ff80548060200260200160405190810160405280929190818152602001828054801561067257602002820191906000526020600020905b81548152602001906001019080831161065e575b5050505050905090565b6000828152606560205260409020600101546106988133610da7565b6106a28383610ec5565b505050565b6001600160a01b03811633146107255760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c660000000000000000000000000000000000606482015260840161051d565b61072f8282610f67565b5050565b600061073f8133610da7565b6101335482116107915760405162461bcd60e51b815260206004820152601d60248201527f5363686564756c61626c653a20696e76616c696420656e642064617465000000604482015260640161051d565b61013482905560405182907f78f20fcb2c7d71918cb4f6b5e6c6eb0bf4ed26c77d48c64a29c6f448c40d9ae890600090a25050565b600054610100900460ff166107e15760005460ff16156107e5565b303b155b6108575760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840161051d565b600054610100900460ff16158015610879576000805461ffff19166101011790555b610881610fea565b610889610fea565b610891610fea565b815161089c90611013565b6108a9826020015161105c565b6108bb82604001518360600151611088565b6108cd82608001518360a001516110de565b6108d5611112565b6108e3826101000151611146565b6108f08260e00151611197565b6109386040518060400160405280600b81526020016a4c61756e6368626c6f636b60a81b815250604051806040016040528060038152602001620312e360ec1b8152506111e8565b6109458260c0015161122b565b61094d61127c565b801561072f576000805461ff00191690555050565b6101335442101580156109785750610134544211155b6109c45760405162461bcd60e51b815260206004820152601760248201527f5363686564756c61626c653a204e6f74206f70656e6564000000000000000000604482015260640161051d565b6101675460ff1615610a0b5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015260640161051d565b8133826000610a1a84846112ae565b9050610a268183611312565b610a725760405162461bcd60e51b815260206004820152601c60248201527f417574686f72697a61626c653a206e6f7420617574686f72697a656400000000604482015260640161051d565b60008611610ac25760405162461bcd60e51b815260206004820152601860248201527f53616c653a20616d6f756e74206d757374206265203e20300000000000000000604482015260640161051d565b610acd3333886113b5565b50505050505050565b6000610ae28133610da7565b60995415610b325760405162461bcd60e51b815260206004820152601f60248201527f4465706f73697461626c653a20746f74616c206465706f73697420213d203000604482015260640161051d565b609880546001600160a01b0319166001600160a01b0384169081179091556040517fa59a70f0dc526699eeba221f83942d7eb396e238fb52deb2e0fdf9e8044ec50390600090a25050565b6000610b8c6101675460ff1690565b905090565b6000610b9d8133610da7565b811580610bac57506099548210155b610bf85760405162461bcd60e51b815260206004820152601860248201527f4465706f73697461626c653a206d6178203c20746f74616c0000000000000000604482015260640161051d565b60cc8290556040518281527fdca7855fb2f3586876bce97c855aa88e8e3f83edd1a6c9c09444bd884284aabe90602001610618565b600082815260656020526040902060010154610c498133610da7565b6106a28383610f67565b610134544211610ca55760405162461bcd60e51b815260206004820152601760248201527f5363686564756c61626c653a204e6f7420636c6f736564000000000000000000604482015260640161051d565b6101675460ff1615610cec5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015260640161051d565b6098546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b158015610d3057600080fd5b505afa158015610d44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d689190612305565b90506105c53382611438565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610d9f8133610da7565b6105c5611598565b60008281526065602090815260408083206001600160a01b038516845290915290205460ff1661072f57610de5816001600160a01b03166014611615565b610df0836020611615565b604051602001610e019291906124b4565b60408051601f198184030181529082905262461bcd60e51b825261051d916004016125b4565b6101675460ff16610e7a5760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f7420706175736564000000000000000000000000604482015260640161051d565b610167805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b60008281526065602090815260408083206001600160a01b038516845290915290205460ff1661072f5760008281526065602090815260408083206001600160a01b03851684529091529020805460ff19166001179055610f233390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60008281526065602090815260408083206001600160a01b038516845290915290205460ff161561072f5760008281526065602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b600054610100900460ff166110115760405162461bcd60e51b815260040161051d906125e7565b565b600054610100900460ff1661103a5760405162461bcd60e51b815260040161051d906125e7565b609880546001600160a01b0319166001600160a01b0392909216919091179055565b600054610100900460ff166110835760405162461bcd60e51b815260040161051d906125e7565b60cc55565b600054610100900460ff166110af5760405162461bcd60e51b815260040161051d906125e7565b61010080546001600160a01b0319166001600160a01b03841617905580516106a29060ff906020840190612134565b600054610100900460ff166111055760405162461bcd60e51b815260040161051d906125e7565b6101339190915561013455565b600054610100900460ff166111395760405162461bcd60e51b815260040161051d906125e7565b610167805460ff19169055565b600054610100900460ff1661116d5760405162461bcd60e51b815260040161051d906125e7565b6105c57f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a8261180b565b600054610100900460ff166111be5760405162461bcd60e51b815260040161051d906125e7565b6105c57f14cf45180c3fcf249a5a305e9657ea05c14fd4f4e1800ee0216a8213091711d28261180b565b600054610100900460ff1661120f5760405162461bcd60e51b815260040161051d906125e7565b8151602092830120815191909201206101fe919091556101ff55565b600054610100900460ff166112525760405162461bcd60e51b815260040161051d906125e7565b6105c57f14dd327f3834be9d0f7cf44f6cf11c96ded83bd68d1a1b3926d35739e7bb88d08261180b565b600054610100900460ff166112a35760405162461bcd60e51b815260040161051d906125e7565b61101160003361180b565b604080517fb64321feb52d8980c1d31ee858f482f2f5cab15642d381cafe591f755eb50a2960208201529081018390526001600160a01b03821660608201526000906080015b60405160208183030381529060405280519060200120905092915050565b60008061131e84611815565b905060008061132d8386611828565b9092509050600081600481111561135457634e487b7160e01b600052602160045260246000fd5b14801561139857506001600160a01b03821660009081527fa7be7fbe46bfa3352d5cc2e1b2f219afdd2a5488af76c0aca49ef1a2f42d03e0602052604090205460ff165b156113a9576001935050505061058c565b50600095945050505050565b60008160cc54600014806113d8575060cc54816099546113d5919061268d565b11155b6114245760405162461bcd60e51b815260206004820152601b60248201527f4d61784465706f73697461626c653a206d617820726561636865640000000000604482015260640161051d565b61142f858585611898565b95945050505050565b7f14cf45180c3fcf249a5a305e9657ea05c14fd4f4e1800ee0216a8213091711d26114638133610da7565b6098546040516370a0823160e01b815230600482015283916001600160a01b0316906370a082319060240160206040518083038186803b1580156114a657600080fd5b505afa1580156114ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114de9190612305565b101561152c5760405162461bcd60e51b815260206004820152601f60248201527f436f6c6c65637461626c653a206e6f7420656e6f7567682062616c616e636500604482015260640161051d565b609854611543906001600160a01b03168484611a84565b6101cb546115519083611afc565b6101cb556040518281526001600160a01b038416907f4256a058fa2b123d727576d3d31e3a272db98ee5fe264e229610ce43dc8499999060200160405180910390a2505050565b6101675460ff16156115df5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015260640161051d565b610167805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258610ea83390565b606060006116248360026126a5565b61162f90600261268d565b67ffffffffffffffff81111561165557634e487b7160e01b600052604160045260246000fd5b6040519080825280601f01601f19166020018201604052801561167f576020820181803683370190505b509050600360fc1b816000815181106116a857634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106116e557634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a90535060006117098460026126a5565b61171490600161268d565b90505b60018111156117b5577f303132333435363738396162636465660000000000000000000000000000000085600f166010811061176357634e487b7160e01b600052603260045260246000fd5b1a60f81b82828151811061178757634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a90535060049490941c936117ae81612707565b9050611717565b5083156118045760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161051d565b9392505050565b61072f8282610ec5565b600061058c611822611b08565b83611b85565b60008082516041141561185f5760208301516040840151606085015160001a61185387828585611bac565b94509450505050611891565b825160401415611889576020830151604084015161187e868383611c99565b935093505050611891565b506000905060025b9250929050565b6101005460405163191ee97760e31b81526001600160a01b038085166004830152600092859285928592169063c8f74bb890602401602060405180830381600087803b1580156118e757600080fd5b505af11580156118fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061191f9190612305565b905060001981136119875760405162461bcd60e51b815260206004820152602c60248201527f4d6178546965724465706f73697461626c653a206d696e696d756d207469657260448201526b081b9bdd081c995858da195960a21b606482015260840161051d565b60ff54819081106119a45760ff546119a1906001906126c4565b90505b60ff81815481106119c557634e487b7160e01b600052603260045260246000fd5b90600052602060002001546119f9846119f3876001600160a01b031660009081526097602052604090205490565b90611afc565b1115611a6d5760405162461bcd60e51b815260206004820152603160248201527f4d6178546965724465706f73697461626c653a20616d6f756e7420726561636860448201527f656420746965722773206d6178696d756d000000000000000000000000000000606482015260840161051d565b611a78888888611ce1565b98975050505050505050565b6040516001600160a01b0383166024820152604481018290526106a290849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b031990931692909217909152611ea0565b6000611804828461268d565b6000610b8c7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f611b386101fe5490565b6101ff546040805160208101859052908101839052606081018290524660808201523060a082015260009060c0016040516020818303038152906040528051906020012090509392505050565b60405161190160f01b602082015260228101839052604281018290526000906062016112f4565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115611be35750600090506003611c90565b8460ff16601b14158015611bfb57508460ff16601c14155b15611c0c5750600090506004611c90565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611c60573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116611c8957600060019250925050611c90565b9150600090505b94509492505050565b6000807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831660ff84901c601b01611cd387828885611bac565b935093505050935093915050565b6098546040516370a0823160e01b815230600482015260009182916001600160a01b03909116906370a082319060240160206040518083038186803b158015611d2957600080fd5b505afa158015611d3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d619190612305565b609854909150611d7c906001600160a01b0316863086611f72565b6098546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b158015611dc057600080fd5b505afa158015611dd4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611df89190612305565b9050611e048183611fb0565b6001600160a01b038616600090815260976020526040902054909450611e2a9085611afc565b6001600160a01b038616600090815260976020526040902055609954611e509085611afc565b6099556040518481526001600160a01b0380871691908816907f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f629060200160405180910390a35091949350505050565b6000611ef5826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611fbc9092919063ffffffff16565b8051909150156106a25780806020019051810190611f139190612276565b6106a25760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161051d565b6040516001600160a01b0380851660248301528316604482015260648101829052611faa9085906323b872dd60e01b90608401611ab0565b50505050565b600061180482846126c4565b6060611fcb8484600085611fd3565b949350505050565b6060824710156120345760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840161051d565b843b6120825760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161051d565b600080866001600160a01b0316858760405161209e9190612498565b60006040518083038185875af1925050503d80600081146120db576040519150601f19603f3d011682016040523d82523d6000602084013e6120e0565b606091505b50915091506120f08282866120fb565b979650505050505050565b6060831561210a575081611804565b82511561211a5782518084602001fd5b8160405162461bcd60e51b815260040161051d91906125b4565b82805482825590600052602060002090810192821561216f579160200282015b8281111561216f578251825591602001919060010190612154565b5061217b92915061217f565b5090565b5b8082111561217b5760008155600101612180565b803561219f8161274a565b919050565b600082601f8301126121b4578081fd5b8135602067ffffffffffffffff8211156121d0576121d0612734565b8160051b6121df82820161265c565b8381528281019086840183880185018910156121f9578687fd5b8693505b8584101561221b5780358352600193909301929184019184016121fd565b50979650505050505050565b600060208284031215612238578081fd5b81356118048161274a565b600060208284031215612254578081fd5b813567ffffffffffffffff81111561226a578182fd5b611fcb848285016121a4565b600060208284031215612287578081fd5b81518015158114611804578182fd5b6000602082840312156122a7578081fd5b5035919050565b600080604083850312156122c0578081fd5b8235915060208301356122d28161274a565b809150509250929050565b6000602082840312156122ee578081fd5b81356001600160e01b031981168114611804578182fd5b600060208284031215612316578081fd5b5051919050565b60006020828403121561232e578081fd5b813567ffffffffffffffff80821115612345578283fd5b908301906101208286031215612359578283fd5b612361612632565b61236a83612194565b81526020830135602082015261238260408401612194565b6040820152606083013582811115612398578485fd5b6123a4878286016121a4565b6060830152506080830135608082015260a083013560a08201526123ca60c08401612194565b60c08201526123db60e08401612194565b60e082015261010091506123f0828401612194565b91810191909152949350505050565b60008060408385031215612411578182fd5b8235915060208084013567ffffffffffffffff80821115612430578384fd5b818601915086601f830112612443578384fd5b81358181111561245557612455612734565b612467601f8201601f1916850161265c565b9150808252878482850101111561247c578485fd5b8084840185840137810190920192909252919491935090915050565b600082516124aa8184602087016126db565b9190910192915050565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516124ec8160178501602088016126db565b7f206973206d697373696e6720726f6c652000000000000000000000000000000060179184019182015283516125298160288401602088016126db565b01602801949350505050565b6020808252825182820181905260009190848201906040850190845b8181101561256d57835183529284019291840191600101612551565b50909695505050505050565b6020808252825482820181905260008481528281209092916040850190845b8181101561256d57835483526001938401939285019201612598565b60208152600082518060208401526125d38160408501602087016126db565b601f01601f19169190910160400192915050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b604051610120810167ffffffffffffffff8111828210171561265657612656612734565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561268557612685612734565b604052919050565b600082198211156126a0576126a061271e565b500190565b60008160001904831182151516156126bf576126bf61271e565b500290565b6000828210156126d6576126d661271e565b500390565b60005b838110156126f65781810151838201526020016126de565b83811115611faa5750506000910152565b6000816127165761271661271e565b506000190190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146105c557600080fdfea26469706673582212206210265436bae3986d7d31a1391d1aa8a5b1e55bdac2df77dc1ea866ba81c71164736f6c63430008040033

Deployed Bytecode Sourcemap

638:3135:29:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1924:258:42;;;;;;:::i;:::-;;:::i;:::-;;3015:213:0;;;;;;:::i;:::-;;:::i;:::-;;;9512:14:63;;9505:22;9487:41;;9475:2;9460:18;3015:213:0;;;;;;;;1698:76:43;;;:::i;3376:238:35:-;;;;;;:::i;:::-;;:::i;450:24:42:-;;;;;;;;;9685:25:63;;;9673:2;9658:18;450:24:42;9640:76:63;935:28:35;;;;;-1:-1:-1;;;;;935:28:35;;;;;;-1:-1:-1;;;;;7266:55:63;;;7248:74;;7236:2;7221:18;935:28:35;7203:125:63;3699:151:35;;;:::i;:::-;;;;;;;:::i;3846:118:33:-;;;;;;:::i;:::-;-1:-1:-1;;;;;3938:19:33;3912:7;3938:19;;;:9;:19;;;;;;;3846:118;4420:121:0;;;;;;:::i;:::-;4486:7;4512:12;;;:6;:12;;;;;:22;;;;4420:121;4791:145;;;;;;:::i;:::-;;:::i;5808:214::-;;;;;;:::i;:::-;;:::i;2297:244:42:-;;;;;;:::i;:::-;;:::i;3016:103:31:-;;3067:52;3016:103;;1536:929:29;;;;;;:::i;:::-;;:::i;1377:84:4:-;1447:7;;;;1377:84;;3124:293:29;;;;;;:::i;:::-;;:::i;4028::33:-;;;;;;:::i;:::-;;:::i;1310:88:43:-;;;:::i;590:30:34:-;;;;;;3315:137:0;;;;;;:::i;:::-;3393:4;3416:12;;;:6;:12;;;;;;;;-1:-1:-1;;;;;3416:29:0;;;;;;;;;;;;;;;3315:137;836:68:32;;877:27;836:68;;2422:49:0;;2467:4;2422:49;;922:70:31;;964:28;922:70;;2370:311:34;;;;;;:::i;:::-;;:::i;501:22:42:-;;;;;;851:37:33;;;;;-1:-1:-1;;;;;851:37:33;;;5170:147:0;;;;;;:::i;:::-;;:::i;945:29:32:-;;;;;;3575:164:29;;;:::i;587:62:43:-;;625:24;587:62;;1510:75;;;:::i;929:27:33:-;;;;;;1924:258:42;2467:4:0;2900:30;2467:4;965:10:8;2900::0;:30::i;:::-;2058:7:42::1;;2045:10;:20;2037:64;;;::::0;-1:-1:-1;;;2037:64:42;;14357:2:63;2037:64:42::1;::::0;::::1;14339:21:63::0;14396:2;14376:18;;;14369:30;14435:33;14415:18;;;14408:61;14486:18;;2037:64:42::1;;;;;;;;;2111:9;:22:::0;;;2148:27:::1;::::0;2123:10;;2148:27:::1;::::0;;;::::1;1924:258:::0;;:::o;3015:213:0:-;3100:4;-1:-1:-1;;;;;;3123:58:0;;-1:-1:-1;;;3123:58:0;;:98;;-1:-1:-1;;;;;;;;;;1203:51:13;;;3185:36:0;3116:105;3015:213;-1:-1:-1;;3015:213:0:o;1698:76:43:-;625:24;2900:30:0;625:24:43;965:10:8;2900::0;:30::i;:::-;1757:10:43::1;:8;:10::i;:::-;1698:76:::0;:::o;3376:238:35:-;2467:4:0;2900:30;2467:4;965:10:8;2900::0;:30::i;:::-;3508:33:35;;::::1;::::0;:22:::1;::::0;:33:::1;::::0;::::1;::::0;::::1;:::i;:::-;;3556:51;3584:22;3556:51;;;;;;:::i;:::-;;;;;;;;3376:238:::0;;:::o;3699:151::-;3782:16;3821:22;3814:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3699:151;:::o;4791:145:0:-;4486:7;4512:12;;;:6;:12;;;;;:22;;;2900:30;2911:4;965:10:8;2900::0;:30::i;:::-;4904:25:::1;4915:4;4921:7;4904:10;:25::i;:::-;4791:145:::0;;;:::o;5808:214::-;-1:-1:-1;;;;;5903:23:0;;965:10:8;5903:23:0;5895:83;;;;-1:-1:-1;;;5895:83:0;;19207:2:63;5895:83:0;;;19189:21:63;19246:2;19226:18;;;19219:30;19285:34;19265:18;;;19258:62;19356:17;19336:18;;;19329:45;19391:19;;5895:83:0;19179:237:63;5895:83:0;5989:26;6001:4;6007:7;5989:11;:26::i;:::-;5808:214;;:::o;2297:244:42:-;2467:4:0;2900:30;2467:4;965:10:8;2900::0;:30::i;:::-;2425:9:42::1;;2414:8;:20;2406:62;;;::::0;-1:-1:-1;;;2406:62:42;;13592:2:63;2406:62:42::1;::::0;::::1;13574:21:63::0;13631:2;13611:18;;;13604:30;13670:31;13650:18;;;13643:59;13719:18;;2406:62:42::1;13564:179:63::0;2406:62:42::1;2478:7;:18:::0;;;2511:23:::1;::::0;2488:8;;2511:23:::1;::::0;;;::::1;2297:244:::0;;:::o;1536:929:29:-;2347:13:3;;;;;;;:48;;2383:12;;;;2382:13;2347:48;;;3125:4;1098:20:7;1144:8;2363:16:3;2339:107;;;;-1:-1:-1;;;2339:107:3;;15062:2:63;2339:107:3;;;15044:21:63;15101:2;15081:18;;;15074:30;15140:34;15120:18;;;15113:62;15211:16;15191:18;;;15184:44;15245:19;;2339:107:3;15034:236:63;2339:107:3;2457:19;2480:13;;;;;;2479:14;2503:98;;;;2537:13;:20;;-1:-1:-1;;2571:19:3;;;;;2503:98;1647:26:29::1;:24;:26::i;:::-;1683:25;:23;:25::i;:::-;1718:32;:30;:32::i;:::-;1789:26:::0;;1760:56:::1;::::0;:28:::1;:56::i;:::-;1826:62;1858:13;:29;;;1826:31;:62::i;:::-;1898:127;1947:13;:25;;;1986:13;:29;;;1898:35;:127::i;:::-;2035:110;2077:13;:23;;;2114:13;:21;;;2035:28;:110::i;:::-;2155:27;:25;:27::i;:::-;2192:50;2221:13;:20;;;2192:28;:50::i;:::-;2252:53;2281:13;:23;;;2252:28;:53::i;:::-;2315:45;;;;;;;;;;;;;;-1:-1:-1::0;;;2315:45:29::1;;::::0;::::1;;;;;;;;;;;;;-1:-1:-1::0;;;2315:45:29::1;;::::0;:23:::1;:45::i;:::-;2370:55;2400:13;:24;;;2370:29;:55::i;:::-;2435:23;:21;:23::i;:::-;2627:14:3::0;2623:66;;;2673:5;2657:21;;-1:-1:-1;;2657:21:3;;;1536:929:29;;:::o;3124:293::-;864:9:42;;845:15;:28;;:58;;;;;896:7;;877:15;:26;;845:58;824:128;;;;-1:-1:-1;;;824:128:42;;19623:2:63;824:128:42;;;19605:21:63;19662:2;19642:18;;;19635:30;19701:25;19681:18;;;19674:53;19744:18;;824:128:42;19595:173:63;824:128:42;1447:7:4;;;;1690:9:::1;1682:38;;;::::0;-1:-1:-1;;;1682:38:4;;14717:2:63;1682:38:4::1;::::0;::::1;14699:21:63::0;14756:2;14736:18;;;14729:30;-1:-1:-1;;;14775:18:63;;;14768:46;14831:18;;1682:38:4::1;14689:166:63::0;1682:38:4::1;3262:6:29::0;965:10:8;3284:9:29::2;1209:18:31;1230:32;1249:6;1257:4;1230:18;:32::i;:::-;1209:53;;1293:35;1306:10;1318:9;1293:12;:35::i;:::-;1272:110;;;::::0;-1:-1:-1;;;1272:110:31;;16891:2:63;1272:110:31::2;::::0;::::2;16873:21:63::0;16930:2;16910:18;;;16903:30;16969;16949:18;;;16942:58;17017:18;;1272:110:31::2;16863:178:63::0;1272:110:31::2;3326:1:29::3;3317:6;:10;3309:47;;;::::0;-1:-1:-1;;;3309:47:29;;16538:2:63;3309:47:29::3;::::0;::::3;16520:21:63::0;16577:2;16557:18;;;16550:30;16616:26;16596:18;;;16589:54;16660:18;;3309:47:29::3;16510:174:63::0;3309:47:29::3;3366:44;965:10:8::0;;3403:6:29::3;3366:8;:44::i;:::-;;1730:1:4::2;;;;3124:293:29::0;;:::o;4028::33:-;2467:4:0;2900:30;2467:4;965:10:8;2900::0;:30::i;:::-;4165:12:33::1;::::0;:17;4157:61:::1;;;::::0;-1:-1:-1;;;4157:61:33;;17248:2:63;4157:61:33::1;::::0;::::1;17230:21:63::0;17287:2;17267:18;;;17260:30;17326:33;17306:18;;;17299:61;17377:18;;4157:61:33::1;17220:181:63::0;4157:61:33::1;4228:12;:28:::0;;-1:-1:-1;;;;;;4228:28:33::1;-1:-1:-1::0;;;;;4228:28:33;::::1;::::0;;::::1;::::0;;;4272:42:::1;::::0;::::1;::::0;-1:-1:-1;;4272:42:33::1;4028:293:::0;;:::o;1310:88:43:-;1360:4;1383:8;1447:7:4;;;;;1377:84;1383:8:43;1376:15;;1310:88;:::o;2370:311:34:-;2467:4:0;2900:30;2467:4;965:10:8;2900::0;:30::i;:::-;2508:11:34;;;:37:::1;;;2533:12;;2523:6;:22;;2508:37;2487:108;;;::::0;-1:-1:-1;;;2487:108:34;;15477:2:63;2487:108:34::1;::::0;::::1;15459:21:63::0;15516:2;15496:18;;;15489:30;15555:26;15535:18;;;15528:54;15599:18;;2487:108:34::1;15449:174:63::0;2487:108:34::1;2605:15;:24:::0;;;2645:29:::1;::::0;9685:25:63;;;2645:29:34::1;::::0;9673:2:63;9658:18;2645:29:34::1;9640:76:63::0;5170:147:0;4486:7;4512:12;;;:6;:12;;;;;:22;;;2900:30;2911:4;965:10:8;2900::0;:30::i;:::-;5284:26:::1;5296:4;5302:7;5284:11;:26::i;3575:164:29:-:0;1114:7:42;;1096:15;:25;1088:61;;;;-1:-1:-1;;;1088:61:42;;15830:2:63;1088:61:42;;;15812:21:63;15869:2;15849:18;;;15842:30;15908:25;15888:18;;;15881:53;15951:18;;1088:61:42;15802:173:63;1088:61:42;1447:7:4;;;;1690:9:::1;1682:38;;;::::0;-1:-1:-1;;;1682:38:4;;14717:2:63;1682:38:4::1;::::0;::::1;14699:21:63::0;14756:2;14736:18;;;14729:30;-1:-1:-1;;;14775:18:63;;;14768:46;14831:18;;1682:38:4::1;14689:166:63::0;1682:38:4::1;3655:12:29::2;::::0;:37:::2;::::0;-1:-1:-1;;;3655:37:29;;3686:4:::2;3655:37;::::0;::::2;7248:74:63::0;3638:14:29::2;::::0;-1:-1:-1;;;;;3655:12:29::2;::::0;:22:::2;::::0;7221:18:63;;3655:37:29::2;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3638:54:::0;-1:-1:-1;3702:30:29::2;965:10:8::0;3725:6:29::2;3702:8;:30::i;1510:75:43:-:0;625:24;2900:30:0;625:24:43;965:10:8;2900::0;:30::i;:::-;1570:8:43::1;:6;:8::i;3733:506:0:-:0;3393:4;3416:12;;;:6;:12;;;;;;;;-1:-1:-1;;;;;3416:29:0;;;;;;;;;;;;3808:425;;3996:52;4035:7;-1:-1:-1;;;;;3996:52:0;4045:2;3996:30;:52::i;:::-;4119:49;4158:4;4165:2;4119:30;:49::i;:::-;3903:287;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;3903:287:0;;;;;;;;;;-1:-1:-1;;;3851:371:0;;;;;;;:::i;2389:117:4:-;1447:7;;;;1948:41;;;;-1:-1:-1;;;1948:41:4;;13243:2:63;1948:41:4;;;13225:21:63;13282:2;13262:18;;;13255:30;13321:22;13301:18;;;13294:50;13361:18;;1948:41:4;13215:170:63;1948:41:4;2447:7:::1;:15:::0;;-1:-1:-1;;2447:15:4::1;::::0;;2477:22:::1;965:10:8::0;2486:12:4::1;2477:22;::::0;-1:-1:-1;;;;;7266:55:63;;;7248:74;;7236:2;7221:18;2477:22:4::1;;;;;;;2389:117::o:0;7265:233:0:-;3393:4;3416:12;;;:6;:12;;;;;;;;-1:-1:-1;;;;;3416:29:0;;;;;;;;;;;;7343:149;;7386:12;;;;:6;:12;;;;;;;;-1:-1:-1;;;;;7386:29:0;;;;;;;;;:36;;-1:-1:-1;;7386:36:0;7418:4;7386:36;;;7468:12;965:10:8;;886:96;7468:12:0;-1:-1:-1;;;;;7441:40:0;7459:7;-1:-1:-1;;;;;7441:40:0;7453:4;7441:40;;;;;;;;;;7265:233;;:::o;7623:234::-;3393:4;3416:12;;;:6;:12;;;;;;;;-1:-1:-1;;;;;3416:29:0;;;;;;;;;;;;7702:149;;;7776:5;7744:12;;;:6;:12;;;;;;;;-1:-1:-1;;;;;7744:29:0;;;;;;;;;;:37;;-1:-1:-1;;7744:37:0;;;7800:40;965:10:8;;7744:12:0;;7800:40;;7776:5;7800:40;7623:234;;:::o;812:69:8:-;2930:13:3;;;;;;;2922:69;;;;-1:-1:-1;;;2922:69:3;;;;;;;:::i;:::-;812::8:o;1870:162:33:-;2930:13:3;;;;;;;2922:69;;;;-1:-1:-1;;;2922:69:3;;;;;;;:::i;:::-;1997:12:33::1;:28:::0;;-1:-1:-1;;;;;;1997:28:33::1;-1:-1:-1::0;;;;;1997:28:33;;;::::1;::::0;;;::::1;::::0;;1870:162::o;1623:164:34:-;2930:13:3;;;;;;;2922:69;;;;-1:-1:-1;;;2922:69:3;;;;;;;:::i;:::-;1746:15:34::1;:34:::0;1623:164::o;2543:246:35:-;2930:13:3;;;;;;;2922:69;;;;-1:-1:-1;;;2922:69:3;;;;;;;:::i;:::-;2705:11:35::1;:26:::0;;-1:-1:-1;;;;;;2705:26:35::1;-1:-1:-1::0;;;;;2705:26:35;::::1;;::::0;;2741:41;;::::1;::::0;:22:::1;::::0;:41:::1;::::0;::::1;::::0;::::1;:::i;1618:189:42:-:0;2930:13:3;;;;;;;2922:69;;;;-1:-1:-1;;;2922:69:3;;;;;;;:::i;:::-;1750:9:42::1;:22:::0;;;;1782:7:::1;:18:::0;1618:189::o;1187:95:4:-;2930:13:3;;;;;;;2922:69;;;;-1:-1:-1;;;2922:69:3;;;;;;;:::i;:::-;1260:7:4::1;:15:::0;;-1:-1:-1;;1260:15:4::1;::::0;;1187:95::o;1055:150:43:-;2930:13:3;;;;;;;2922:69;;;;-1:-1:-1;;;2922:69:3;;;;;;;:::i;:::-;1166:32:43::1;625:24;1190:7;1166:10;:32::i;1639:159:32:-:0;2930:13:3;;;;;;;2922:69;;;;-1:-1:-1;;;2922:69:3;;;;;;;:::i;:::-;1753:38:32::1;877:27;1780:10;1753;:38::i;2429:297:12:-:0;2930:13:3;;;;;;;2922:69;;;;-1:-1:-1;;;2922:69:3;;;;;;;:::i;:::-;2562:22:12;;::::1;::::0;;::::1;::::0;2618:25;;;;;::::1;::::0;2653:12:::1;:25:::0;;;;2688:15:::1;:31:::0;2429:297::o;2112:163:31:-;2930:13:3;;;;;;;2922:69;;;;-1:-1:-1;;;2922:69:3;;;;;;;:::i;:::-;2228:40:31::1;964:28;2256:11;2228:10;:40::i;2471:120:29:-:0;2930:13:3;;;;;;;2922:69;;;;-1:-1:-1;;;2922:69:3;;;;;;;:::i;:::-;2540:44:29::1;2467:4:0;965:10:8::0;2540::29::1;:44::i;3211:199:31:-:0;3358:44;;;3067:52;3358:44;;;10440:25:63;10481:18;;;10474:34;;;-1:-1:-1;;;;;10544:55:63;;10524:18;;;10517:83;3318:7:31;;10413:18:63;;3358:44:31;;;;;;;;;;;;;3348:55;;;;;;3341:62;;3211:199;;;;:::o;2487:523::-;2600:4;2620:12;2635:28;2652:10;2635:16;:28::i;:::-;2620:43;;2687:17;2718:35;2766:44;2794:4;2800:9;2766:27;:44::i;:::-;2673:137;;-1:-1:-1;2673:137:31;-1:-1:-1;2846:37:31;2837:5;:46;;;;;;-1:-1:-1;;;2837:46:31;;;;;;;;;;:97;;;;-1:-1:-1;;;;;;3416:29:0;;3393:4;3416:29;;;:12;;:29;:12;:29;;;;;2899:35:31;2820:161;;;2966:4;2959:11;;;;;;;2820:161;-1:-1:-1;2998:5:31;;2487:523;-1:-1:-1;;;;;2487:523:31:o;2652:329:29:-;2899:7;2874:6;960:15:34;;979:1;960:20;:64;;;;1009:15;;999:6;984:12;;:21;;;;:::i;:::-;:40;;960:64;939:138;;;;-1:-1:-1;;;939:138:34;;16182:2:63;939:138:34;;;16164:21:63;16221:2;16201:18;;;16194:30;16260:29;16240:18;;;16233:57;16307:18;;939:138:34;16154:177:63;939:138:34;2929:45:29::1;2957:4;2963:2;2967:6;2929:27;:45::i;:::-;2922:52:::0;2652:329;-1:-1:-1;;;;;2652:329:29:o;1997:421:32:-;877:27;2900:30:0;877:27:32;965:10:8;2900::0;:30::i;:::-;2153:12:32::1;::::0;:37:::1;::::0;-1:-1:-1;;;2153:37:32;;2184:4:::1;2153:37;::::0;::::1;7248:74:63::0;2194:6:32;;-1:-1:-1;;;;;2153:12:32::1;::::0;:22:::1;::::0;7221:18:63;;2153:37:32::1;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:47;;2132:125;;;::::0;-1:-1:-1;;;2132:125:32;;12883:2:63;2132:125:32::1;::::0;::::1;12865:21:63::0;12922:2;12902:18;;;12895:30;12961:33;12941:18;;;12934:61;13012:18;;2132:125:32::1;12855:181:63::0;2132:125:32::1;2268:12;::::0;:46:::1;::::0;-1:-1:-1;;;;;2268:12:32::1;2294:11:::0;2307:6;2268:25:::1;:46::i;:::-;2341:14;::::0;:26:::1;::::0;2360:6;2341:18:::1;:26::i;:::-;2324:14;:43:::0;2383:28:::1;::::0;9685:25:63;;;-1:-1:-1;;;;;2383:28:32;::::1;::::0;::::1;::::0;9673:2:63;9658:18;2383:28:32::1;;;;;;;1997:421:::0;;;:::o;2142:115:4:-;1447:7;;;;1690:9;1682:38;;;;-1:-1:-1;;;1682:38:4;;14717:2:63;1682:38:4;;;14699:21:63;14756:2;14736:18;;;14729:30;-1:-1:-1;;;14775:18:63;;;14768:46;14831:18;;1682:38:4;14689:166:63;1682:38:4;2201:7:::1;:14:::0;;-1:-1:-1;;2201:14:4::1;2211:4;2201:14;::::0;;2230:20:::1;2237:12;965:10:8::0;;886:96;1599:441:9;1674:13;1699:19;1731:10;1735:6;1731:1;:10;:::i;:::-;:14;;1744:1;1731:14;:::i;:::-;1721:25;;;;;;-1:-1:-1;;;1721:25:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1721:25:9;;1699:47;;-1:-1:-1;;;1756:6:9;1763:1;1756:9;;;;;;-1:-1:-1;;;1756:9:9;;;;;;;;;;;;:15;-1:-1:-1;;;;;1756:15:9;;;;;;;;;-1:-1:-1;;;1781:6:9;1788:1;1781:9;;;;;;-1:-1:-1;;;1781:9:9;;;;;;;;;;;;:15;-1:-1:-1;;;;;1781:15:9;;;;;;;;-1:-1:-1;1811:9:9;1823:10;1827:6;1823:1;:10;:::i;:::-;:14;;1836:1;1823:14;:::i;:::-;1811:26;;1806:132;1843:1;1839;:5;1806:132;;;1877:12;1890:5;1898:3;1890:11;1877:25;;;;;-1:-1:-1;;;1877:25:9;;;;;;;;;;;;1865:6;1872:1;1865:9;;;;;;-1:-1:-1;;;1865:9:9;;;;;;;;;;;;:37;-1:-1:-1;;;;;1865:37:9;;;;;;;;-1:-1:-1;1926:1:9;1916:11;;;;;1846:3;;;:::i;:::-;;;1806:132;;;-1:-1:-1;1955:10:9;;1947:55;;;;-1:-1:-1;;;1947:55:9;;12522:2:63;1947:55:9;;;12504:21:63;;;12541:18;;;12534:30;12600:34;12580:18;;;12573:62;12652:18;;1947:55:9;12494:182:63;1947:55:9;2026:6;1599:441;-1:-1:-1;;;1599:441:9:o;6661:110:0:-;6739:25;6750:4;6756:7;6739:10;:25::i;3860:176:12:-;3937:7;3963:66;3996:20;:18;:20::i;:::-;4018:10;3963:32;:66::i;2250:1279:10:-;2331:7;2340:12;2561:9;:16;2581:2;2561:22;2557:966;;;2850:4;2835:20;;2829:27;2899:4;2884:20;;2878:27;2956:4;2941:20;;2935:27;2599:9;2927:36;2997:25;3008:4;2927:36;2829:27;2878;2997:10;:25::i;:::-;2990:32;;;;;;;;;2557:966;3043:9;:16;3063:2;3043:22;3039:484;;;3312:4;3297:20;;3291:27;3362:4;3347:20;;3341:27;3402:23;3413:4;3291:27;3341;3402:10;:23::i;:::-;3395:30;;;;;;;;3039:484;-1:-1:-1;3472:1:10;;-1:-1:-1;3476:35:10;3039:484;2250:1279;;;;;:::o;2991:279:35:-;1331:11;;:27;;-1:-1:-1;;;1331:27:35;;-1:-1:-1;;;;;7266:55:63;;;1331:27:35;;;7248:74:63;3195:7:35;;3166:2;;3170:6;;3195:7;;1331:11;;:18;;7221::63;;1331:27:35;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1317:41;;-1:-1:-1;;1376:4:35;:9;1368:66;;;;-1:-1:-1;;;1368:66:35;;12109:2:63;1368:66:35;;;12091:21:63;12148:2;12128:18;;;12121:30;12187:34;12167:18;;;12160:62;-1:-1:-1;;;12238:18:63;;;12231:42;12290:19;;1368:66:35;12081:234:63;1368:66:35;1612:22;:29;1469:4;;1603:38;;1599:110;;1665:22;:29;:33;;1697:1;;1665:33;:::i;:::-;1657:41;;1599:110;1773:22;1796:5;1773:29;;;;;;-1:-1:-1;;;1773:29:35;;;;;;;;;;;;;;;;;1739:30;1762:6;1739:18;1749:7;-1:-1:-1;;;;;3938:19:33;3912:7;3938:19;;;:9;:19;;;;;;;3846:118;1739:18:35;:22;;:30::i;:::-;:63;;1718:159;;;;-1:-1:-1;;;1718:159:35;;18378:2:63;1718:159:35;;;18360:21:63;18417:2;18397:18;;;18390:30;18456:34;18436:18;;;18429:62;18527:19;18507:18;;;18500:47;18564:19;;1718:159:35;18350:239:63;1718:159:35;3225:38:::1;3246:4;3252:2;3256:6;3225:20;:38::i;:::-;3218:45:::0;2991:279;-1:-1:-1;;;;;;;;2991:279:35:o;745:216:6:-;895:58;;-1:-1:-1;;;;;7928:55:63;;895:58:6;;;7910:74:63;8000:18;;;7993:34;;;868:86:6;;888:5;;-1:-1:-1;;;918:23:6;7883:18:63;;895:58:6;;;;-1:-1:-1;;895:58:6;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;895:58:6;;;;;;;;;;868:19;:86::i;2752:96:16:-;2810:7;2836:5;2840:1;2836;:5;:::i;2812:160:12:-;2865:7;2891:74;1565:95;2925:17;4356:12;;;4272:103;2925:17;4701:15;;3154:73;;;;;;9980:25:63;;;10021:18;;;10014:34;;;10064:18;;;10057:34;;;3198:13:12;10107:18:63;;;10100:34;3221:4:12;10150:19:63;;;10143:84;3118:7:12;;9952:19:63;;3154:73:12;;;;;;;;;;;;3144:84;;;;;;3137:91;;2978:257;;;;;;9158:194:10;9287:57;;-1:-1:-1;;;9287:57:10;;;6172:27:63;6215:11;;;6208:27;;;6251:12;;;6244:28;;;9251:7:10;;6288:12:63;;9287:57:10;6162:144:63;5766:1603:10;5892:7;;6816:66;6803:79;;6799:161;;;-1:-1:-1;6914:1:10;;-1:-1:-1;6918:30:10;6898:51;;6799:161;6973:1;:7;;6978:2;6973:7;;:18;;;;;6984:1;:7;;6989:2;6984:7;;6973:18;6969:100;;;-1:-1:-1;7023:1:10;;-1:-1:-1;7027:30:10;7007:51;;6969:100;7180:24;;;7163:14;7180:24;;;;;;;;;10838:25:63;;;10911:4;10899:17;;10879:18;;;10872:45;;;;10933:18;;;10926:34;;;10976:18;;;10969:34;;;7180:24:10;;10810:19:63;;7180:24:10;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;7180:24:10;;-1:-1:-1;;7180:24:10;;;-1:-1:-1;;;;;;;7218:20:10;;7214:101;;7270:1;7274:29;7254:50;;;;;;;7214:101;7333:6;-1:-1:-1;7341:20:10;;-1:-1:-1;5766:1603:10;;;;;;;;:::o;4796:379::-;4906:7;;5011:66;5003:75;;5104:3;5100:12;;;5114:2;5096:21;5143:25;5154:4;5096:21;5163:1;5003:75;5143:10;:25::i;:::-;5136:32;;;;;;4796:379;;;;;;:::o;2415:684:33:-;2630:12;;:37;;-1:-1:-1;;;2630:37:33;;2661:4;2630:37;;;7248:74:63;2531:7:33;;;;-1:-1:-1;;;;;2630:12:33;;;;:22;;7221:18:63;;2630:37:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2677:12;;2612:55;;-1:-1:-1;2677:58:33;;-1:-1:-1;;;;;2677:12:33;2707:4;2721;2728:6;2677:29;:58::i;:::-;2766:12;;:37;;-1:-1:-1;;;2766:37:33;;2797:4;2766:37;;;7248:74:63;2745:18:33;;-1:-1:-1;;;;;2766:12:33;;:22;;7221:18:63;;2766:37:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2745:58;-1:-1:-1;2880:23:33;2745:58;2895:7;2880:14;:23::i;:::-;-1:-1:-1;;;;;2954:13:33;;;;;;:9;:13;;;;;;2871:32;;-1:-1:-1;2954:25:33;;2871:32;2954:17;:25::i;:::-;-1:-1:-1;;;;;2938:13:33;;;;;;:9;:13;;;;;:41;3004:12;;:24;;3021:6;3004:16;:24::i;:::-;2989:12;:39;3043:25;;9685::63;;;-1:-1:-1;;;;;3043:25:33;;;;;;;;;;9673:2:63;9658:18;3043:25:33;;;;;;;-1:-1:-1;3086:6:33;;2415:684;-1:-1:-1;;;;2415:684:33:o;3306:717:6:-;3736:23;3762:69;3790:4;3762:69;;;;;;;;;;;;;;;;;3770:5;-1:-1:-1;;;;;3762:27:6;;;:69;;;;;:::i;:::-;3845:17;;3736:95;;-1:-1:-1;3845:21:6;3841:176;;3940:10;3929:30;;;;;;;;;;;;:::i;:::-;3921:85;;;;-1:-1:-1;;;3921:85:6;;18796:2:63;3921:85:6;;;18778:21:63;18835:2;18815:18;;;18808:30;18874:34;18854:18;;;18847:62;-1:-1:-1;;;18925:18:63;;;18918:40;18975:19;;3921:85:6;18768:232:63;967:252:6;1143:68;;-1:-1:-1;;;;;7614:15:63;;;1143:68:6;;;7596:34:63;7666:15;;7646:18;;;7639:43;7698:18;;;7691:34;;;1116:96:6;;1136:5;;-1:-1:-1;;;1166:27:6;7508:18:63;;1143:68:6;7490:241:63;1116:96:6;967:252;;;;:::o;3119:96:16:-;3177:7;3203:5;3207:1;3203;:5;:::i;3525:223:7:-;3658:12;3689:52;3711:6;3719:4;3725:1;3728:12;3689:21;:52::i;:::-;3682:59;3525:223;-1:-1:-1;;;;3525:223:7:o;4612:499::-;4777:12;4834:5;4809:21;:30;;4801:81;;;;-1:-1:-1;;;4801:81:7;;13950:2:63;4801:81:7;;;13932:21:63;13989:2;13969:18;;;13962:30;14028:34;14008:18;;;14001:62;-1:-1:-1;;;14079:18:63;;;14072:36;14125:19;;4801:81:7;13922:228:63;4801:81:7;1098:20;;4892:60;;;;-1:-1:-1;;;4892:60:7;;17608:2:63;4892:60:7;;;17590:21:63;17647:2;17627:18;;;17620:30;17686:31;17666:18;;;17659:59;17735:18;;4892:60:7;17580:179:63;4892:60:7;4964:12;4978:23;5005:6;-1:-1:-1;;;;;5005:11:7;5024:5;5031:4;5005:31;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4963:73;;;;5053:51;5070:7;5079:10;5091:12;5053:16;:51::i;:::-;5046:58;4612:499;-1:-1:-1;;;;;;;4612:499:7:o;6275:692::-;6421:12;6449:7;6445:516;;;-1:-1:-1;6479:10:7;6472:17;;6445:516;6590:17;;:21;6586:365;;6784:10;6778:17;6844:15;6831:10;6827:2;6823:19;6816:44;6733:145;6923:12;6916:20;;-1:-1:-1;;;6916:20:7;;;;;;;;:::i;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;14:134:63;82:20;;111:31;82:20;111:31;:::i;:::-;63:85;;;:::o;153:743::-;207:5;260:3;253:4;245:6;241:17;237:27;227:2;;282:5;275;268:20;227:2;322:6;309:20;348:4;371:18;367:2;364:26;361:2;;;393:18;;:::i;:::-;439:2;436:1;432:10;462:28;486:2;482;478:11;462:28;:::i;:::-;524:15;;;555:12;;;;587:15;;;621;;;617:24;;614:33;-1:-1:-1;611:2:63;;;664:5;657;650:20;611:2;690:5;681:14;;704:163;718:2;715:1;712:9;704:163;;;775:17;;763:30;;736:1;729:9;;;;;813:12;;;;845;;704:163;;;-1:-1:-1;885:5:63;217:679;-1:-1:-1;;;;;;;217:679:63:o;901:257::-;960:6;1013:2;1001:9;992:7;988:23;984:32;981:2;;;1034:6;1026;1019:22;981:2;1078:9;1065:23;1097:31;1122:5;1097:31;:::i;1163:368::-;1247:6;1300:2;1288:9;1279:7;1275:23;1271:32;1268:2;;;1321:6;1313;1306:22;1268:2;1366:9;1353:23;1399:18;1391:6;1388:30;1385:2;;;1436:6;1428;1421:22;1385:2;1464:61;1517:7;1508:6;1497:9;1493:22;1464:61;:::i;1536:297::-;1603:6;1656:2;1644:9;1635:7;1631:23;1627:32;1624:2;;;1677:6;1669;1662:22;1624:2;1714:9;1708:16;1767:5;1760:13;1753:21;1746:5;1743:32;1733:2;;1794:6;1786;1779:22;1838:190;1897:6;1950:2;1938:9;1929:7;1925:23;1921:32;1918:2;;;1971:6;1963;1956:22;1918:2;-1:-1:-1;1999:23:63;;1908:120;-1:-1:-1;1908:120:63:o;2033:325::-;2101:6;2109;2162:2;2150:9;2141:7;2137:23;2133:32;2130:2;;;2183:6;2175;2168:22;2130:2;2224:9;2211:23;2201:33;;2284:2;2273:9;2269:18;2256:32;2297:31;2322:5;2297:31;:::i;:::-;2347:5;2337:15;;;2120:238;;;;;:::o;2363:306::-;2421:6;2474:2;2462:9;2453:7;2449:23;2445:32;2442:2;;;2495:6;2487;2480:22;2442:2;2526:23;;-1:-1:-1;;;;;;2578:32:63;;2568:43;;2558:2;;2630:6;2622;2615:22;2961:193;3030:6;3083:2;3071:9;3062:7;3058:23;3054:32;3051:2;;;3104:6;3096;3089:22;3051:2;-1:-1:-1;3132:16:63;;3041:113;-1:-1:-1;3041:113:63:o;3159:1196::-;3253:6;3306:2;3294:9;3285:7;3281:23;3277:32;3274:2;;;3327:6;3319;3312:22;3274:2;3372:9;3359:23;3401:18;3442:2;3434:6;3431:14;3428:2;;;3463:6;3455;3448:22;3428:2;3491:22;;;;3547:6;3529:16;;;3525:29;3522:2;;;3572:6;3564;3557:22;3522:2;3603:22;;:::i;:::-;3648;3667:2;3648:22;:::i;:::-;3641:5;3634:37;3724:2;3720;3716:11;3703:25;3698:2;3691:5;3687:14;3680:49;3761:31;3788:2;3784;3780:11;3761:31;:::i;:::-;3756:2;3749:5;3745:14;3738:55;3839:2;3835;3831:11;3818:25;3868:2;3858:8;3855:16;3852:2;;;3889:6;3881;3874:22;3852:2;3930:56;3978:7;3967:8;3963:2;3959:17;3930:56;:::i;:::-;3925:2;3918:5;3914:14;3907:80;;4041:3;4037:2;4033:12;4020:26;4014:3;4007:5;4003:15;3996:51;4101:3;4097:2;4093:12;4080:26;4074:3;4067:5;4063:15;4056:51;4140:32;4167:3;4163:2;4159:12;4140:32;:::i;:::-;4134:3;4127:5;4123:15;4116:57;4206:32;4233:3;4229:2;4225:12;4206:32;:::i;:::-;4200:3;4193:5;4189:15;4182:57;4258:3;4248:13;;4293:31;4320:2;4316;4312:11;4293:31;:::i;:::-;4277:14;;;4270:55;;;;4281:5;3264:1091;-1:-1:-1;;;;3264:1091:63:o;4754:876::-;4831:6;4839;4892:2;4880:9;4871:7;4867:23;4863:32;4860:2;;;4913:6;4905;4898:22;4860:2;4954:9;4941:23;4931:33;;4983:2;5036;5025:9;5021:18;5008:32;5059:18;5100:2;5092:6;5089:14;5086:2;;;5121:6;5113;5106:22;5086:2;5164:6;5153:9;5149:22;5139:32;;5209:7;5202:4;5198:2;5194:13;5190:27;5180:2;;5236:6;5228;5221:22;5180:2;5277;5264:16;5299:2;5295;5292:10;5289:2;;;5305:18;;:::i;:::-;5347:53;5390:2;5371:13;;-1:-1:-1;;5367:27:63;5363:36;;5347:53;:::i;:::-;5334:66;;5423:2;5416:5;5409:17;5463:7;5458:2;5453;5449;5445:11;5441:20;5438:33;5435:2;;;5489:6;5481;5474:22;5435:2;5549;5544;5540;5536:11;5531:2;5524:5;5520:14;5507:45;5572:14;;5568:23;;;5561:39;;;;4850:780;;5576:5;;-1:-1:-1;4850:780:63;;-1:-1:-1;;4850:780:63:o;5635:274::-;5764:3;5802:6;5796:13;5818:53;5864:6;5859:3;5852:4;5844:6;5840:17;5818:53;:::i;:::-;5887:16;;;;;5772:137;-1:-1:-1;;5772:137:63:o;6311:786::-;6722:25;6717:3;6710:38;6692:3;6777:6;6771:13;6793:62;6848:6;6843:2;6838:3;6834:12;6827:4;6819:6;6815:17;6793:62;:::i;:::-;6919:19;6914:2;6874:16;;;6906:11;;;6899:40;6964:13;;6986:63;6964:13;7035:2;7027:11;;7020:4;7008:17;;6986:63;:::i;:::-;7069:17;7088:2;7065:26;;6700:397;-1:-1:-1;;;;6700:397:63:o;8038:635::-;8209:2;8261:21;;;8331:13;;8234:18;;;8353:22;;;8180:4;;8209:2;8432:15;;;;8406:2;8391:18;;;8180:4;8478:169;8492:6;8489:1;8486:13;8478:169;;;8553:13;;8541:26;;8622:15;;;;8587:12;;;;8514:1;8507:9;8478:169;;;-1:-1:-1;8664:3:63;;8189:484;-1:-1:-1;;;;;;8189:484:63:o;8678:664::-;8846:2;8898:21;;;8968:13;;8871:18;;;8990:22;;;8817:4;9055:20;;;9098:19;;;8817:4;;8846:2;9043;9028:18;;;8817:4;9148:168;9162:6;9159:1;9156:13;9148:168;;;9223:13;;9211:26;;9304:1;9292:14;;;;9257:12;;;;9177:9;9148:168;;11519:383;11668:2;11657:9;11650:21;11631:4;11700:6;11694:13;11743:6;11738:2;11727:9;11723:18;11716:34;11759:66;11818:6;11813:2;11802:9;11798:18;11793:2;11785:6;11781:15;11759:66;:::i;:::-;11886:2;11865:15;-1:-1:-1;;11861:29:63;11846:45;;;;11893:2;11842:54;;11640:262;-1:-1:-1;;11640:262:63:o;17764:407::-;17966:2;17948:21;;;18005:2;17985:18;;;17978:30;18044:34;18039:2;18024:18;;18017:62;-1:-1:-1;;;18110:2:63;18095:18;;18088:41;18161:3;18146:19;;17938:233::o;19955:255::-;20027:2;20021:9;20069:6;20057:19;;20106:18;20091:34;;20127:22;;;20088:62;20085:2;;;20153:18;;:::i;:::-;20189:2;20182:22;20001:209;:::o;20215:275::-;20286:2;20280:9;20351:2;20332:13;;-1:-1:-1;;20328:27:63;20316:40;;20386:18;20371:34;;20407:22;;;20368:62;20365:2;;;20433:18;;:::i;:::-;20469:2;20462:22;20260:230;;-1:-1:-1;20260:230:63:o;20495:128::-;20535:3;20566:1;20562:6;20559:1;20556:13;20553:2;;;20572:18;;:::i;:::-;-1:-1:-1;20608:9:63;;20543:80::o;20628:168::-;20668:7;20734:1;20730;20726:6;20722:14;20719:1;20716:21;20711:1;20704:9;20697:17;20693:45;20690:2;;;20741:18;;:::i;:::-;-1:-1:-1;20781:9:63;;20680:116::o;20801:125::-;20841:4;20869:1;20866;20863:8;20860:2;;;20874:18;;:::i;:::-;-1:-1:-1;20911:9:63;;20850:76::o;20931:258::-;21003:1;21013:113;21027:6;21024:1;21021:13;21013:113;;;21103:11;;;21097:18;21084:11;;;21077:39;21049:2;21042:10;21013:113;;;21144:6;21141:1;21138:13;21135:2;;;-1:-1:-1;;21179:1:63;21161:16;;21154:27;20984:205::o;21194:136::-;21233:3;21261:5;21251:2;;21270:18;;:::i;:::-;-1:-1:-1;;;21306:18:63;;21241:89::o;21335:127::-;21396:10;21391:3;21387:20;21384:1;21377:31;21427:4;21424:1;21417:15;21451:4;21448:1;21441:15;21467:127;21528:10;21523:3;21519:20;21516:1;21509:31;21559:4;21556:1;21549:15;21583:4;21580:1;21573:15;21599:154;-1:-1:-1;;;;;21678:5:63;21674:54;21667:5;21664:65;21654:2;;21743:1;21740;21733:12

Swarm Source

ipfs://6210265436bae3986d7d31a1391d1aa8a5b1e55bdac2df77dc1ea866ba81c711

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.