Contract 0xdd835a49432b12e96c1d8922212fcdfc4822be8f

 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xcc0d87bde4473d1f7e42d472dcb9d97efc3809e78ad3d7c749410699c3cb4889Initialize154776592022-06-23 7:05:57167 days 7 hrs ago0x420220b72bbd307db8615e7aa0eadca399cf2fc0 IN  0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH0.000226834016 ETH
0xcbd4fc2df6fcd946f73b7b06d2a0248517233550daaa7b96c63db849bc88f9170x60a0604053349142022-02-02 2:21:55308 days 12 hrs ago0x420220b72bbd307db8615e7aa0eadca399cf2fc0 IN  Create: RewardPool0 ETH0.072912310987 ETH
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xa0052df8bf1da02719ea67bfedfc11548389f842a2bb9e5c0ef667b400e2e98e161603432022-06-28 4:06:25162 days 10 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x0960f26e7f28c5a5ed687eb0d53fadeebb2c38aba6810cdc2559b43fd1872e76160484042022-06-27 9:58:22163 days 4 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0xb24a9bd85c8e822c30ea2cc541eec32e4bc526eff32bf074233564e219bbf332160483522022-06-27 9:57:15163 days 4 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0xb24a9bd85c8e822c30ea2cc541eec32e4bc526eff32bf074233564e219bbf332160483522022-06-27 9:57:15163 days 4 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x100c5aa93b43e2207c9936f9609e3c741a83db0927d2ff2da9a15ec444c49c57160154082022-06-27 5:57:42163 days 8 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x43cb106b1b27ae2b606476b7d148840349665754242d39863063fc34f6aff0f9158787852022-06-26 8:34:29164 days 6 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x43cb106b1b27ae2b606476b7d148840349665754242d39863063fc34f6aff0f9158787852022-06-26 8:34:29164 days 6 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x43cb106b1b27ae2b606476b7d148840349665754242d39863063fc34f6aff0f9158787852022-06-26 8:34:29164 days 6 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x12771668f8b409779105ab67e156184c87f4107c7123a1fd5d846af33d7137f1158787202022-06-26 8:32:40164 days 6 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x12771668f8b409779105ab67e156184c87f4107c7123a1fd5d846af33d7137f1158787202022-06-26 8:32:40164 days 6 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x0901ad61ab57319ddf2e21d40bea7436bb8b70b06c3cec4fa7f705594cc0ab2c152502272022-06-21 11:56:25169 days 2 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x0901ad61ab57319ddf2e21d40bea7436bb8b70b06c3cec4fa7f705594cc0ab2c152502272022-06-21 11:56:25169 days 2 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x0901ad61ab57319ddf2e21d40bea7436bb8b70b06c3cec4fa7f705594cc0ab2c152502272022-06-21 11:56:25169 days 2 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x601cae62705ed0b3486ecd2a47d8d8968158f7763b68787dd2a5b1059f39d40c152115902022-06-21 2:19:16169 days 12 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x601cae62705ed0b3486ecd2a47d8d8968158f7763b68787dd2a5b1059f39d40c152115902022-06-21 2:19:16169 days 12 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x601cae62705ed0b3486ecd2a47d8d8968158f7763b68787dd2a5b1059f39d40c152115902022-06-21 2:19:16169 days 12 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x8474a5153f96bf3078a1c9e990d06a3dceed4bb40c40986d41237930d98a2f4d152033782022-06-20 23:27:58169 days 15 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x8474a5153f96bf3078a1c9e990d06a3dceed4bb40c40986d41237930d98a2f4d152033782022-06-20 23:27:58169 days 15 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0x8474a5153f96bf3078a1c9e990d06a3dceed4bb40c40986d41237930d98a2f4d152033782022-06-20 23:27:58169 days 15 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0xc7b265fd148fc19a4f22544c3b4fa46b46cabaf4528b94d1a3f89ef0b820b222151677152022-06-20 13:53:57170 days 43 mins ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0xc7b265fd148fc19a4f22544c3b4fa46b46cabaf4528b94d1a3f89ef0b820b222151677152022-06-20 13:53:57170 days 43 mins ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0xc7b265fd148fc19a4f22544c3b4fa46b46cabaf4528b94d1a3f89ef0b820b222151677152022-06-20 13:53:57170 days 43 mins ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0xa4db51e6b2344642d909795338a2ae04af906346de46273724b0446fdf634008151390242022-06-20 6:33:12170 days 8 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0xa4db51e6b2344642d909795338a2ae04af906346de46273724b0446fdf634008151390242022-06-20 6:33:12170 days 8 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
0xa4cc1c7e3ddd5162a830cc7242f200c616adbdb612c5557847b574d028ba5449151389972022-06-20 6:33:12170 days 8 hrs ago 0xde17af0e4a6c870762508dcb7dcc20719584cbd0 0xdd835a49432b12e96c1d8922212fcdfc4822be8f0 ETH
[ Download CSV Export 
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.

Contract Source Code Verified (Exact Match)

Contract Name:
RewardPool

Compiler Version
v0.8.3+commit.8d00100c

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 23 : RewardPool.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
pragma abicoder v2;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/Bank/IHandleComponent.sol";
import "../interfaces/Bank/IHandle.sol";
import "../interfaces/rewards/IRewardPool.sol";
import "../Bank/Roles.sol";
import "../governance/GovernanceLock.sol";

/** @dev Provides scalable pools mapped by protocol reward category IDs.
 *       Categories are dynamic and may have weights adjusted as individual
 *       pools.
 *       Rewards are given in ERC20 (FOREX) and go into each category
 *       proportionally to their weights for the category users to claim.
 *       Pools may accept staking of a virtual number (no underlying asset),
 *       an ERC20 (not yet implemented) or an NFT (not yet implemented).
 *       Pool weights may be adjusted by an external DAO voting contract
 *       that has the OPERATOR_ROLE role.
 */
contract RewardPool is
    IRewardPool,
    IHandleComponent,
    Initializable,
    UUPSUpgradeable,
    Roles,
    ReentrancyGuardUpgradeable
{
    /** @dev The Handle contract interface */
    IHandle private handle;
    /** @dev The canonical FOREX token to be used for reward claiming.
     *       The contract assumes it always has sufficient balance to
     *       execute reward claims.
     */
    IERC20 public override forex;
    /** @dev Pool mapping from pool ID to Pool struct */
    mapping(uint256 => Pool) private pools;
    /** @dev Hash alias to pool pool ID */
    mapping(bytes32 => uint256) private poolAliases;
    /** @dev Array of enabled pools */
    uint256[] public enabledPools;
    /** @dev FOREX distribution rate per second */
    uint256 public forexDistributionRate;
    /** @dev Date at which the last distribution of FOREX happened */
    uint256 public lastDistributionDate;
    /** @dev Number of pools created */
    uint256 public poolCount;
    /** @dev The GovernanceLock contract */
    GovernanceLock governanceLock;

    /** @dev Proxy initialisation function */
    function initialize() public initializer {
        __AccessControl_init();
        __ReentrancyGuard_init();
        _setupRole(ADMIN_ROLE, msg.sender);
        _setRoleAdmin(OPERATOR_ROLE, ADMIN_ROLE);
        _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE);
    }

    /**
     * @dev Setter for Handle contract reference
     * @param _handle The Handle contract address
     */
    function setHandleContract(address _handle) public override onlyAdmin {
        handle = IHandle(_handle);
        forex = IERC20(handle.forex());
    }

    /**
     * @dev Setter for the GovernanceLock contract reference
     * @param _governanceLock The GovernanceLock contract address
     */
    function setGovernanceLock(address _governanceLock) external onlyAdmin {
        governanceLock = GovernanceLock(_governanceLock);
    }

    /** @dev Getter for Handle contract address */
    function handleAddress() public view override returns (address) {
        return address(handle);
    }

    /**
     * @dev Stakes into pool to start accruing rewards for pool.
     * @param account The account to stake with.
     * @param value The value to be staked.
     * @param poolId The pool ID to stake into.
     */
    function stake(
        address account,
        uint256 value,
        uint256 poolId
    ) external override returns (uint256 errorCode) {
        Pool storage pool = pools[poolId];
        // Check for staking permission.
        require(canStake(pool), "Unauthorized");
        // TODO: Add support for ERC20 & NFT LP tokens,
        require(pool.assetType == AssetType.None, "Not yet supported");
        // Try to claim before rewriting user deposit.
        claimForAccount(account);
        Deposit storage deposit = pool.deposits[account];
        // TODO: Correctly implement this for ERC20 & NFT LP tokens.
        updateDeposit(account, deposit.amount + value, poolId);
        emit Stake(account, poolId, value);
        return 0;
    }

    /**
     * @dev Returns the boosted stake value for an account and pool.
     * @param account The account to calculate the boosted value for.
     * @param value The provided stake value.
     * @param totalRealDeposits The liquidity to calculate the boosted value for.
     * @param boostWeight The account's boost wait for the calculation.
     */
    function getUserBoostedStake(
        address account,
        uint256 value,
        uint256 totalRealDeposits,
        uint256 boostWeight
    ) public view override returns (uint256) {
        // Return base value if the gFOREX data can't be fetched.
        if (address(governanceLock) == address(0) || value == 0) return value;
        uint256 totalSupply = governanceLock.totalSupply();
        // Prevent division by zero if total supply is zero.
        if (totalSupply == 0) return value;
        uint256 boost =
            (totalRealDeposits * boostWeight * 15) / (totalSupply * 10);
        uint256 total = value + boost;
        uint256 max = (value * 25) / 10;
        // Return the minimum between the boosted total and the value provided.
        return total > max ? max : total;
    }

    /**
     * @dev Unstakes from a pool to stop accruing rewards.
     * @param account The account to unstake with.
     * @param value The value to be unstaked.
     * @param poolId The poolId ID to unstake from.
     */
    function unstake(
        address account,
        uint256 value,
        uint256 poolId
    ) external override returns (uint256 errorCode) {
        Pool storage pool = pools[poolId];
        // Check for unstaking permission.
        require(canStake(pool), "Unauthorized");
        Deposit storage deposit = pool.deposits[account];
        // Return early if the account has nothing staked.
        if (deposit.amount == 0) return 1;
        // Try to claim before modifying deposit value due to unstaking.
        claimForAccount(account);
        // TODO: Add support for ERC20 & NFT LP tokens
        uint256 stakeAmount = deposit.amount;
        // Limit value instead of reverting.
        if (value > stakeAmount) value = stakeAmount;
        uint256 newAmount = stakeAmount - value;
        updateDeposit(account, newAmount, poolId);
        emit Unstake(account, poolId, value);
        return 0;
    }

    /**
     * @dev Updates a deposit amount and the S value.
     * @param account The account to update the deposit for.
     * @param amount The new deposit amount.
     * @param poolId The pool ID.
     */
    function updateDeposit(
        address account,
        uint256 amount,
        uint256 poolId
    ) private {
        Pool storage pool = pools[poolId];
        Deposit storage deposit = pool.deposits[account];
        // Current boosted deposit amount.
        uint256 currentDeposit =
            getUserBoostedStake(
                account,
                deposit.amount,
                pool.totalRealDeposits,
                deposit.boostWeight
            );
        // Update boostWeight with user's gFOREX balance if available.
        uint256 newBoostWeight =
            address(governanceLock) != address(0)
                ? governanceLock.balanceOf(account)
                : 0;
        deposit.boostWeight = newBoostWeight;
        // New boosted deposit amount, only used for the totalDeposits variable.
        if (amount == 0) {
            pool.totalDeposits = pool.totalDeposits - currentDeposit;
            pool.totalRealDeposits = pool.totalRealDeposits - deposit.amount;
            delete pool.deposits[account];
            return;
        }
        deposit.S = pool.S;
        pool.totalRealDeposits = deposit.amount > amount // Unstaking.
            ? pool.totalRealDeposits - (deposit.amount - amount) // Staking.
            : pool.totalRealDeposits + (amount - deposit.amount);
        uint256 newDeposit =
            getUserBoostedStake(account, amount, pool.totalRealDeposits, newBoostWeight);
        pool.totalDeposits = currentDeposit > newDeposit // Unstaking.
            ? pool.totalDeposits - (currentDeposit - newDeposit) // Staking.
            : pool.totalDeposits + (newDeposit - currentDeposit);
        deposit.amount = amount;
    }

    /**
     * @dev Claims FOREX rewards for caller.
     */
    function claim() external override {
        claimForAccount(msg.sender);
    }

    /**
     * @dev Claims FOREX rewards for account.
     */
    function claimForAccount(address account) private {
        // Update distribution as required before claiming.
        distribute();
        assert(lastDistributionDate == block.timestamp);
        uint256 enabledCount = enabledPools.length;
        uint256[] memory amounts = new uint256[](enabledCount);
        uint256 totalAmount;
        for (uint256 i = 0; i < enabledCount; i++) {
            uint256 poolId = enabledPools[i];
            Pool storage pool = pools[poolId];
            Deposit storage deposit = pool.deposits[account];
            // Cache storage read values.
            uint256 depositS = deposit.S;
            uint256 poolS = pool.S;
            uint256 boostWeightBeforeClaim = deposit.boostWeight;
            // Refresh boost value & totalDeposits if needed, and update
            // deposit's S value.
            updateDeposit(account, deposit.amount, poolId);
            // Continue to next iteration if there is nothing to claim.
            if (depositS >= poolS) continue;
            uint256 boostedAmount =
                getUserBoostedStake(
                    account,
                    deposit.amount,
                    pool.totalRealDeposits,
                    boostWeightBeforeClaim
                );
            uint256 deltaS = poolS - depositS;
            uint256 amount = (boostedAmount * deltaS) / (1 ether);
            // Update event variables.
            amounts[i] = amount;
            totalAmount += amount;
        }
        // Return early if there is nothing to claim.
        if (totalAmount == 0) return;
        // Transfer total reward to user.
        forex.transfer(account, totalAmount);
        emit Claim(account, totalAmount, enabledPools, amounts);
    }

    /**
     * @dev Creates a new pool.
     * @param weight The pool weight.
     * @param assetType The asset type for the pool.
     * @param assetAddress The asset address for the pool, or zero if none.
     * @param poolIds The array of pools to keep enabled and configure.
     * @param weights The weight array relative to the pools array.
     */
    function createPool(
        uint256 weight,
        AssetType assetType,
        address assetAddress,
        uint256[] memory poolIds,
        uint256[] memory weights
    ) external override onlyOperatorOrAdmin {
        _setPools(poolIds, weights);
        // Create new pool.
        uint256 newPoolId = poolCount++;
        Pool storage pool = pools[newPoolId];
        pool.weight = weight;
        pool.assetType = assetType;
        pool.assetAddress = assetAddress;
        addToEnabledPools(newPoolId);
        emit CreatePool(newPoolId, assetType, assetAddress, weight);
    }

    /**
     * @dev Allows for setting up multiple pools at once after deployment.
     * @param assetTypes The asset type array for each pool.
     * @param assetAddresses The asset addresses array for each pool.
     * @param weights The weight array relative to the pools array.
     * @param aliases The aliases for the pools.
     */
    function setupPools(
        AssetType[] memory assetTypes,
        address[] memory assetAddresses,
        uint256[] memory weights,
        bytes32[] memory aliases
    ) external override onlyOperatorOrAdmin {
        require(poolCount == 0, "Pools already initialised");
        uint256 count = assetTypes.length;
        assert(count == assetAddresses.length && count == weights.length);
        uint256[] memory poolIds = new uint256[](count);
        for (uint256 i = 0; i < count; i++) {
            Pool storage pool = pools[i];
            pool.assetType = assetTypes[i];
            pool.assetAddress = assetAddresses[i];
            pool.weight = weights[i];
            poolIds[i] = i;
            setPoolAlias(aliases[i], i);
            emit CreatePool(i, assetTypes[i], assetAddresses[i], weights[i]);
        }
        enabledPools = poolIds;
        poolCount = count;
    }

    /**
     * @dev Used to enable and disable pools.
     *      To disable, a weight must be set to zero.
     *      Will emit events accordingly.
     * @param poolIds] The array of poolIds] to configure.
     * @param weights The array of weights to configure.
     */
    function setPools(uint256[] memory poolIds, uint256[] memory weights)
        public
        override
        onlyOperatorOrAdmin
    {
        _setPools(poolIds, weights);
    }

    /**
     * @dev Used to enable and disable pools.
     *      To disable, a weight must be set to zero.
     *      Will emit events accordingly.
     * @param poolIds The array of pool IDs to configure.
     * @param weights The array of weights to configure.
     */
    function _setPools(uint256[] memory poolIds, uint256[] memory weights)
        private
    {
        uint256 enabledLength = poolIds.length;
        assert(enabledLength == weights.length);
        // Cache storage read.
        uint256 count = poolCount;
        // Set pool weights as required.
        for (uint256 i = 0; i < count; i++) {
            // Find whether this pool is to be disabled.
            bool foundInEnabled = false;
            uint256 foundEnabledIndex;
            for (uint256 j = 0; j < enabledLength; j++) {
                if (i != poolIds[j]) continue;
                foundInEnabled = true;
                foundEnabledIndex = j;
                break;
            }
            Pool storage pool = pools[poolIds[i]];
            // Disable pool if needed, otherwise override weight.
            pool.weight = !foundInEnabled ? 0 : weights[foundEnabledIndex];
        }
        enabledPools = poolIds;
        emit SetPoolWeights(poolIds, weights);
    }

    /**
     * @dev Checks whether account has the permission to stake or unstake.
     * @param pool The pool reference.
     */
    function canStake(Pool storage pool) private view returns (bool) {
        return ((pool.assetType != AssetType.None && pool.weight > 0) ||
            pool.stakerWhitelist[msg.sender]);
    }

    /**
     * @dev Set the FOREX distribution rate per second.
     * @param rate The FOREX rate per second to distribute across pools.
     */
    function setForexDistributionRate(uint256 rate)
        external
        override
        onlyOperatorOrAdmin
    {
        // Return early if rates are the same.
        if (rate == forexDistributionRate) return;
        // Distribute outstanding FOREX before updating the rate.
        if (lastDistributionDate != block.timestamp) {
            distribute();
            assert(lastDistributionDate == block.timestamp);
        }
        forexDistributionRate = rate;
        emit SetForexDistributionRate(rate);
    }

    /**
     * @dev Distributes outstanding FOREX across pools.
     *      Updates the lastDistributionDate to the current block timestamp
     *      if successful.
     */
    function distribute() public override {
        // Return early after running once this block.
        if (block.timestamp == lastDistributionDate) return;
        (, uint256[] memory amounts, uint256[] memory deltaS) = getPoolsData();
        // Calculate seconds passed since last distribution.
        uint256 duration = block.timestamp - lastDistributionDate;
        // Update the last distribution date.
        lastDistributionDate = block.timestamp;
        // Return early if there are no amounts to distribute.
        if (amounts.length == 0) return;
        assert(amounts.length == deltaS.length);
        uint256 totalAmount = 0;
        // Cache storage read.
        uint256 count = enabledPools.length;
        for (uint256 i = 0; i < count; i++) {
            Pool storage pool = pools[enabledPools[i]];
            // Continue if there are no amounts or pool is disabled.
            if (amounts[i] == 0 || pool.weight == 0) continue;
            pool.S += deltaS[i];
            totalAmount += amounts[i];
        }
        emit ForexDistributed(
            duration,
            forexDistributionRate,
            totalAmount,
            enabledPools,
            amounts
        );
    }

    /**
     * @dev Sets or unsets a staking address as whitelisted from a pool.
     *      The whitelist is only used if the pool's asset type is None.
     */
    function setWhitelistedStaker(
        address staker,
        uint256 poolId,
        bool isWhitelisted
    ) external override onlyOperatorOrAdmin {
        pools[poolId].stakerWhitelist[staker] = isWhitelisted;
        emit WhitelistChanged(staker, poolId, isWhitelisted);
    }

    /** @dev Sets a pool hash alias. These are used to fetch similar pools
     *       of pools without relying on the ID directly, e.g. all minting
     *       rewards pool for the protocol fxTokens. The ID for those may be
     *       obtained by e.g. getPoolAlias(keccak256("minting" + tokenAddress))
     */
    function setPoolAlias(bytes32 hash, uint256 poolId)
        public
        override
        onlyOperatorOrAdmin
    {
        // The poolId gets added by 1 so that a value of zero can revert
        // to identify aliases that have not been set (see getPoolAlias).
        poolAliases[hash] = poolId + 1;
        emit PoolAliasChanged(poolId, hash);
    }

    /**
     * @dev Adds an item to the enabledPools array
     * @param poolId The poolId ID to push to the array.
     */
    function addToEnabledPools(uint256 poolId) private {
        uint256 count = enabledPools.length;
        for (uint256 i = 0; i < count; i++) {
            if (enabledPools[i] != poolId) continue;
            revert("Pushing duplicate pool ID");
        }
        enabledPools.push(poolId);
    }

    /**
     * @dev Calculates pool parameters for all enabled pools from the
     *      current block.
     */
    function getPoolsData()
        public
        view
        override
        returns (
            uint256[] memory poolRatios,
            uint256[] memory accruedAmounts,
            uint256[] memory deltaS
        )
    {
        uint256 enabledCount = enabledPools.length;
        poolRatios = new uint256[](enabledCount);
        accruedAmounts = new uint256[](enabledCount);
        deltaS = new uint256[](enabledCount);
        // Return early if there no pools enabled.
        if (enabledCount == 0) return (poolRatios, accruedAmounts, deltaS);
        // Return early after running once this block.
        if (block.timestamp == lastDistributionDate)
            return (poolRatios, accruedAmounts, deltaS);
        // Return early if rate is zero.
        if (forexDistributionRate == 0)
            return (poolRatios, accruedAmounts, deltaS);
        uint256 totalWeight = getTotalWeight();
        uint256 rate = forexDistributionRate;
        uint256 duration = block.timestamp - lastDistributionDate;
        uint256 amount = duration * rate;
        for (uint256 i = 0; i < enabledCount; i++) {
            Pool storage pool = pools[enabledPools[i]];
            if (pool.weight == 0) continue;
            poolRatios[i] = (pool.weight * (1 ether)) / totalWeight;
            if (pool.totalDeposits == 0) continue;
            accruedAmounts[i] = (amount * poolRatios[i]) / (1 ether);
            deltaS[i] = (accruedAmounts[i] * (1 ether)) / pool.totalDeposits;
        }
    }

    /**
     * @dev Returns a reward pool alias for the fxToken and pool category.
     * @param token The fxToken address to get the pool for.
     * @param category The reward category number.
     */
    function getFxTokenPoolAlias(address token, uint256 category)
        external
        view
        override
        returns (bytes32)
    {
        return keccak256(abi.encodePacked(token, category));
    }

    /** @dev Returns a pool hash alias or reverts if not defined */
    function getPoolIdByAlias(bytes32 hash)
        public
        view
        override
        returns (bool found, uint256 poolId)
    {
        uint256 result = poolAliases[hash];
        if (result == 0) return (false, 0);
        return (true, result - 1);
    }

    /**
     * @dev Get pool data.
     * @param poolId The ID of the pool to view.
     */
    function getPool(uint256 poolId)
        external
        view
        override
        returns (
            uint256 weight,
            AssetType assetType,
            address assetAddress,
            uint256 totalDeposits,
            uint256 S,
            uint256 totalRealDeposits
        )
    {
        Pool storage pool = pools[poolId];
        return (
            pool.weight,
            pool.assetType,
            pool.assetAddress,
            pool.totalDeposits,
            pool.S,
            pool.totalRealDeposits
        );
    }

    /**
     * @dev Get deposit for address.
     * @param account The account to view.
     * @param poolId The ID of the pool to view.
     */
    function getDeposit(address account, uint256 poolId)
        external
        view
        override
        returns (Deposit memory)
    {
        return pools[poolId].deposits[account];
    }

    /**
     * @dev Returns the reward balance for an address.
     * @param account The address to fetch balance for.
     */
    function balanceOf(address account)
        public
        view
        override
        returns (uint256 balance)
    {
        (, , uint256[] memory deltaS) = getPoolsData();
        uint256 count = enabledPools.length;
        for (uint256 i = 0; i < count; i++) {
            uint256 poolId = enabledPools[i];
            Pool storage pool = pools[poolId];
            Deposit storage deposit = pool.deposits[account];
            uint256 depositS = deposit.S;
            uint256 poolS = pool.S + deltaS[i];
            uint256 depositAmount = deposit.amount;
            depositAmount = getUserBoostedStake(
                account,
                depositAmount,
                pool.totalRealDeposits,
                deposit.boostWeight
            );
            if (depositAmount == 0 || depositS >= poolS) continue;
            uint256 deltaS = poolS - depositS;
            balance += (depositAmount * deltaS) / (1 ether);
        }
    }

    /**
     * @dev View to return the length of the enabledPools array,
     *      because Solidity is a special snowflake.
     */
    function enabledPoolsLength() external view returns (uint256) {
        return enabledPools.length;
    }

    /**
     * @dev Returns the total weight for all enabled pools.
     */
    function getTotalWeight() private view returns (uint256 totalWeight) {
        uint256 enabledCount = enabledPools.length;
        for (uint256 i = 0; i < enabledCount; i++) {
            totalWeight += pools[enabledPools[i]].weight;
        }
    }

    /**
     * @dev Sets totalRealDeposits to totalDeposits for all pools.
     *      Only works if totalRealDeposits is zero and totalDeposits is not.
     *      Should only be called before boosts are active due to the
     *      totalRealDeposits variable being added to the code as a fix.
     */
    function syncTotalRealDeposits() external onlyAdmin {
        uint256 count = poolCount;
        for (uint256 i = 0; i < count; i++) {
            Pool storage pool = pools[i];
            if (pool.totalDeposits == 0)
                continue;
            pool.totalRealDeposits = pool.totalDeposits;
        }
    }

    /** @dev Protected UUPS upgrade authorization function */
    function _authorizeUpgrade(address) internal override onlyAdmin {}
}

File 2 of 23 : 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 3 of 23 : ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

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

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

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

    uint256 private _status;

    function __ReentrancyGuard_init() internal onlyInitializing {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        _status = _NOT_ENTERED;
    }

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

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

        _;

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

File 4 of 23 : UUPSUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.0;

import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
import "./Initializable.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 *
 * _Available since v4.1._
 */
abstract contract UUPSUpgradeable is Initializable, ERC1967UpgradeUpgradeable {
    function __UUPSUpgradeable_init() internal onlyInitializing {
        __ERC1967Upgrade_init_unchained();
        __UUPSUpgradeable_init_unchained();
    }

    function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
    }
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
    address private immutable __self = address(this);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        require(address(this) != __self, "Function must be called through delegatecall");
        require(_getImplementation() == __self, "Function must be called through active proxy");
        _;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     */
    function upgradeTo(address newImplementation) external virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallSecure(newImplementation, new bytes(0), false);
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallSecure(newImplementation, data, true);
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeTo} and {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal override onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;
    uint256[50] private __gap;
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

File 6 of 23 : IHandleComponent.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
pragma abicoder v2;

interface IHandleComponent {
    function setHandleContract(address hanlde) external;

    function handleAddress() external view returns (address);
}

File 7 of 23 : IHandle.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

pragma abicoder v2;

interface IHandle {
    struct Vault {
        // Collateral token address => balance
        mapping(address => uint256) collateralBalance;
        uint256 debt;
        // Collateral token address => R0
        mapping(address => uint256) R0;
    }

    struct CollateralData {
        uint256 mintCR;
        uint256 liquidationFee;
        uint256 interestRate;
    }

    event UpdateDebt(address indexed account, address indexed fxToken);

    event UpdateCollateral(
        address indexed account,
        address indexed fxToken,
        address indexed collateralToken
    );

    event ConfigureCollateralToken(address indexed collateralToken);

    event ConfigureFxToken(address indexed fxToken, bool removed);

    function setCollateralUpperBoundPCT(uint256 ratio) external;

    function setPaused(bool value) external;

    function setFxToken(address token) external;

    function removeFxToken(address token) external;

    function setCollateralToken(
        address token,
        uint256 mintCR,
        uint256 liquidationFee,
        uint256 interestRatePerMille
    ) external;

    function removeCollateralToken(address token) external;

    function getAllCollateralTypes()
        external
        view
        returns (address[] memory collateral);

    function getCollateralDetails(address collateral)
        external
        view
        returns (CollateralData memory);

    function WETH() external view returns (address);

    function treasury() external view returns (address payable);

    function comptroller() external view returns (address);

    function vaultLibrary() external view returns (address);

    function fxKeeperPool() external view returns (address);

    function pct() external view returns (address);

    function liquidator() external view returns (address);

    function interest() external view returns (address);

    function referral() external view returns (address);

    function forex() external view returns (address);

    function rewards() external view returns (address);

    function pctCollateralUpperBound() external view returns (uint256);

    function isFxTokenValid(address fxToken) external view returns (bool);

    function isCollateralValid(address collateral) external view returns (bool);

    function setComponents(address[] memory components) external;

    function updateDebtPosition(
        address account,
        uint256 amount,
        address fxToken,
        bool increase
    ) external;

    function updateCollateralBalance(
        address account,
        uint256 amount,
        address fxToken,
        address collateralToken,
        bool increase
    ) external;

    function setFeeRecipient(address feeRecipient) external;

    function setFees(
        uint256 withdrawFeePerMille,
        uint256 depositFeePerMille,
        uint256 mintFeePerMille,
        uint256 burnFeePerMille
    ) external;

    function getCollateralBalance(
        address account,
        address collateralType,
        address fxToken
    ) external view returns (uint256 balance);

    function getBalance(address account, address fxToken)
        external
        view
        returns (address[] memory collateral, uint256[] memory balances);

    function getDebt(address owner, address fxToken)
        external
        view
        returns (uint256 _debt);

    function getPrincipalDebt(address owner, address fxToken)
        external
        view
        returns (uint256 _debt);

    function getCollateralR0(
        address account,
        address fxToken,
        address collateral
    ) external view returns (uint256 R0);

    function getTokenPrice(address token) external view returns (uint256 quote);

    function setOracle(address fxToken, address oracle) external;

    function FeeRecipient() external view returns (address);

    function mintFeePerMille() external view returns (uint256);

    function burnFeePerMille() external view returns (uint256);

    function withdrawFeePerMille() external view returns (uint256);

    function depositFeePerMille() external view returns (uint256);

    function isPaused() external view returns (bool);
}

File 8 of 23 : IRewardPool.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
pragma abicoder v2;

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

/**
 * @dev Established pool categories to be used internally by the
 * protocol for the users.
 * This enum is used to get the correct pool ID from the pool alias
 * which consists of an fxToken address and a category number.
 */
enum RewardPoolCategory {Mint, Deposit, Keeper}

interface IRewardPool {
    /** Reward category e.g. Keepers or Liquidity Providers */
    struct Pool {
        // Pool reward weight. The amount of FOREX allocated to this
        // category is given by the ratio of this weight to the sum of all
        // weights for enabled pools.
        // A category is enabled if the weight is not zero.
        uint256 weight;
        // Asset is used for pools that require token staking,
        // such as LP tokens. Could be e.g. None, ERC20 or ERC721.
        AssetType assetType;
        // For pools with no token (AssetType::None), this value is zero.
        address assetAddress;
        // If AssetType == None, whitelist with this map from staker address.
        mapping(address => bool) stakerWhitelist;
        // Account -> Deposit
        mapping(address => Deposit) deposits;
        // Total amount deposited, which may be boosted.
        uint256 totalDeposits;
        // Current pool reward ratio over total deposits.
        uint256 S;
        // Total real value deposited (excluding boosts).
        uint256 totalRealDeposits;
    }

    /** Reward pool deposit for tracking user contributions */
    struct Deposit {
        // Amount contributed.
        // e.g. for Keepers, the total amount staked.
        //      for minters, the total amount minted.
        //      for LPs, the total liquidity provided.
        uint256 amount;
        // Reward ratio over total deposits during deposit.
        uint256 S;
        // The weight (gFOREX balance) by which the boost is
        // calculated from during claim.
        uint256 boostWeight;
    }

    enum AssetType {None, ERC20, ERC721}

    event Stake(address indexed account, uint256 poolId, uint256 amount);

    event Unstake(address indexed account, uint256 poolId, uint256 amount);

    event CreatePool(
        uint256 id,
        AssetType assetType,
        address asset,
        uint256 weight
    );

    event SetPoolWeights(uint256[] poolIds, uint256[] weights);

    event SetFxTokenWeights(address[] fxTokens, uint256[] weights);

    event ForexDistributed(
        uint256 duration,
        uint256 rate,
        uint256 totalAmount,
        uint256[] poolIds,
        uint256[] amounts
    );

    event SetForexDistributionRate(uint256 ratePerSecond);

    event Claim(
        address indexed acount,
        uint256 amount,
        uint256[] poolIds,
        uint256[] amounts
    );

    event WhitelistChanged(address staker, uint256 poolId, bool whitelisted);

    event PoolAliasChanged(uint256 poolId, bytes32 aliasHash);

    function stake(
        address account,
        uint256 value,
        uint256 poolId
    ) external returns (uint256 errorCode);

    function unstake(
        address account,
        uint256 value,
        uint256 poolId
    ) external returns (uint256 errorCode);

    function claim() external;

    function distribute() external;

    function createPool(
        uint256 weight,
        AssetType assetType,
        address assetAddress,
        uint256[] memory poolIds,
        uint256[] memory weights
    ) external;

    function setupPools(
        AssetType[] memory assetTypes,
        address[] memory assetAddresses,
        uint256[] memory weights,
        bytes32[] memory aliases
    ) external;

    // Used to enable and disable pools.
    // To disable, a weight must be set to zero.
    // Will emit events accordingly.
    function setPools(uint256[] memory poolIds, uint256[] memory weights)
        external;

    function setWhitelistedStaker(
        address staker,
        uint256 poolId,
        bool isWhitelisted
    ) external;

    function setForexDistributionRate(uint256 rate) external;

    function setPoolAlias(bytes32 hash, uint256 poolId) external;

    function getPoolsData()
        external
        view
        returns (
            uint256[] memory poolRatios,
            uint256[] memory accruedAmounts,
            uint256[] memory deltaS
        );

    function getPoolIdByAlias(bytes32 hash)
        external
        view
        returns (bool found, uint256 poolId);

    function getFxTokenPoolAlias(address token, uint256 category)
        external
        view
        returns (bytes32);

    // Return allowed parameters only (no mappings)
    // because Solidity is a Special Snowflake (tm)
    function getPool(uint256 poolId)
        external
        view
        returns (
            uint256 weight,
            AssetType assetType,
            address assetAddress,
            uint256 totalDeposits,
            uint256 S,
            uint256 totalRealDeposits
        );

    function getDeposit(address account, uint256 poolId)
        external
        view
        returns (Deposit memory);

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

    function getUserBoostedStake(
        address account,
        uint256 value,
        uint256 poolId,
        uint256 boostWeight
    ) external view returns (uint256);

    function forex() external view returns (IERC20);
}

File 9 of 23 : Roles.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

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

abstract contract Roles is AccessControlUpgradeable {
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

    modifier onlyOperator() {
        require(hasRole(OPERATOR_ROLE, msg.sender), "NO");
        _;
    }

    modifier onlyAdmin() {
        require(hasRole(ADMIN_ROLE, msg.sender), "NA");
        _;
    }

    modifier onlyOperatorOrAdmin() {
        require(
            hasRole(OPERATOR_ROLE, msg.sender) ||
                hasRole(ADMIN_ROLE, msg.sender),
            "NW"
        );
        _;
    }

    modifier onlyAddressOrOperatorExcludeAdmin(address addressAllowed) {
        // Protect user deposits from abuse
        require(
            msg.sender == addressAllowed ||
                (hasRole(OPERATOR_ROLE, msg.sender) &&
                    !hasRole(ADMIN_ROLE, msg.sender)),
            "NW"
        );
        _;
    }
}

File 10 of 23 : GovernanceLock.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
pragma abicoder v2;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/rewards/IRewardPool.sol";

struct Point {
    int128 bias;
    int128 slope;
    uint256 ts;
    uint256 blk; // block
}

struct LockedBalance {
    int128 amount;
    uint256 end;
}

/**
 * @dev Allows FOREX holders to lock tokens for gFOREX and gain Handle DAO voting power.
 *      FOREX may be locked for up to 4 years.
 *      gFOREX balance decays linearly during the locked period until FOREX is fully unlocked.
 *      A locked position may be added to or have its duration extended at any time for an
 *      increase in gFOREX voting power.
 */
contract GovernanceLock is
    Initializable,
    UUPSUpgradeable,
    OwnableUpgradeable,
    ReentrancyGuardUpgradeable
{
    using SafeERC20 for IERC20;

    int128 public constant MAX_TIME = 4 * 365 * 86400; // 4 years
    bytes32 public constant REWARD_POOL_ALIAS = keccak256("governancelock");

    /** @dev The token to be locked. e.g. FOREX */
    address public token;
    /** @dev The supply of token's locked */
    uint256 public supply;
    /** @dev Mapping from account address to locked balance position */
    mapping(address => LockedBalance) public locked;
    /** @dev The current system epoch */
    uint256 public epoch;
    /** @dev Mapping from epoch to point history */
    mapping(uint256 => Point) public pointHistory;
    /** @dev Mapping from account address to epoch to point history */
    mapping(address => mapping(uint256 => Point)) public userPointHistory;
    /** @dev Mapping from account address to current user epoch */
    mapping(address => uint256) public userPointEpoch;
    /** @dev Mapping from slope changes @ "week time" to slope value */
    mapping(uint256 => int128) public slopeChanges;
    /** @dev Mapping from contract address to its whitelisted status */
    mapping(address => bool) public whitelistedContracts;
    /** @dev Whether the whitelist is enabled for contract access */
    bool public isWhitelistEnabled;
    /** @dev The Handle reward pool for rewarding locking */
    IRewardPool public rewardPool;

    uint256 public constant WEEK = 7 * 86400;
    uint256 public constant MULTIPLIER = 1 ether;

    /** @dev Whether the contract has been retired and token refunds are on */
    bool public retiredContract;

    enum DepositType {
        DepositFor,
        CreateLock,
        IncreaseLockAmount,
        IncreaseUnlockTime
    }

    event Deposit(
        address indexed depositor,
        uint256 value,
        uint256 indexed locktime,
        DepositType depositType,
        uint256 ts
    );
    event Withdraw(address indexed depositor, uint256 value, uint256 ts);
    event Supply(uint256 previousSupply, uint256 supply);

    /**
     * @dev Reverts the tranasction if the sender is a contract and not
     *      whitelisted.
     */
    modifier onlyAllowedLocker() {
        require(
            !isWhitelistEnabled ||
                !isContract(msg.sender) ||
                whitelistedContracts[msg.sender],
            "Contract not allowed"
        );
        _;
    }

    /** @dev Proxy initialisation function */
    function initialize(address tokenAddress, address rewardPoolAddress)
        public
        initializer
    {
        __UUPSUpgradeable_init();
        __Ownable_init();
        __ReentrancyGuard_init();
        token = tokenAddress;
        rewardPool = IRewardPool(rewardPoolAddress);
        isWhitelistEnabled = true;
        pointHistory[0].blk = block.number;
        pointHistory[0].ts = block.timestamp;
    }

    /**
     * @dev "Retires" the contract by allowing refunds and preventing new deposits.
     * @param isRetired Whether to retire the contract.
     */
    function retireContract(bool isRetired) external onlyOwner {
        retiredContract = isRetired;
    }

    /**
     * @dev Adds or removes a contract access from the whitelist.
     */
    function setContractWhitelist(address contractAddress, bool isWhitelisted)
        external
        onlyOwner
    {
        whitelistedContracts[contractAddress] = isWhitelisted;
    }

    /**
     * @dev Enables or disables the contract access whitelist.
     */
    function setWhitelistEnabled(bool isEnabled) external onlyOwner {
        isWhitelistEnabled = isEnabled;
    }

    /**
     * @dev Returns user slope at last user epoch.
     */
    function getLastUserSlope(address account) external view returns (int128) {
        uint256 userEpoch = userPointEpoch[account];
        return userPointHistory[account][userEpoch].slope;
    }

    /**
     * @dev Getter for user point history at point idx
     */
    function userPointHistoryTs(address account, uint256 idx)
        external
        view
        returns (uint256)
    {
        return userPointHistory[account][idx].ts;
    }

    /**
     * @dev Getter for account's locked position end time.
     */
    function lockedEnd(address account) external view returns (uint256) {
        return locked[account].end;
    }

    /**
     * @dev Updates the system state and optionally for a given account.
     */
    function _checkpoint(
        address account,
        LockedBalance memory oldLocked,
        LockedBalance memory newLocked
    ) private {
        Point memory uOld = EMPTY_POINT_FACTORY();
        Point memory uNew = EMPTY_POINT_FACTORY();
        int128 oldDslope;
        int128 newDslope;
        uint256 _epoch = epoch;

        if (account != address(0)) {
            // Calculate slopes and biases
            // kept at zero when they have to
            if (oldLocked.end > block.timestamp && oldLocked.amount > 0) {
                uOld.slope = oldLocked.amount / MAX_TIME;
                uOld.bias =
                    uOld.slope *
                    int128(int256(oldLocked.end) - int256(block.timestamp));
            }
            if (newLocked.end > block.timestamp && newLocked.amount > 0) {
                uNew.slope = newLocked.amount / MAX_TIME;
                uNew.bias =
                    uNew.slope *
                    int128(int256(newLocked.end) - int256(block.timestamp));
            }
            // Read values of schedules changes in the slope
            // oldLocked.end can be in the past and in the future
            // newLocked.end can ONLY be in the FUTURE unless everything
            // expired: then zeros
            oldDslope = slopeChanges[oldLocked.end];
            if (newLocked.end != 0) {
                if (newLocked.end == oldLocked.end) {
                    newDslope = oldDslope;
                } else {
                    newDslope = slopeChanges[newLocked.end];
                }
            }
        }

        Point memory lastPoint =
            Point({bias: 0, slope: 0, ts: block.timestamp, blk: block.number});

        if (epoch > 0) lastPoint = pointHistory[_epoch];
        uint256 lastCheckpoint = lastPoint.ts;

        // Used for extrapolation to calculate block number
        // (approximately, for *At methods) and save them
        // as it cannot be figured out exactly from inside the contract
        Point memory initialLastPoint = lastPoint;
        uint256 blockSlope;
        if (block.timestamp > lastPoint.ts)
            blockSlope =
                (MULTIPLIER * (block.number - lastPoint.blk)) /
                (block.timestamp - lastPoint.ts);

        // Round to nearest week.
        // Go over weeks to fill history and calculate what the current point
        // is.
        {
            uint256 t_i = (lastCheckpoint / WEEK) * WEEK;
            for (uint256 i = 0; i < 255; i++) {
                // If this does not get used in 5 years, users will be able to
                // withdraw but vote weight will be broken.
                t_i += WEEK;
                int128 dSlope;
                if (t_i > block.timestamp) {
                    t_i = block.timestamp;
                } else {
                    dSlope = slopeChanges[t_i];
                }
                lastPoint.bias -=
                    lastPoint.slope *
                    int128(int256(t_i) - int256(lastCheckpoint));
                lastPoint.slope += dSlope;
                // This can happen.
                if (lastPoint.bias < 0) {
                    lastPoint.bias = 0;
                }
                // In theory this cannot happen.
                if (lastPoint.slope < 0) {
                    lastPoint.slope = 0;
                }
                lastCheckpoint = t_i;
                lastPoint.ts = t_i;
                lastPoint.blk =
                    initialLastPoint.blk +
                    (blockSlope * (t_i - initialLastPoint.ts)) /
                    MULTIPLIER;
                _epoch += 1;
                if (t_i == block.timestamp) {
                    lastPoint.blk = block.number;
                    break;
                } else {
                    pointHistory[_epoch] = lastPoint;
                }
            }
        }
        epoch = _epoch;
        // pointHistory is now up to date with current block
        if (account != address(0)) {
            lastPoint.slope += (uNew.slope - uOld.slope);
            lastPoint.bias += (uNew.bias - uOld.bias);
            if (lastPoint.slope < 0) lastPoint.slope = 0;
            if (lastPoint.bias < 0) lastPoint.bias = 0;
        }
        // Record the changed point into history
        pointHistory[_epoch] = lastPoint;
        if (account != address(0)) {
            // Schedule the slope changes (slope is going down)
            // Subtract new slope from [newLocked.end]
            // Add old slope to [oldLocked.epoch]
            if (oldLocked.end > block.timestamp) {
                oldDslope += uOld.slope;
                if (newLocked.end == oldLocked.end) oldDslope -= uNew.slope; // new deposit, not extension
                slopeChanges[oldLocked.end] = oldDslope;
            }
            if (newLocked.end > block.timestamp) {
                if (newLocked.end > oldLocked.end) {
                    newDslope -= uNew.slope;
                    slopeChanges[newLocked.end] = newDslope;
                }
                // else: has already been recorded in oldDslope
            }
            // Handle user history
            uint256 userEpoch = userPointEpoch[account] + 1;
            userPointEpoch[account] = userEpoch;
            uNew.ts = block.timestamp;
            uNew.blk = block.number;
            userPointHistory[account][userEpoch] = Point({
                bias: uNew.bias,
                slope: uNew.slope,
                ts: block.timestamp,
                blk: block.number
            });
        }
    }

    /**
     * @dev Internal function to handle FOREX deposits and/or locktime increase.
     */
    function _depositFor(
        address account,
        uint256 value,
        uint256 unlockTime,
        LockedBalance memory lockedBalance,
        DepositType depositType
    ) private {
        require(!retiredContract, "Contract retired");
        LockedBalance memory _locked = lockedBalance;
        uint256 supplyBefore = supply;
        supply = supplyBefore + value;
        LockedBalance memory oldLocked =
            LockedBalance({amount: _locked.amount, end: _locked.end});
        // Adding to existing lock, or if expired create a new one
        _locked.amount += int128(int256(value));
        if (unlockTime != 0) _locked.end = unlockTime;
        locked[account] = _locked;
        // Possibilities:
        // both oldLocked.end could be current or expired (>/< block.timestamp)
        // value == 0 (extend lock) or value > 0 (add to lock or extend lock)
        // _locked.end > block.timestamp (always)
        _checkpoint(account, oldLocked, _locked);
        if (value != 0)
            IERC20(token).safeTransferFrom(account, address(this), value);
        // Stake into reward pool.
        uint256 stakeAmount =
            _locked.amount > 0 ? uint256(uint128(_locked.amount)) : 0;
        setUserRewardStakeAmount(account, stakeAmount);
        emit Deposit(account, value, _locked.end, depositType, block.timestamp);
        emit Supply(supplyBefore, supplyBefore + value);
    }

    /**
     * @dev Sets the staked value in the gFOREX reward pool for an account.
     */
    function setUserRewardStakeAmount(address account, uint256 value) private {
        if (retiredContract) return;
        (bool foundRewardPool, uint256 rewardPoolId) =
            rewardPool.getPoolIdByAlias(REWARD_POOL_ALIAS);
        if (!foundRewardPool) return;
        // Unstake current amount from pool.
        rewardPool.unstake(account, 2**256 - 1, rewardPoolId);
        if (value > 0) {
            // Stake value.
            rewardPool.stake(account, value, rewardPoolId);
        }
    }

    /**
     * @dev Updates the system state without affecting any
     *      specific account directly.
     */
    function checkpoint() external {
        LockedBalance memory empty;
        _checkpoint(
            address(0),
            EMPTY_LOCKED_BALANCE_FACTORY(),
            EMPTY_LOCKED_BALANCE_FACTORY()
        );
    }

    /**
     * @dev Increases the locked FOREX amount for an account by depositing more.
     */
    function depositFor(address account, uint256 value) external {
        LockedBalance storage _locked = locked[account];
        assert(value > 0);
        assert(_locked.amount > 0);
        assert(_locked.end > block.timestamp);
        _depositFor(account, value, 0, locked[account], DepositType.DepositFor);
    }

    /**
     * @dev Opens a new locked FOREX position for the message sender.
     */
    function createLock(uint256 value, uint256 unlockTime)
        external
        onlyAllowedLocker
    {
        // Round unlockTime to weeks.
        unlockTime = (unlockTime / WEEK) * WEEK;
        LockedBalance memory _locked = locked[msg.sender];
        assert(value > 0);
        require(_locked.amount == 0, "Withdraw old tokens first");
        require(unlockTime > block.timestamp, "Must unlock in the future");
        require(
            unlockTime <= block.timestamp + uint256(uint128(MAX_TIME)),
            "Lock must not be > 4 years"
        );
        _depositFor(
            msg.sender,
            value,
            unlockTime,
            _locked,
            DepositType.CreateLock
        );
    }

    /**
     * @dev Increases FOREX lock amount.
     */
    function increaseAmount(uint256 value) external onlyAllowedLocker {
        LockedBalance storage _locked = locked[msg.sender];
        assert(value > 0);
        require(_locked.amount > 0, "No existing lock");
        require(_locked.end > block.timestamp, "Lock has expired");
        _depositFor(
            msg.sender,
            value,
            0,
            _locked,
            DepositType.IncreaseLockAmount
        );
    }

    /**
     * @dev Increases FOREX lock time.
     */
    function increaseUnlockTime(uint256 unlockTime) external onlyAllowedLocker {
        LockedBalance storage _locked = locked[msg.sender];
        // Round unlockTime to weeks.
        unlockTime = (unlockTime / WEEK) * WEEK;
        require(_locked.end > block.timestamp, "Lock has expired");
        require(_locked.amount > 0, "Nothing is locked");
        require(unlockTime > _locked.end, "Can only increase lock duration");
        require(
            unlockTime <= block.timestamp + uint256(uint128(MAX_TIME)),
            "Lock must not be > 4 years"
        );
        _depositFor(
            msg.sender,
            0,
            unlockTime,
            _locked,
            DepositType.IncreaseUnlockTime
        );
    }

    /**
     * @dev Withdraws fully unlocked FOREX from contract as well as rewards.
     */
    function withdraw() external {
        LockedBalance storage _locked = locked[msg.sender];
        require(
            retiredContract || block.timestamp >= _locked.end,
            "The lock didn't expire"
        );
        require(_locked.amount > 0, "Nothing to withdraw");
        uint256 value = uint256(uint128(_locked.amount));
        LockedBalance memory oldLocked =
            LockedBalance({amount: _locked.amount, end: _locked.end});
        _locked.end = 0;
        _locked.amount = 0;
        uint256 supplyBefore = supply;
        supply = supplyBefore - value;
        if (!retiredContract) _checkpoint(msg.sender, oldLocked, _locked);
        IERC20(token).safeTransfer(msg.sender, value);
        // Unstake from reward pool.
        setUserRewardStakeAmount(msg.sender, 0);
        emit Withdraw(msg.sender, value, block.timestamp);
        emit Supply(supplyBefore, supplyBefore - value);
    }

    /**
     * @dev Finds epoch from block number and max epoch search range.
     */
    function findBlockEpoch(uint256 blockNumber, uint256 maxEpoch)
        private
        view
        returns (uint256 minEpoch)
    {
        minEpoch = 0;
        // Binary search for 128 bit value
        for (uint256 i = 0; i < 128; i++) {
            if (minEpoch >= maxEpoch) break;
            uint256 midEpoch = (minEpoch + maxEpoch + 1) / 2;
            if (pointHistory[midEpoch].blk <= blockNumber) {
                minEpoch = midEpoch;
            } else {
                maxEpoch = midEpoch - 1;
            }
        }
    }

    /**
     * @dev Returns an account's gFOREX balance.
     */
    function balanceOf(address account) public view returns (uint256) {
        uint256 epoch = userPointEpoch[account];
        if (epoch == 0) return 0;
        Point memory lastPoint = userPointHistory[account][epoch];
        lastPoint.bias -=
            lastPoint.slope *
            int128(int256(block.timestamp - lastPoint.ts));
        if (lastPoint.bias < 0) lastPoint.bias = 0;
        return uint256(uint128(lastPoint.bias));
    }

    /**
     * @dev Returns the gFOREX supply at time t.
     */
    function supplyAt(Point memory point, uint256 t)
        private
        view
        returns (uint256)
    {
        uint256 t_i = (point.ts / WEEK) * WEEK;
        for (uint256 i = 0; i < 255; i++) {
            t_i += WEEK;
            int128 dSlope;
            if (t_i > t) {
                t_i = t;
            } else {
                dSlope = slopeChanges[t_i];
            }
            point.bias -= point.slope * int128(int256(t_i) - int256(point.ts));
            if (t_i == t) break;
            point.slope += dSlope;
            point.ts = t_i;
        }
        if (point.bias < 0) point.bias = 0;
        return uint256(uint128(point.bias));
    }

    /**
     * @dev Returns the total gFOREX supply.
     */
    function totalSupply() external view returns (uint256) {
        uint256 _epoch = epoch;
        Point memory lastPoint = pointHistory[epoch];
        return supplyAt(lastPoint, block.timestamp);
    }

    /**
     * @dev Returns the total gFOREX supply at a block.
     * @param blockNumber The block to calculate the supply at.
     */
    function totalSupplyAt(uint256 blockNumber)
        external
        view
        returns (uint256)
    {
        require(blockNumber <= block.number);
        uint256 _epoch = epoch;
        uint256 targetEpoch = findBlockEpoch(blockNumber, _epoch);

        Point memory point = pointHistory[targetEpoch];
        uint256 dt = 0;

        if (targetEpoch < _epoch) {
            Point memory pointNext = pointHistory[targetEpoch + 1];
            if (point.blk != pointNext.blk) {
                dt =
                    ((blockNumber - point.blk) * (pointNext.ts - point.ts)) /
                    (pointNext.blk - point.blk);
            }
        } else if (point.blk != block.number) {
            dt =
                ((blockNumber - point.blk) * (block.timestamp - point.ts)) /
                (block.number - point.blk);
        }

        // Now, dt contains info on how far the current block is beyond "point".
        return supplyAt(point, point.ts + dt);
    }

    /**
     * @dev Measure balance of `account` at block height `blockNumber`
     * @param account Account to check balance from
     * @param blockNumber Block to calculate the balance at
     */
    function balanceOfAt(address account, uint256 blockNumber)
        external
        view
        returns (uint256)
    {
        // Copying and pasting totalSupply code because Vyper cannot pass by
        // reference yet
        require(blockNumber <= block.number);

        // Binary search
        uint256 _min = 0;
        uint256 _max = userPointEpoch[account];

        // Will be always enough for 128-bit numbers
        for (uint256 i = 0; i < 128; i++) {
            if (_min >= _max) {
                break;
            }
            uint256 _mid = (_min + _max + 1) / 2;
            if (userPointHistory[account][_mid].blk <= blockNumber) {
                _min = _mid;
            } else {
                _max = _mid - 1;
            }
        }

        Point memory upoint = userPointHistory[account][_min];
        uint256 max_epoch = epoch;
        uint256 _epoch = findBlockEpoch(blockNumber, max_epoch);
        Point memory point_0 = pointHistory[_epoch];
        uint256 d_block = 0;
        uint256 d_t = 0;

        if (_epoch < max_epoch) {
            Point memory point_1 = pointHistory[_epoch + 1];
            d_block = point_1.blk - point_0.blk;
            d_t = point_1.ts - point_0.ts;
        } else {
            d_block = block.number - point_0.blk;
            d_t = block.timestamp - point_0.ts;
        }

        uint256 block_time = point_0.ts;
        if (d_block != 0) {
            block_time += (d_t * (blockNumber - point_0.blk)) / d_block;
        }
        upoint.bias -=
            upoint.slope *
            (int128(uint128(block_time)) - int128(uint128(upoint.ts)));

        return upoint.bias >= 0 ? uint256(int256(upoint.bias)) : 0;
    }

    /**
     * @dev Returns whether addr is a contract (except for constructor).
     */
    function isContract(address addr) private returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(addr)
        }
        return size > 0;
    }

    function EMPTY_POINT_FACTORY() private view returns (Point memory) {
        return Point({bias: 0, slope: 0, ts: 0, blk: 0});
    }

    function EMPTY_LOCKED_BALANCE_FACTORY()
        private
        view
        returns (LockedBalance memory)
    {
        return LockedBalance({amount: 0, end: 0});
    }

    /** @dev Protected UUPS upgrade authorization fuction */
    function _authorizeUpgrade(address) internal override onlyOwner {}
}

File 11 of 23 : 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 12 of 23 : ERC1967UpgradeUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Upgrade.sol)

pragma solidity ^0.8.2;

import "../beacon/IBeaconUpgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/StorageSlotUpgradeable.sol";
import "../utils/Initializable.sol";

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 *
 * _Available since v4.1._
 *
 * @custom:oz-upgrades-unsafe-allow delegatecall
 */
abstract contract ERC1967UpgradeUpgradeable is Initializable {
    function __ERC1967Upgrade_init() internal onlyInitializing {
        __ERC1967Upgrade_init_unchained();
    }

    function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
    }
    // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
    bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Returns the current implementation address.
     */
    function _getImplementation() internal view returns (address) {
        return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
        StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Perform implementation upgrade
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Perform implementation upgrade with additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCall(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        _upgradeTo(newImplementation);
        if (data.length > 0 || forceCall) {
            _functionDelegateCall(newImplementation, data);
        }
    }

    /**
     * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCallSecure(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        address oldImplementation = _getImplementation();

        // Initial upgrade and setup call
        _setImplementation(newImplementation);
        if (data.length > 0 || forceCall) {
            _functionDelegateCall(newImplementation, data);
        }

        // Perform rollback test if not already in progress
        StorageSlotUpgradeable.BooleanSlot storage rollbackTesting = StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT);
        if (!rollbackTesting.value) {
            // Trigger rollback using upgradeTo from the new implementation
            rollbackTesting.value = true;
            _functionDelegateCall(
                newImplementation,
                abi.encodeWithSignature("upgradeTo(address)", oldImplementation)
            );
            rollbackTesting.value = false;
            // Check rollback was effective
            require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
            // Finally reset to the new implementation and log the upgrade
            _upgradeTo(newImplementation);
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Returns the current admin.
     */
    function _getAdmin() internal view returns (address) {
        return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
        StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     */
    function _changeAdmin(address newAdmin) internal {
        emit AdminChanged(_getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
     */
    bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Emitted when the beacon is upgraded.
     */
    event BeaconUpgraded(address indexed beacon);

    /**
     * @dev Returns the current beacon.
     */
    function _getBeacon() internal view returns (address) {
        return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the EIP1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
        require(
            AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
            "ERC1967: beacon implementation is not a contract"
        );
        StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
    }

    /**
     * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
     * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
     *
     * Emits a {BeaconUpgraded} event.
     */
    function _upgradeBeaconToAndCall(
        address newBeacon,
        bytes memory data,
        bool forceCall
    ) internal {
        _setBeacon(newBeacon);
        emit BeaconUpgraded(newBeacon);
        if (data.length > 0 || forceCall) {
            _functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
        }
    }

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

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
    }
    uint256[50] private __gap;
}

File 13 of 23 : IBeaconUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.0;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeaconUpgradeable {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {BeaconProxy} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}

File 14 of 23 : StorageSlotUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
 */
library StorageSlotUpgradeable {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        assembly {
            r.slot := slot
        }
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @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 17 of 23 : 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 18 of 23 : 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 19 of 23 : 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 20 of 23 : 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 21 of 23 : 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 22 of 23 : 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 23 of 23 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

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

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Context_init_unchained();
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

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

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

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

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
    uint256[49] private __gap;
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"acount","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"poolIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"enum IRewardPool.AssetType","name":"assetType","type":"uint8"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"weight","type":"uint256"}],"name":"CreatePool","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalAmount","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"poolIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"ForexDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"poolId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"aliasHash","type":"bytes32"}],"name":"PoolAliasChanged","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":false,"internalType":"uint256","name":"ratePerSecond","type":"uint256"}],"name":"SetForexDistributionRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"fxTokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"weights","type":"uint256[]"}],"name":"SetFxTokenWeights","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"poolIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"weights","type":"uint256[]"}],"name":"SetPoolWeights","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"poolId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Stake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"poolId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unstake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"poolId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"whitelisted","type":"bool"}],"name":"WhitelistChanged","type":"event"},{"inputs":[],"name":"ADMIN_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":"OPERATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"enum IRewardPool.AssetType","name":"assetType","type":"uint8"},{"internalType":"address","name":"assetAddress","type":"address"},{"internalType":"uint256[]","name":"poolIds","type":"uint256[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"}],"name":"createPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distribute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"enabledPools","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"enabledPoolsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"forex","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"forexDistributionRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getDeposit","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"S","type":"uint256"},{"internalType":"uint256","name":"boostWeight","type":"uint256"}],"internalType":"struct IRewardPool.Deposit","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"category","type":"uint256"}],"name":"getFxTokenPoolAlias","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getPool","outputs":[{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"enum IRewardPool.AssetType","name":"assetType","type":"uint8"},{"internalType":"address","name":"assetAddress","type":"address"},{"internalType":"uint256","name":"totalDeposits","type":"uint256"},{"internalType":"uint256","name":"S","type":"uint256"},{"internalType":"uint256","name":"totalRealDeposits","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"getPoolIdByAlias","outputs":[{"internalType":"bool","name":"found","type":"bool"},{"internalType":"uint256","name":"poolId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPoolsData","outputs":[{"internalType":"uint256[]","name":"poolRatios","type":"uint256[]"},{"internalType":"uint256[]","name":"accruedAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"deltaS","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":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"totalRealDeposits","type":"uint256"},{"internalType":"uint256","name":"boostWeight","type":"uint256"}],"name":"getUserBoostedStake","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":[],"name":"handleAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastDistributionDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"rate","type":"uint256"}],"name":"setForexDistributionRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_governanceLock","type":"address"}],"name":"setGovernanceLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_handle","type":"address"}],"name":"setHandleContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"setPoolAlias","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"poolIds","type":"uint256[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"}],"name":"setPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"poolId","type":"uint256"},{"internalType":"bool","name":"isWhitelisted","type":"bool"}],"name":"setWhitelistedStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum IRewardPool.AssetType[]","name":"assetTypes","type":"uint8[]"},{"internalType":"address[]","name":"assetAddresses","type":"address[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"},{"internalType":"bytes32[]","name":"aliases","type":"bytes32[]"}],"name":"setupPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"stake","outputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"syncTotalRealDeposits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"unstake","outputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"}]

60a06040523060601b60805234801561001757600080fd5b5060805160601c613a6061004b60003960008181610b4601528181610b8601528181610c710152610cb10152613a606000f3fe6080604052600436106102305760003560e01c80638a4962cd1161012e578063d547741f116100ab578063e8ee33b51161006f578063e8ee33b51461072a578063e9b86a911461074a578063f525cb681461076e578063f5b541a614610785578063ffcd9da6146107a757610230565b8063d547741f1461067e578063db86b7991461069e578063df483d8d146106d5578063e4fc6b6d146106f5578063e61ab44d1461070a57610230565b8063a217fddf116100f2578063a217fddf14610606578063a2bc66be1461061b578063a6e81e6c1461063b578063a709e74814610650578063b5ec189a1461066757610230565b80638a4962cd1461056657806391d14854146105865780639b1c414f146105a65780639c301c99146105c65780639e9d1d2f146105e657610230565b80633b17c517116101bc57806370a082311161018057806370a08231146104bc57806374bf578b146104dc57806375b238fc1461050f5780638129fc1c14610531578063889deaad1461054657610230565b80633b17c517146103fc5780634c46358f146104125780634e71d92d146104325780634f1ef286146104475780636de6d5ec1461045a57610230565b8063248a9ca311610203578063248a9ca31461032a5780632726b5061461035a5780632f2ff15d1461039c57806336568abe146103bc5780633659cfe6146103dc57610230565b806301ffc9a714610235578063068bcd8d1461026a5780630c51b88f146102da5780631710de0714610308575b600080fd5b34801561024157600080fd5b506102556102503660046133ce565b6107c8565b60405190151581526020015b60405180910390f35b34801561027657600080fd5b506102c8610285366004613366565b600090815261012f602052604090208054600182015460048301546005840154600690940154929460ff8316946101009093046001600160a01b03169391929190565b604051610261969594939291906137cb565b3480156102e657600080fd5b506102fa6102f5366004613173565b610801565b604051908152602001610261565b34801561031457600080fd5b50610328610323366004613132565b610951565b005b34801561033657600080fd5b506102fa610345366004613366565b600090815260c9602052604090206001015490565b34801561036657600080fd5b5061037a610375366004613107565b610a1c565b6040805182518152602080840151908201529181015190820152606001610261565b3480156103a857600080fd5b506103286103b736600461337e565b610a91565b3480156103c857600080fd5b506103286103d736600461337e565b610abd565b3480156103e857600080fd5b506103286103f736600461302d565b610b3b565b34801561040857600080fd5b50610131546102fa565b34801561041e57600080fd5b5061032861042d36600461302d565b610c04565b34801561043e57600080fd5b50610328610c5b565b610328610455366004613065565b610c66565b34801561046657600080fd5b506102fa610475366004613107565b6040516bffffffffffffffffffffffff19606084901b1660208201526034810182905260009060540160405160208183030381529060405280519060200120905092915050565b3480156104c857600080fd5b506102fa6104d736600461302d565b610d1c565b3480156104e857600080fd5b5061012d546001600160a01b03165b6040516001600160a01b039091168152602001610261565b34801561051b57600080fd5b506102fa600080516020613a0b83398151915281565b34801561053d57600080fd5b50610328610e6e565b34801561055257600080fd5b506102fa610561366004613366565b610f8c565b34801561057257600080fd5b506103286105813660046132e9565b610fae565b34801561059257600080fd5b506102556105a136600461337e565b61100a565b3480156105b257600080fd5b506103286105c136600461302d565b611035565b3480156105d257600080fd5b506103286105e13660046131e1565b61111d565b3480156105f257600080fd5b50610328610601366004613366565b611481565b34801561061257600080fd5b506102fa600081565b34801561062757600080fd5b506102fa610636366004613173565b611551565b34801561064757600080fd5b50610328611651565b34801561065c57600080fd5b506102fa6101335481565b34801561067357600080fd5b506102fa6101325481565b34801561068a57600080fd5b5061032861069936600461337e565b6116d0565b3480156106aa57600080fd5b506106be6106b9366004613366565b6116f6565b604080519215158352602083019190915201610261565b3480156106e157600080fd5b506103286106f036600461340e565b611731565b34801561070157600080fd5b50610328611861565b34801561071657600080fd5b506103286107253660046133ad565b611a33565b34801561073657600080fd5b506102fa6107453660046131a7565b611ae1565b34801561075657600080fd5b5061075f611c0a565b604051610261939291906135e0565b34801561077a57600080fd5b506102fa6101345481565b34801561079157600080fd5b506102fa6000805160206139c483398151915281565b3480156107b357600080fd5b5061012e546104f7906001600160a01b031681565b60006001600160e01b03198216637965db0b60e01b14806107f957506301ffc9a760e01b6001600160e01b03198316145b90505b919050565b600081815261012f6020526040812061081981611f2f565b6108595760405162461bcd60e51b815260206004820152600c60248201526b155b985d5d1a1bdc9a5e995960a21b60448201526064015b60405180910390fd5b6000600182015460ff16600281111561088257634e487b7160e01b600052602160045260246000fd5b146108c35760405162461bcd60e51b8152602060048201526011602482015270139bdd081e595d081cdd5c1c1bdc9d1959607a1b6044820152606401610850565b6108cc85611f88565b6001600160a01b0385166000908152600382016020526040902080546108ff9087906108f99088906138a4565b86612223565b60408051858152602081018790526001600160a01b038816917f5af417134f72a9d41143ace85b0a26dce6f550f894f2cbc1eeee8810603d91b6910160405180910390a26000925050505b9392505050565b6109696000805160206139c48339815191523361100a565b806109875750610987600080516020613a0b8339815191523361100a565b6109a35760405162461bcd60e51b8152600401610850906136a2565b600082815261012f602090815260408083206001600160a01b0387168085526002909101835292819020805460ff191685151590811790915581519384529183018590528201527fa839f18792cf90163c4f23341b425ae8ed1de428169e4006c33f71a023ee6c299060600160405180910390a1505050565b610a4060405180606001604052806000815260200160008152602001600081525090565b50600081815261012f602090815260408083206001600160a01b0386168452600301825291829020825160608101845281548152600182015492810192909252600201549181019190915292915050565b600082815260c96020526040902060010154610aae81335b612427565b610ab8838361248b565b505050565b6001600160a01b0381163314610b2d5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610850565b610b378282612511565b5050565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161415610b845760405162461bcd60e51b815260040161085090613656565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610bb6612578565b6001600160a01b031614610bdc5760405162461bcd60e51b8152600401610850906136be565b610be5816125a6565b60408051600080825260208201909252610c01918391906125da565b50565b610c1c600080516020613a0b8339815191523361100a565b610c385760405162461bcd60e51b81526004016108509061370a565b61013580546001600160a01b0319166001600160a01b0392909216919091179055565b610c6433611f88565b565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161415610caf5760405162461bcd60e51b815260040161085090613656565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610ce1612578565b6001600160a01b031614610d075760405162461bcd60e51b8152600401610850906136be565b610d10826125a6565b610b37828260016125da565b600080610d27611c0a565b610131549093509150600090505b81811015610e665760006101318281548110610d6157634e487b7160e01b600052603260045260246000fd5b600091825260208083209091015480835261012f825260408084206001600160a01b038b168552600381019093528320600181015488519295509293909291889087908110610dc057634e487b7160e01b600052603260045260246000fd5b60200260200101518460050154610dd791906138a4565b9050600083600001549050610df68b8287600601548760020154611ae1565b9050801580610e055750818310155b15610e1557505050505050610e54565b6000610e2184846138fb565b9050670de0b6b3a7640000610e3682846138dc565b610e4091906138bc565b610e4a908c6138a4565b9a50505050505050505b80610e5e81613959565b915050610d35565b505050919050565b600054610100900460ff16610e895760005460ff1615610e8d565b303b155b610ef05760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610850565b600054610100900460ff16158015610f12576000805461ffff19166101011790555b610f1a612725565b610f22612764565b610f3a600080516020613a0b83398151915233612793565b610f606000805160206139c4833981519152600080516020613a0b83398151915261279d565b610f78600080516020613a0b8339815191528061279d565b8015610c01576000805461ff001916905550565b6101318181548110610f9d57600080fd5b600091825260209091200154905081565b610fc66000805160206139c48339815191523361100a565b80610fe45750610fe4600080516020613a0b8339815191523361100a565b6110005760405162461bcd60e51b8152600401610850906136a2565b610b3782826127e8565b600091825260c9602090815260408084206001600160a01b0393909316845291905290205460ff1690565b61104d600080516020613a0b8339815191523361100a565b6110695760405162461bcd60e51b81526004016108509061370a565b61012d80546001600160a01b0319166001600160a01b0383169081179091556040805160016219312d60e11b03198152905163ffcd9da691600480820192602092909190829003018186803b1580156110c157600080fd5b505afa1580156110d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f99190613049565b61012e80546001600160a01b0319166001600160a01b039290921691909117905550565b6111356000805160206139c48339815191523361100a565b806111535750611153600080516020613a0b8339815191523361100a565b61116f5760405162461bcd60e51b8152600401610850906136a2565b61013454156111c05760405162461bcd60e51b815260206004820152601960248201527f506f6f6c7320616c726561647920696e697469616c69736564000000000000006044820152606401610850565b83518351811480156111d25750825181145b6111ec57634e487b7160e01b600052600160045260246000fd5b60008167ffffffffffffffff81111561121557634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561123e578160200160208202803683370190505b50905060005b8281101561146057600081815261012f60205260409020875188908390811061127d57634e487b7160e01b600052603260045260246000fd5b60200260200101518160010160006101000a81548160ff021916908360028111156112b857634e487b7160e01b600052602160045260246000fd5b02179055508682815181106112dd57634e487b7160e01b600052603260045260246000fd5b60200260200101518160010160016101000a8154816001600160a01b0302191690836001600160a01b0316021790555085828151811061132d57634e487b7160e01b600052603260045260246000fd5b602002602001015181600001819055508183838151811061135e57634e487b7160e01b600052603260045260246000fd5b60200260200101818152505061139b85838151811061138d57634e487b7160e01b600052603260045260246000fd5b602002602001015183611a33565b7ff37d8e8567f4d54f174bbf67f5f838918fdd376d4777566b7337eeb88a1ac93c828984815181106113dd57634e487b7160e01b600052603260045260246000fd5b602002602001015189858151811061140557634e487b7160e01b600052603260045260246000fd5b602002602001015189868151811061142d57634e487b7160e01b600052603260045260246000fd5b6020026020010151604051611445949392919061379c565b60405180910390a1508061145881613959565b915050611244565b50805161147590610131906020840190612ee9565b50506101345550505050565b6114996000805160206139c48339815191523361100a565b806114b757506114b7600080516020613a0b8339815191523361100a565b6114d35760405162461bcd60e51b8152600401610850906136a2565b610132548114156114e357610c01565b426101335414611515576114f5611861565b42610133541461151557634e487b7160e01b600052600160045260246000fd5b6101328190556040518181527fc5763479bd9bf9105881647ea9226f99c26cb53d6df3a140b6a1f3d1128841799060200160405180910390a150565b600081815261012f6020526040812061156981611f2f565b6115a45760405162461bcd60e51b815260206004820152600c60248201526b155b985d5d1a1bdc9a5e995960a21b6044820152606401610850565b6001600160a01b0385166000908152600382016020526040902080546115cf5760019250505061094a565b6115d886611f88565b8054808611156115e6578095505b60006115f287836138fb565b90506115ff888288612223565b60408051878152602081018990526001600160a01b038a16917ff960dbf9e5d0682f7a298ed974e33a28b4464914b7a2bfac12ae419a9afeb280910160405180910390a2506000979650505050505050565b611669600080516020613a0b8339815191523361100a565b6116855760405162461bcd60e51b81526004016108509061370a565b6101345460005b81811015610b3757600081815261012f6020526040902060048101546116b257506116be565b60048101546006909101555b806116c881613959565b91505061168c565b600082815260c960205260409020600101546116ec8133610aa9565b610ab88383612511565b6000818152610130602052604081205481908061171a57600080925092505061172c565b600161172681836138fb565b92509250505b915091565b6117496000805160206139c48339815191523361100a565b806117675750611767600080516020613a0b8339815191523361100a565b6117835760405162461bcd60e51b8152600401610850906136a2565b61178d82826127e8565b61013480546000918261179f83613959565b90915550600081815261012f6020526040902087815560018082018054939450919288929160ff19909116908360028111156117eb57634e487b7160e01b600052602160045260246000fd5b0217905550600181018054610100600160a81b0319166101006001600160a01b0388160217905561181b82612957565b7ff37d8e8567f4d54f174bbf67f5f838918fdd376d4777566b7337eeb88a1ac93c8287878a604051611850949392919061379c565b60405180910390a150505050505050565b6101335442141561187157610c64565b60008061187c611c0a565b92509250506000610133544261189291906138fb565b426101335583519091506118a857505050610c64565b81518351146118c757634e487b7160e01b600052600160045260246000fd5b61013154600090815b818110156119e757600061012f6000610131848154811061190157634e487b7160e01b600052603260045260246000fd5b90600052602060002001548152602001908152602001600020905086828151811061193c57634e487b7160e01b600052603260045260246000fd5b60200260200101516000148061195157508054155b1561195c57506119d5565b85828151811061197c57634e487b7160e01b600052603260045260246000fd5b602002602001015181600501600082825461199791906138a4565b925050819055508682815181106119be57634e487b7160e01b600052603260045260246000fd5b6020026020010151846119d191906138a4565b9350505b806119df81613959565b9150506118d0565b507f19ce097e53a1fc72f05d4aeb15d0a311003530e7071c94493a22b71e07352e0883610132548461013189604051611a2495949392919061380c565b60405180910390a15050505050565b611a4b6000805160206139c48339815191523361100a565b80611a695750611a69600080516020613a0b8339815191523361100a565b611a855760405162461bcd60e51b8152600401610850906136a2565b611a908160016138a4565b600083815261013060209081526040918290209290925580518381529182018490527f0bd70adcb4a4ff93bb3cfd3a095c56f26903685009797109f8bab78ff9d57f45910160405180910390a15050565b610135546000906001600160a01b03161580611afb575083155b15611b07575082611c02565b61013554604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b158015611b4d57600080fd5b505afa158015611b61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b8591906133f6565b905080611b955784915050611c02565b6000611ba282600a6138dc565b611bac85876138dc565b611bb790600f6138dc565b611bc191906138bc565b90506000611bcf82886138a4565b90506000600a611be08960196138dc565b611bea91906138bc565b9050808211611bf95781611bfb565b805b9450505050505b949350505050565b61013154606090819081908067ffffffffffffffff811115611c3c57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611c65578160200160208202803683370190505b5093508067ffffffffffffffff811115611c8f57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611cb8578160200160208202803683370190505b5092508067ffffffffffffffff811115611ce257634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611d0b578160200160208202803683370190505b50915080611d195750611f2a565b61013354421415611d2a5750611f2a565b61013254611d385750611f2a565b6000611d42612a30565b610132546101335491925090600090611d5b90426138fb565b90506000611d6983836138dc565b905060005b85811015611f2357600061012f60006101318481548110611d9f57634e487b7160e01b600052603260045260246000fd5b906000526020600020015481526020019081526020016000209050806000015460001415611dcd5750611f11565b80548690611de390670de0b6b3a76400006138dc565b611ded91906138bc565b8a8381518110611e0d57634e487b7160e01b600052603260045260246000fd5b60209081029190910101526004810154611e275750611f11565b670de0b6b3a76400008a8381518110611e5057634e487b7160e01b600052603260045260246000fd5b602002602001015184611e6391906138dc565b611e6d91906138bc565b898381518110611e8d57634e487b7160e01b600052603260045260246000fd5b6020026020010181815250508060040154898381518110611ebe57634e487b7160e01b600052603260045260246000fd5b6020026020010151670de0b6b3a7640000611ed991906138dc565b611ee391906138bc565b888381518110611f0357634e487b7160e01b600052603260045260246000fd5b602002602001018181525050505b80611f1b81613959565b915050611d6e565b5050505050505b909192565b600080600183015460ff166002811115611f5957634e487b7160e01b600052602160045260246000fd5b14158015611f675750815415155b806107f9575050336000908152600291909101602052604090205460ff1690565b611f90611861565b426101335414611fb057634e487b7160e01b600052600160045260246000fd5b6101315460008167ffffffffffffffff811115611fdd57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612006578160200160208202803683370190505b5090506000805b83811015612140576000610131828154811061203957634e487b7160e01b600052603260045260246000fd5b600091825260208083209091015480835261012f825260408084206001600160a01b038b168552600381019093529092206001810154600583015460028301548354959650939492939192909190612093908c9088612223565b8183106120a55750505050505061212e565b60006120bb8c8660000154886006015485611ae1565b905060006120c985856138fb565b90506000670de0b6b3a76400006120e083856138dc565b6120ea91906138bc565b9050808c8b8151811061210d57634e487b7160e01b600052603260045260246000fd5b6020908102919091010152612122818c6138a4565b9a505050505050505050505b8061213881613959565b91505061200d565b508061214e57505050610c01565b61012e5460405163a9059cbb60e01b81526001600160a01b038681166004830152602482018490529091169063a9059cbb90604401602060405180830381600087803b15801561219d57600080fd5b505af11580156121b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121d5919061334a565b50836001600160a01b03167f274a65b8c8fd25732fc33bc5a16dd4e85d44fedfdf10caa13c268380038ddaf6826101318560405161221593929190613771565b60405180910390a250505050565b600081815261012f602090815260408083206001600160a01b0387168452600381019092528220805460068301546002830154939492936122679289929091611ae1565b610135549091506000906001600160a01b0316612285576000612303565b610135546040516370a0823160e01b81526001600160a01b038981166004830152909116906370a082319060240160206040518083038186803b1580156122cb57600080fd5b505afa1580156122df573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061230391906133f6565b6002840181905590508561236d5781846004015461232191906138fb565b60048501558254600685015461233791906138fb565b60068501555050506001600160a01b03841660009081526003909101602052604081208181556001810182905560020155610ab8565b600584015460018401558254861061239f57825461238b90876138fb565b846006015461239a91906138a4565b6123bb565b82546123ac9087906138fb565b84600601546123bb91906138fb565b600685018190556000906123d3908990899085611ae1565b90508083116123fa576123e683826138fb565b85600401546123f591906138a4565b612413565b61240481846138fb565b856004015461241391906138fb565b600490950194909455505083905550505050565b612431828261100a565b610b3757612449816001600160a01b03166014612aa9565b612454836020612aa9565b604051602001612465929190613546565b60408051601f198184030181529082905262461bcd60e51b825261085091600401613623565b612495828261100a565b610b3757600082815260c9602090815260408083206001600160a01b03851684529091529020805460ff191660011790556124cd3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b61251b828261100a565b15610b3757600082815260c9602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b6125be600080516020613a0b8339815191523361100a565b610c015760405162461bcd60e51b81526004016108509061370a565b60006125e4612578565b90506125ef84612c8b565b6000835111806125fc5750815b1561260d5761260b8484612d30565b505b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143805460ff1661271e57805460ff191660011781556040516001600160a01b038316602482015261268c90869060440160408051601f198184030181529190526020810180516001600160e01b0316631b2ce7f360e11b179052612d30565b50805460ff1916815561269d612578565b6001600160a01b0316826001600160a01b0316146127155760405162461bcd60e51b815260206004820152602f60248201527f45524331393637557067726164653a207570677261646520627265616b73206660448201526e75727468657220757067726164657360881b6064820152608401610850565b61271e85612e1b565b5050505050565b600054610100900460ff1661274c5760405162461bcd60e51b815260040161085090613726565b612754612e5b565b61275c612e5b565b610c64612e5b565b600054610100900460ff1661278b5760405162461bcd60e51b815260040161085090613726565b610c64612e82565b610b37828261248b565b600082815260c96020526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b81518151811461280857634e487b7160e01b600052600160045260246000fd5b6101345460005b8181101561290257600080805b858110156128755787818151811061284457634e487b7160e01b600052603260045260246000fd5b6020026020010151841461285757612863565b60019250809150612875565b8061286d81613959565b91505061281c565b50600061012f600089868151811061289d57634e487b7160e01b600052603260045260246000fd5b60200260200101518152602001908152602001600020905082156128e8578682815181106128db57634e487b7160e01b600052603260045260246000fd5b60200260200101516128eb565b60005b9055508190506128fa81613959565b91505061280f565b50835161291790610131906020870190612ee9565b507fcabae56731cea70615176c847ec16408fd97d7eb3cc387917aea0d7de26f459784846040516129499291906135bb565b60405180910390a150505050565b6101315460005b818110156129f85782610131828154811061298957634e487b7160e01b600052603260045260246000fd5b90600052602060002001541461299e576129e6565b60405162461bcd60e51b815260206004820152601960248201527f50757368696e67206475706c696361746520706f6f6c204944000000000000006044820152606401610850565b806129f081613959565b91505061295e565b505061013180546001810182556000919091527fbd987ad6ccdb7c7567f7335ea839f95dc944431abcf935b6924f70215963db730155565b61013154600090815b81811015612aa45761012f60006101318381548110612a6857634e487b7160e01b600052603260045260246000fd5b906000526020600020015481526020019081526020016000206000015483612a9091906138a4565b925080612a9c81613959565b915050612a39565b505090565b60606000612ab88360026138dc565b612ac39060026138a4565b67ffffffffffffffff811115612ae957634e487b7160e01b600052604160045260246000fd5b6040519080825280601f01601f191660200182016040528015612b13576020820181803683370190505b509050600360fc1b81600081518110612b3c57634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110612b7957634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a9053506000612b9d8460026138dc565b612ba89060016138a4565b90505b6001811115612c3c576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110612bea57634e487b7160e01b600052603260045260246000fd5b1a60f81b828281518110612c0e57634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a90535060049490941c93612c3581613942565b9050612bab565b50831561094a5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610850565b803b612cef5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610850565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b6060823b612d8f5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610850565b600080846001600160a01b031684604051612daa919061352a565b600060405180830381855af49150503d8060008114612de5576040519150601f19603f3d011682016040523d82523d6000602084013e612dea565b606091505b5091509150612e1282826040518060600160405280602781526020016139e460279139612eb0565b95945050505050565b612e2481612c8b565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b600054610100900460ff16610c645760405162461bcd60e51b815260040161085090613726565b600054610100900460ff16612ea95760405162461bcd60e51b815260040161085090613726565b600160fb55565b60608315612ebf57508161094a565b825115612ecf5782518084602001fd5b8160405162461bcd60e51b81526004016108509190613623565b828054828255906000526020600020908101928215612f24579160200282015b82811115612f24578251825591602001919060010190612f09565b50612f30929150612f34565b5090565b5b80821115612f305760008155600101612f35565b600082601f830112612f59578081fd5b81356020612f6e612f6983613880565b61384f565b80838252828201915082860187848660051b8901011115612f8d578586fd5b855b85811015612fb4578135612fa2816139a0565b84529284019290840190600101612f8f565b5090979650505050505050565b600082601f830112612fd1578081fd5b81356020612fe1612f6983613880565b80838252828201915082860187848660051b8901011115613000578586fd5b855b85811015612fb457813584529284019290840190600101613002565b8035600381106107fc57600080fd5b60006020828403121561303e578081fd5b813561094a816139a0565b60006020828403121561305a578081fd5b815161094a816139a0565b60008060408385031215613077578081fd5b8235613082816139a0565b915060208381013567ffffffffffffffff8082111561309f578384fd5b818601915086601f8301126130b2578384fd5b8135818111156130c4576130c461398a565b6130d6601f8201601f1916850161384f565b915080825287848285010111156130eb578485fd5b8084840185840137810190920192909252919491935090915050565b60008060408385031215613119578182fd5b8235613124816139a0565b946020939093013593505050565b600080600060608486031215613146578081fd5b8335613151816139a0565b9250602084013591506040840135613168816139b5565b809150509250925092565b600080600060608486031215613187578081fd5b8335613192816139a0565b95602085013595506040909401359392505050565b600080600080608085870312156131bc578182fd5b84356131c7816139a0565b966020860135965060408601359560600135945092505050565b600080600080608085870312156131f6578182fd5b843567ffffffffffffffff8082111561320d578384fd5b818701915087601f830112613220578384fd5b81356020613230612f6983613880565b8083825282820191508286018c848660051b890101111561324f578889fd5b8896505b84871015613278576132648161301e565b835260019690960195918301918301613253565b509850508801359250508082111561328e578384fd5b61329a88838901612f49565b945060408701359150808211156132af578384fd5b6132bb88838901612fc1565b935060608701359150808211156132d0578283fd5b506132dd87828801612fc1565b91505092959194509250565b600080604083850312156132fb578182fd5b823567ffffffffffffffff80821115613312578384fd5b61331e86838701612fc1565b93506020850135915080821115613333578283fd5b5061334085828601612fc1565b9150509250929050565b60006020828403121561335b578081fd5b815161094a816139b5565b600060208284031215613377578081fd5b5035919050565b60008060408385031215613390578182fd5b8235915060208301356133a2816139a0565b809150509250929050565b600080604083850312156133bf578182fd5b50508035926020909101359150565b6000602082840312156133df578081fd5b81356001600160e01b03198116811461094a578182fd5b600060208284031215613407578081fd5b5051919050565b600080600080600060a08688031215613425578283fd5b853594506134356020870161301e565b93506040860135613445816139a0565b9250606086013567ffffffffffffffff80821115613461578283fd5b61346d89838a01612fc1565b93506080880135915080821115613482578283fd5b5061348f88828901612fc1565b9150509295509295909350565b6000815180845260208085019450808401835b838110156134cb578151875295820195908201906001016134af565b509495945050505050565b6000815480845260208085019450838352808320835b838110156134cb578154875295820195600191820191016134ec565b6003811061352657634e487b7160e01b600052602160045260246000fd5b9052565b6000825161353c818460208701613912565b9190910192915050565b60007f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008252835161357e816017850160208801613912565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516135af816028840160208801613912565b01602801949350505050565b6000604082526135ce604083018561349c565b8281036020840152612e12818561349c565b6000606082526135f3606083018661349c565b8281036020840152613605818661349c565b90508281036040840152613619818561349c565b9695505050505050565b6000602082528251806020840152613642816040850160208701613912565b601f01601f19169190910160400192915050565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b19195b1959d85d1958d85b1b60a21b606082015260800190565b6020808252600290820152614e5760f01b604082015260600190565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b6163746976652070726f787960a01b606082015260800190565b6020808252600290820152614e4160f01b604082015260600190565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b60008482526060602083015261378a60608301856134d6565b8281036040840152613619818561349c565b848152608081016137b06020830186613508565b6001600160a01b039390931660408201526060015292915050565b86815260c081016137df6020830188613508565b6001600160a01b039590951660408201526060810193909352608083019190915260a09091015292915050565b600086825285602083015284604083015260a0606083015261383160a08301856134d6565b8281036080840152613843818561349c565b98975050505050505050565b604051601f8201601f1916810167ffffffffffffffff811182821017156138785761387861398a565b604052919050565b600067ffffffffffffffff82111561389a5761389a61398a565b5060051b60200190565b600082198211156138b7576138b7613974565b500190565b6000826138d757634e487b7160e01b81526012600452602481fd5b500490565b60008160001904831182151516156138f6576138f6613974565b500290565b60008282101561390d5761390d613974565b500390565b60005b8381101561392d578181015183820152602001613915565b8381111561393c576000848401525b50505050565b60008161395157613951613974565b506000190190565b600060001982141561396d5761396d613974565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114610c0157600080fd5b8015158114610c0157600080fdfe97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b929416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775a2646970667358221220b37bf6cc81867e4e449e94dee086a2b0e8417341a6feef6dbd6483450095954364736f6c63430008030033

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