ETH Price: $2,975.68 (-0.49%)
Gas: 8 Gwei

Contract

0x674EeEF1Da64b9e398210621A61d1d2a8Bf27387
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Deposit200815972024-06-13 8:05:1125 days ago1718265911IN
0x674EeEF1...a8Bf27387
0 ETH0.0032552712.88665776
Withdraw200056412024-06-02 17:31:1135 days ago1717349471IN
0x674EeEF1...a8Bf27387
0 ETH0.0031689520.45288841
Set Allowed Amou...196612212024-04-15 13:31:1184 days ago1713187871IN
0x674EeEF1...a8Bf27387
0 ETH0.0007856824.15719379
Withdraw196056482024-04-07 18:38:3591 days ago1712515115IN
0x674EeEF1...a8Bf27387
0 ETH0.0029734221.57363261
Withdraw194601302024-03-18 7:14:47112 days ago1710746087IN
0x674EeEF1...a8Bf27387
0 ETH0.0033529123.57207804
Withdraw192289212024-02-14 21:37:47144 days ago1707946667IN
0x674EeEF1...a8Bf27387
0 ETH0.0008487325.32870656
Withdraw192289212024-02-14 21:37:47144 days ago1707946667IN
0x674EeEF1...a8Bf27387
0 ETH0.0040355925.32870656
Withdraw190482152024-01-20 12:56:23170 days ago1705755383IN
0x674EeEF1...a8Bf27387
0 ETH0.0022548915.8526269
Withdraw190007052024-01-13 21:38:47176 days ago1705181927IN
0x674EeEF1...a8Bf27387
0 ETH0.0023606816.59635
Restake189292962024-01-03 20:47:23186 days ago1704314843IN
0x674EeEF1...a8Bf27387
0 ETH0.0095913724.26893924
Withdraw189211402024-01-02 17:19:47187 days ago1704215987IN
0x674EeEF1...a8Bf27387
0 ETH0.0049648731.15880755
Withdraw188873422023-12-28 23:21:23192 days ago1703805683IN
0x674EeEF1...a8Bf27387
0 ETH0.0024310817.09129613
Withdraw188483512023-12-23 11:55:11198 days ago1703332511IN
0x674EeEF1...a8Bf27387
0 ETH0.0034862221.88229316
Deposit186297272023-11-22 20:41:23228 days ago1700685683IN
0x674EeEF1...a8Bf27387
0 ETH0.0108485941.78435102
Withdraw186138212023-11-20 15:14:11231 days ago1700493251IN
0x674EeEF1...a8Bf27387
0 ETH0.0050841635.74633633
Withdraw178368782023-08-03 20:33:47339 days ago1691094827IN
0x674EeEF1...a8Bf27387
0 ETH0.0045863932.24930631
Deposit175717802023-06-27 16:09:47377 days ago1687882187IN
0x674EeEF1...a8Bf27387
0 ETH0.016184962.33188023
Set Allowed Amou...175266992023-06-21 7:58:11383 days ago1687334291IN
0x674EeEF1...a8Bf27387
0 ETH0.0004119112.66489851
Deposit174941602023-06-16 18:24:23387 days ago1686939863IN
0x674EeEF1...a8Bf27387
0 ETH0.0077091730.25473343
Deposit174873062023-06-15 19:18:59388 days ago1686856739IN
0x674EeEF1...a8Bf27387
0 ETH0.0053769121.09974314
Deposit174810972023-06-14 22:18:47389 days ago1686781127IN
0x674EeEF1...a8Bf27387
0 ETH0.0053587321.02741548
Deposit174770952023-06-14 8:47:47390 days ago1686732467IN
0x674EeEF1...a8Bf27387
0 ETH0.0046960218.42698932
Deposit174770612023-06-14 8:40:59390 days ago1686732059IN
0x674EeEF1...a8Bf27387
0 ETH0.0050845519.22990108
Deposit174651102023-06-12 16:19:23392 days ago1686586763IN
0x674EeEF1...a8Bf27387
0 ETH0.005583321.11521513
Deposit174496582023-06-10 12:05:23394 days ago1686398723IN
0x674EeEF1...a8Bf27387
0 ETH0.0048179518.90541537
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xE6925d1E...9cBC15232
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
StakingV2

Compiler Version
v0.8.6+commit.11564f7e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 16 : StakingV2.sol
// SPDX-License-Identifier: MIT

pragma solidity =0.8.6;

import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/access/AccessControl.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/Address.sol';
import "./IStakingV2Vendor.sol";
import './IStakingV2Factory.sol';
import './IStakingDelegate.sol';

/**
 * @title Token Staking
 * @dev BEP20 compatible token.
 */
contract StakingV2 is Ownable, AccessControl {
    using SafeERC20 for IERC20;
    using Address for address;

    bytes32 public constant ADMIN_ROLE = keccak256('ADMIN_ROLE');
    bytes32 public constant MAINTAINER_ROLE = keccak256('MAINTAINER_ROLE');

    struct UserInfo {
        uint256 amount;
        uint256 rewardDebt; // backwards compatibility
        uint256 pendingRewards; // backwards compatibility
        uint256 lockedTimestamp;
        uint256 lockupTimestamp;
        uint256 lockupTimerange;
        uint256 virtAmount;
    }

    struct PoolInfo {
        uint256 lastBlock;
        uint256 tokenPerShare;
        uint256 tokenRealStaked;
        uint256 tokenVirtStaked;
        uint256 tokenRewarded;
        uint256 tokenTotalLimit;
        uint256 lockupMaxTimerange;
        uint256 lockupMinTimerange;
    }

    IERC20 public token;

    uint256 public minAmount;
    uint256 public maxAmount;
    uint256 public tokenPerBlock; // backwards compatibility
    uint256 public startBlock;
    uint256 public closeBlock;
    uint256 public maxPid;
    uint256 private constant MAX = ~uint256(0);

    PoolInfo[] public poolInfo;
    mapping(uint256 => mapping(address => UserInfo)) public userInfo;
    mapping(address => address) public vendorInfo;
    address[] public vendors;
    address[] public delistedVendors;
    mapping(address => bool) public allowedStakingInstances;
    uint256[] public multipliers = [
         12742, 13081, 13428, 13785, 14152, 14528, 14914, 15311, 15718, 16136, 16565, 17005,
         17457, 17921, 18398, 18887, 19389, 19904, 20433, 20976, 21534, 22107, 22694, 23298,
         23917, 24553, 25205, 25876, 26563, 27270, 27995, 28732, 29503, 30287, 31092, 31919,
         32767, 33638, 34533, 35451, 36393, 37360, 38354, 39373, 40420, 41494, 42598, 43730,
         44892, 46086, 47311, 48569, 49860, 51185, 52546, 53943, 55377, 56849, 58360, 59912,
         61505, 63140, 64818, 66541, 68310, 70126, 71990, 73904, 75869, 77886, 79956, 82082,
         84264, 86504, 88803, 91164, 93587, 96075, 98629,101251,103943,106706,109543,112455,
        115444,118513,121664,124898,128218,131627,135126,138718,142406,146192,150078,154067
    ];

    IStakingDelegate public delegate;
    IStakingV2Factory public factory;

    event PoolAdded(uint256 minTimer, uint256 maxTimer, uint256 limit);
    event Deposited(address indexed user, uint256 indexed pid, address indexed token, uint256 amount);
    event Withdrawn(address indexed user, uint256 indexed pid, address indexed token, uint256 amount);
    event WithdrawnReward(address indexed user, uint256 indexed pid, address indexed token, uint256 amount);
    event WithdrawnRemain(address indexed user, uint256 indexed pid, address indexed token, uint256 amount);
    event TokenVendorChanged(address indexed token, address indexed vendor);
    event DelegateAddressChanged(address indexed addr);
    event FactoryAddressChanged(address indexed addr);
    event AllowedAmountsChanged(uint256 minAmount, uint256 maxAmount);
    event StakingInstanceChanged();

    event StartBlockChanged(uint256 block);
    event CloseBlockChanged(uint256 block);

    modifier onlyAuthority {
        require(msg.sender == owner() || hasRole(MAINTAINER_ROLE, msg.sender), 'Staking: only authorities can call this method');
        _;
    }

    constructor(IERC20 _token, uint256 _minPoolTimer, uint256 _maxPoolTimer, uint256 _minAmount, uint256 _maxAmount, uint256 _poolLimit) {
        require(address(_token) != address(0), 'Staking: token address needs to be different than zero!');
        token = _token;
        minAmount = _minAmount;
        maxAmount = _maxAmount;
        addPool(_minPoolTimer, _maxPoolTimer, _poolLimit);
        tokenPerBlock = 1e4; // in this interface tokenPerBlock serves purpose as a precision gadget

        _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE);
        _setRoleAdmin(MAINTAINER_ROLE, ADMIN_ROLE);

        _setupRole(ADMIN_ROLE, address(this));
        _setupRole(MAINTAINER_ROLE, owner());
    }

    function addMaintainer(address account) external onlyOwner returns (bool) {
        bytes4 selector = this.grantRole.selector;
        address(this).functionCall(abi.encodeWithSelector(selector, MAINTAINER_ROLE, account));
        return true;
    }

    function delMaintainer(address account) external onlyOwner returns (bool) {
        bytes4 selector = this.revokeRole.selector;
        address(this).functionCall(abi.encodeWithSelector(selector, MAINTAINER_ROLE, account));
        return true;
    }

    function isMaintainer(address account) external view returns (bool) {
        return hasRole(MAINTAINER_ROLE, account);
    }

    // staking instances need to be added to properly chain multiple staking instances
    function addStakingInstances(address[] memory stakingInstances, bool status) public onlyOwner {
        for (uint i=0; i<stakingInstances.length; ++i) {
            allowedStakingInstances[stakingInstances[i]] = status;
        }
        emit StakingInstanceChanged();
    }

    // factory is used to instantiate staking vendors to decrease size of this contract
    function setFactoryAddress(IStakingV2Factory _factory) public onlyOwner {
        require(address(_factory) != address(0), 'Staking: factory address needs to be different than zero!');
        factory = _factory;
        emit FactoryAddressChanged(address(factory));
    }

    // set min/max amount possible
    function setAllowedAmounts(uint256 _minAmount, uint256 _maxAmount) public onlyOwner {
        minAmount = _minAmount;
        maxAmount = _maxAmount;
        emit AllowedAmountsChanged(minAmount, maxAmount);
    }

    // set token reward with infinite time range
    function setTokenPerBlock(IERC20 _token, uint256 _tokenPerBlock) public onlyAuthority {
        require(startBlock != 0, 'Staking: cannot add reward before setting start block');
        require(address(_token) != address(0), 'Staking: token address needs to be different than zero!');

        address addr = vendorInfo[address(_token)];
        // if vendor for asset already exists and is not closed then overwrite its reward schedule instead of invoking new one
        if (addr != address(0)) {
            IStakingV2Vendor vendor = IStakingV2Vendor(addr);
            uint256 _prevCloseBlock = vendor.closeBlock();
            if (_prevCloseBlock == 0 || block.number <= _prevCloseBlock) {
                // we need to update the pool manually in this case because of premature return
                for (uint i=0; i<maxPid; i++) updatePool(i);
                _token.approve(address(vendor), MAX);
                vendor.setTokenPerBlock(_tokenPerBlock, vendor.startBlock(), vendor.closeBlock());
                return;
            }
        }

        setTokenPerBlock(_token, _tokenPerBlock, 0);
    }

    // set token reward for some specific time range
    function setTokenPerBlock(IERC20 _token, uint256 _tokenPerBlock, uint256 _blockRange) public onlyAuthority {
        require(startBlock != 0, 'Staking: cannot add reward before setting start block');
        require(address(_token) != address(0), 'Staking: token address needs to be different than zero!');

        address addr = vendorInfo[address(_token)];
        uint256 _startBlock = block.number > startBlock ? block.number : startBlock;
        uint256 _closeBlock = _blockRange == 0 ? 0 : _startBlock + _blockRange;

        // if vendor for asset already exists overwrite startBlock with the value that vendor initally held instead
        if (addr != address(0)) {
            // start block has to remain same regardless of current timestamp and block range
            _startBlock = IStakingV2Vendor(addr).startBlock();
        }

        setTokenPerBlock(_token, _tokenPerBlock, _startBlock, _closeBlock);
    }

    // set token reward for some specific time range by specifying start and close blocks
    function setTokenPerBlock(IERC20 _token, uint256 _tokenPerBlock, uint256 _startBlock, uint256 _closeBlock) public onlyAuthority {
        require(startBlock != 0, 'Staking: cannot add reward before setting start block');
        require(_startBlock >= startBlock, 'Staking: token start block needs to be different than zero!');
        require(_closeBlock > _startBlock || _closeBlock == 0, 'Staking: token close block needs to be higher than start block!');
        require(address(_token) != address(0), 'Staking: token address needs to be different than zero!');

        for (uint i=0; i<maxPid; i++) {
            updatePool(i); // pool needs to be updated to keep vendor data consistent
        }

        address addr = vendorInfo[address(_token)];
        IStakingV2Vendor vendor;

        // if vendor for asset already exists and is not closed overwrite its reward schedule
        if (addr != address(0)) {
            vendor = IStakingV2Vendor(addr);
            uint256 _prevStartBlock = vendor.startBlock();
            uint256 _prevCloseBlock = vendor.closeBlock();

            // not closed
            if (_prevCloseBlock == 0 || block.number <= _prevCloseBlock) {
                require(_startBlock == _prevStartBlock || block.number < _prevStartBlock,
                    'Staking: token start block cannot be changed');
                _token.approve(address(vendor), MAX);
                vendor.setTokenPerBlock(_tokenPerBlock, _startBlock, _closeBlock);
                return;
            }

            // if it is closed though, then treat it the same as if vendor was not created yet - new one is needed
            if (_prevCloseBlock != 0 && _prevCloseBlock < _startBlock) {
                addr = address(0);
            }
        }

        // if vendor for asset does not exist (or expired) create a new one
        if (addr == address(0)) {
            updateVendors();
            require(vendors.length < 20, 'Staking: limit of actively distributed tokens reached');

            addr = factory.createVendor(address(this), _token);
            vendor = IStakingV2Vendor(addr);
            _token.approve(address(vendor), MAX);
            vendor.setTokenPerBlock(_tokenPerBlock, _startBlock, _closeBlock);

            vendorInfo[address(_token)] = address(vendor);
            vendors.push(address(_token));
            emit TokenVendorChanged(address(_token), address(vendor));
            return;
        }

        revert('Staking: invalid configuration provided');
    }

    function setStartBlock(uint256 _startBlock) public onlyOwner {
        require(startBlock == 0 || startBlock > block.number, 'Staking: start block already set');
        require(_startBlock > 0, 'Staking: start block needs to be higher than zero!');
        startBlock = _startBlock;

        IStakingV2Vendor vendor;
        for (uint i=0; i<vendors.length; i++) {
            vendor = IStakingV2Vendor(vendorInfo[vendors[i]]);
            if (vendor.startBlock() == 0 || vendor.startBlock() < startBlock) vendor.setStartBlock(startBlock);
        }
        emit StartBlockChanged(startBlock);
    }

    function setCloseBlock(uint256 _closeBlock) public onlyOwner {
        require(startBlock != 0, 'Staking: start block needs to be set first');
        require(closeBlock == 0 || closeBlock > block.number, 'Staking: close block already set');
        require(_closeBlock > startBlock, 'Staking: close block needs to be higher than start one!');
        closeBlock = _closeBlock;

        IStakingV2Vendor vendor;
        for (uint i=0; i<vendors.length; i++) {
            vendor = IStakingV2Vendor(vendorInfo[vendors[i]]);
            if (vendor.closeBlock() == 0 || vendor.closeBlock() > closeBlock) vendor.setCloseBlock(closeBlock);
        }
        emit CloseBlockChanged(closeBlock);
    }

    // set delegate to which events about staking amounts should be send to
    function setDelegateAddress(IStakingDelegate _delegate) public onlyOwner {
        require(address(_delegate) != address(0), 'Staking: delegate address needs to be different than zero!');
        delegate = _delegate;
        emit DelegateAddressChanged(address(delegate));
    }

    function withdrawRemaining() public onlyOwner {
        for (uint i=0; i<vendors.length; i++) withdrawRemaining(vendors[i]);
    }

    function withdrawRemaining(address asset) public onlyOwner {
        require(startBlock != 0, 'Staking: start block needs to be set first');
        require(closeBlock != 0, 'Staking: close block needs to be set first');
        require(block.number > closeBlock, 'Staking: withdrawal of remaining funds not ready yet');

        for (uint i=0; i<maxPid; i++) {
            updatePool(i);
        }
        getVendor(asset).withdrawRemaining(owner());
    }

    function pendingRewards(uint256 pid, address addr, address asset) external view returns (uint256) {
        require(pid < maxPid, 'Staking: invalid pool ID provided');
        require(startBlock > 0 && block.number >= startBlock, 'Staking: not started yet');
        return getVendor(asset).pendingRewards(pid, addr);
    }

    function getVAmount(uint256 pid, uint256 amount, uint256 timerange) public view returns (uint256) {
        PoolInfo storage pool = poolInfo[pid];
        if (pool.lockupMaxTimerange == 0) return amount;
        uint256 indx = multipliers.length * timerange / pool.lockupMaxTimerange;
        if (indx == 0) indx = 1;
        return amount * (1e5 + multipliers[indx-1]) / 1e5;
    }

    function deposit(uint256 pid, address addr, uint256 amount, uint256 timerange) external {
        _deposit(pid, msg.sender, addr, amount, timerange);
    }

    // restake is custom functionality in which funds can be restaked between allowed instances without
    function restake(uint256 pid, address addr, uint256 pocket, uint256 amount, uint256 timerange) external {
        require(allowedStakingInstances[addr], 'Staking: unable to restake funds to specified address');
        if (pocket > 0) token.safeTransferFrom(address(msg.sender), address(this), pocket);
        _withdraw(pid, msg.sender, address(this), amount);
        token.approve(addr, pocket+amount);
        StakingV2(addr).deposit(pid, msg.sender, pocket+amount, timerange);
    }

    function withdraw(uint256 pid, address /*addr*/, uint256 amount) external { // keep this method for backward compatibility
        _withdraw(pid, msg.sender, msg.sender, amount);
    }

    function _deposit(uint256 pid, address from, address addr, uint256 amount, uint256 timerange) internal {
        // amount eq to zero is allowed
        require(pid < maxPid, 'Staking: invalid pool ID provided');
        require(startBlock > 0 && block.number >= startBlock, 'Staking: not started yet');
        require(closeBlock == 0 || block.number <= closeBlock,
            'Staking: staking has ended, please withdraw remaining tokens');

        PoolInfo storage pool = poolInfo[pid];
        UserInfo storage user = userInfo[pid][addr];

        require(timerange <= pool.lockupMaxTimerange && timerange >= pool.lockupMinTimerange,
            'Staking: cannot lock funds for that amount of time!');
        require(timerange + block.timestamp >= user.lockedTimestamp,
            'Staking: timerange needs to be equal or higher from previous');

        require(pool.tokenTotalLimit == 0 || pool.tokenTotalLimit >= pool.tokenRealStaked + amount,
            'Staking: you cannot deposit over the limit!');
        require(minAmount == 0 || user.amount + amount >= minAmount, 'Staking: amount needs to be higher');
        require(maxAmount == 0 || user.amount + amount <= maxAmount, 'Staking: amount needs to be lesser');
        require(user.lockedTimestamp <= block.timestamp + timerange, 'Staking: cannot decrease lock time');

        updatePool(pid);

        uint256 virtAmount = getVAmount(pid, user.amount + amount, timerange);
        for (uint i=0; i<vendors.length; i++) getVendor(vendors[i]).update(pid, addr, virtAmount);

        if (amount > 0) {
            user.amount = user.amount + amount;
            pool.tokenRealStaked = pool.tokenRealStaked + amount;

            pool.tokenVirtStaked = pool.tokenVirtStaked - user.virtAmount + virtAmount;
            user.virtAmount = virtAmount;

            token.safeTransferFrom(address(from), address(this), amount); // deposit is from sender
        }
        user.lockedTimestamp = block.timestamp + timerange;
        user.lockupTimestamp = block.timestamp;
        user.lockupTimerange = timerange;
        emit Deposited(addr, pid, address(token), amount);

        if (address(delegate) != address(0)) {
            delegate.balanceChanged(addr, user.amount);
        }
    }

    function _withdraw(uint256 pid, address from, address addr, uint256 amount) internal {
        // amount eq to zero is allowed
        require(pid < maxPid, 'Staking: invalid pool ID provided');
        require(startBlock > 0 && block.number >= startBlock, 'Staking: not started yet');

        PoolInfo storage pool = poolInfo[pid];
        UserInfo storage user = userInfo[pid][from];

        require((addr == address(this)) || (block.timestamp >= user.lockedTimestamp)
            || (closeBlock > 0 && closeBlock <= block.number), 'Staking: you cannot withdraw yet!');
        require(user.amount >= amount, 'Staking: you cannot withdraw more than you have!');

        updatePool(pid);

        uint256 virtAmount = getVAmount(pid, user.amount - amount, user.lockupTimerange);
        for (uint i=0; i<vendors.length; i++) getVendor(vendors[i]).update(pid, addr, virtAmount);

        if (amount > 0) {
            user.amount = user.amount - amount;
            pool.tokenRealStaked = pool.tokenRealStaked - amount;

            pool.tokenVirtStaked = pool.tokenVirtStaked + user.virtAmount - virtAmount;
            user.virtAmount = virtAmount;

            if (addr != address(this)) token.safeTransfer(address(addr), amount);
        }
        user.lockedTimestamp = 0;
        user.lockupTimestamp = 0;
        emit Withdrawn(from, pid, address(token), amount);

        if (address(delegate) != address(0)) {
            delegate.balanceChanged(from, user.amount);
        }
    }

    function claim(uint256 pid) public {
        for (uint i=0; i<vendors.length; i++) claim(pid, vendors[i]);
    }

    function claim(uint256 pid, address asset) public {
        claimFromVendor(pid, address(getVendor(asset)));
    }

    function claimFromVendor(uint256 pid, address addr) public {
        require(pid < maxPid, 'Staking: invalid pool ID provided');
        require(startBlock > 0 && block.number >= startBlock, 'Staking: not started yet');
        updatePool(pid);
        IStakingV2Vendor(addr).claim(pid, msg.sender);
    }

    function addPool(uint256 _lockupMinTimerange, uint256 _lockupMaxTimerange, uint256 _tokenTotalLimit) internal {
        require(maxPid < 10, 'Staking: Cannot add more than 10 pools!');

        poolInfo.push(PoolInfo({
            lastBlock: 0,
            tokenPerShare: 0,
            tokenRealStaked: 0,
            tokenVirtStaked: 0,
            tokenRewarded: 0,
            tokenTotalLimit: _tokenTotalLimit,
            lockupMaxTimerange: _lockupMaxTimerange,
            lockupMinTimerange: _lockupMinTimerange
        }));
        maxPid++;

        emit PoolAdded(_lockupMinTimerange, _lockupMaxTimerange, _tokenTotalLimit);
    }

    function updatePool(uint256 pid) internal {
        if (pid >= maxPid) {
            return;
        }
        if (startBlock == 0 || block.number < startBlock) {
            return;
        }
        PoolInfo storage pool = poolInfo[pid];
        if (pool.lastBlock == 0) {
            pool.lastBlock = startBlock;
        }
        uint256 lastBlock = getLastRewardBlock();
        if (lastBlock <= pool.lastBlock) {
            return;
        }
        uint256 poolTokenVirtStaked = pool.tokenVirtStaked;
        if (poolTokenVirtStaked == 0) {
            return;
        }
        uint256 multiplier = lastBlock - pool.lastBlock;
        uint256 tokenAward = multiplier * tokenPerBlock;
        pool.tokenRewarded = pool.tokenRewarded + tokenAward;
        pool.tokenPerShare = pool.tokenPerShare + (tokenAward * 1e12 / poolTokenVirtStaked);
        pool.lastBlock = lastBlock;
    }

    function updateVendors() public {
        require(msg.sender == address(this) || msg.sender == owner() || hasRole(MAINTAINER_ROLE, msg.sender),
            'Staking: this method can only be called internally or by authority');
        address[] memory _newVendors = new address[](vendors.length);
        uint256 _size;
        address _addr;
        for (uint i=0; i<vendors.length; i++) {
            _addr = vendorInfo[vendors[i]];
            uint256 _closeBlock = IStakingV2Vendor(_addr).closeBlock();
            if (_closeBlock != 0 && _closeBlock < block.number) {
                delistedVendors.push(_addr);
            } else {
                _newVendors[_size++] = vendors[i];
            }
        }
        delete vendors;
        for (uint i=0; i<_size; i++) {
            vendors.push(_newVendors[i]);
        }
    }

    function getLastRewardBlock() internal view returns (uint256) {
        if (startBlock == 0) return 0;
        if (closeBlock == 0) return block.number;
        return (closeBlock < block.number) ? closeBlock : block.number;
    }

    function getVendor(address asset) internal view returns (IStakingV2Vendor) {
        address addr = vendorInfo[asset];
        require(addr != address(0), 'Staking: vendor for this token does not exist');
        return IStakingV2Vendor(addr);
    }
}

File 2 of 16 : StakingV2Vendor.sol
// SPDX-License-Identifier: MIT

pragma solidity =0.8.6;

import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/math/SafeMath.sol';
import './IStakingV2.sol';

/**
 * @title Token Staking
 * @dev BEP20 compatible token.
 */
contract StakingV2Vendor is Ownable {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    struct SuperPoolInfo {
        uint256 lastBlock;
        uint256 tokenPerShare;
        uint256 tokenRealStaked;
        uint256 tokenVirtStaked;
        uint256 tokenRewarded;
        uint256 tokenTotalLimit;
        uint256 lockupMaxTimerange;
        uint256 lockupMinTimerange;
    }

    struct SuperUserInfo {
        uint256 amount;
        uint256 rewardDebt; // backwards compatibility
        uint256 pendingRewards; // backwards compatibility
        uint256 lockedTimestamp;
        uint256 lockupTimestamp;
        uint256 lockupTimerange;
        uint256 virtAmount;
    }

    struct UserInfo {
        uint256 rewardDebt;
        uint256 pendingRewards;
    }

    struct PoolInfo {
        uint256 lastBlock;
        uint256 tokenPerShare;
        uint256 tokenRewarded;
        uint256 realTokenPerShare;
        uint256 realTokenReceived;
        uint256 realTokenRewarded;
    }

    IERC20 public token;
    IStakingV2 public parent;

    uint256 public tokenPerBlock;
    uint256 public tokenParentPrecision;
    uint256 public startBlock;
    uint256 public closeBlock;
    
    uint256 public maxPid;

    PoolInfo[] public poolInfo;
    mapping(uint256 => mapping(address => UserInfo)) public userInfo;

    event WithdrawnReward(address indexed user, uint256 indexed pid, address indexed token, uint256 amount);
    event WithdrawnRemain(address indexed user, uint256 indexed pid, address indexed token, uint256 amount);
    event TokenAddressChanged(address indexed token);
    event TokenRewardsChanged(address indexed token, uint256 amount);

    event ParentChanged(address indexed addr);
    event StartBlockChanged(uint256 block);
    event CloseBlockChanged(uint256 block);

    constructor(address _parent, IERC20 _token) {
        setParent(_parent);
        setTokenAddress(_token);
        for (uint i=0; i<parent.maxPid(); i++) addPool(i);
        tokenParentPrecision = parent.tokenPerBlock();
    }

    function setParent(address _parent) public onlyOwner {
        require(_parent != address(0), 'Staking: parent address needs to be different than zero!');
        parent = IStakingV2(_parent);
        emit ParentChanged(address(parent));
    }

    function setTokenAddress(IERC20 _token) public onlyOwner {
        require(address(_token) != address(0), 'Staking: token address needs to be different than zero!');
        require(address(token) == address(0), 'Staking: tokens already set!');
        token = _token;
        emit TokenAddressChanged(address(token));
    }

    function setTokenPerBlock(uint256 _tokenPerBlock, uint256 _startBlock, uint256 _closeBlock) public virtual onlyOwner {
        if (_startBlock != startBlock) setStartBlock(_startBlock);
        if (_closeBlock != closeBlock) setCloseBlock(_closeBlock);
        setTokenPerBlock(_tokenPerBlock);
    }

    function setTokenPerBlock(uint256 _tokenPerBlock) public virtual onlyOwner {
        require(startBlock != 0, 'Staking: cannot set reward before setting start block');
        for (uint i=0; i<maxPid; i++) updatePool(i);
        tokenPerBlock = _tokenPerBlock;
        emit TokenRewardsChanged(address(token), _tokenPerBlock);
    }

    function setStartBlock(uint256 _startBlock) public virtual onlyOwner {
        require(startBlock == 0 || startBlock > block.number, 'Staking: start block already set');
        require(_startBlock > 0, 'Staking: start block needs to be higher than zero!');
        startBlock = _startBlock;
        emit StartBlockChanged(_startBlock);
    }

    function setCloseBlock(uint256 _closeBlock) public virtual onlyOwner {
        require(startBlock != 0, 'Staking: start block needs to be set first');
        require(closeBlock == 0 || closeBlock > block.number, 'Staking: close block already set');
        require(_closeBlock == 0 || _closeBlock > startBlock, 'Staking: close block needs to be higher than start one!');
        closeBlock = _closeBlock;
        emit CloseBlockChanged(_closeBlock);
    }

    function withdrawRemaining(address addr) external virtual onlyOwner {
        if (startBlock == 0 || closeBlock == 0 || block.number <= closeBlock) {
            return;
        }
        for (uint i=0; i<maxPid; i++) {
            updatePool(i);
        }

        uint256 allTokenRewarded = 0;
        uint256 allTokenReceived = 0;

        for (uint i=0; i<maxPid; i++) {
            allTokenRewarded = allTokenRewarded.add(poolInfo[i].realTokenRewarded);
            allTokenReceived = allTokenReceived.add(poolInfo[i].realTokenReceived);
        }

        uint256 unlockedAmount = 0;
        uint256 possibleAmount = token.balanceOf(address(parent));
        uint256 reservedAmount = allTokenRewarded.sub(allTokenReceived);

        // if token is the same as deposit token then deduct staked tokens as non withdrawable
        if (address(token) == address(parent.token())) {
            for (uint i=0; i<maxPid; i++) {
                reservedAmount = reservedAmount.add(getParentPoolInfo(i).tokenRealStaked);
            }
        }

        if (possibleAmount > reservedAmount) {
            unlockedAmount = possibleAmount.sub(reservedAmount);
        }
        if (unlockedAmount > 0) {
            token.safeTransferFrom(address(parent), addr, unlockedAmount);
            emit WithdrawnRemain(addr, 0, address(token), unlockedAmount);
        }
    }

    function pendingRewards(uint256 pid, address addr) external virtual view returns (uint256) {
        if (pid >= maxPid || startBlock == 0 || block.number < startBlock) {
            return 0;
        }

        PoolInfo storage pool = poolInfo[pid];
        UserInfo storage user = userInfo[pid][addr];
        SuperUserInfo memory superUser = getParentUserInfo(pid, addr);
        uint256 amount = superUser.virtAmount;

        uint256 lastMintedBlock = pool.lastBlock;
        if (lastMintedBlock == 0) {
            lastMintedBlock = startBlock;
        }
        uint256 lastBlock = getLastRewardBlock();
        if (lastBlock == 0) {
            return 0;
        }
        SuperPoolInfo memory superPool = getParentPoolInfo(pid);
        uint256 poolTokenRealStaked = superPool.tokenVirtStaked;

        uint256 realTokenPerShare = pool.realTokenPerShare;
        if (lastBlock > lastMintedBlock && poolTokenRealStaked != 0) {
            uint256 tokenPerShare = superPool.tokenPerShare.sub(pool.tokenPerShare);
            realTokenPerShare = realTokenPerShare.add(tokenPerShare.mul(tokenPerBlock));
        }

        return amount.mul(realTokenPerShare).div(1e12).div(tokenParentPrecision).sub(user.rewardDebt).add(user.pendingRewards);
    }

    function update(uint256 pid, address user, uint256 amount) external virtual onlyOwner {
        if (pid >= maxPid || startBlock == 0 || block.number < startBlock) {
            return;
        }
        updatePool(pid);
        updatePendingReward(pid, user);
        updateRealizeReward(pid, user, amount);
    }

    function claim(uint256 pid, address addr) external virtual onlyOwner returns (uint256) {
        if (pid >= maxPid || startBlock == 0 || block.number < startBlock) {
            return 0;
        }

        PoolInfo storage pool = poolInfo[pid];
        UserInfo storage user = userInfo[pid][addr];

        updatePool(pid);
        updatePendingReward(pid, addr);

        uint256 claimedAmount = 0;
        if (user.pendingRewards > 0) {
            claimedAmount = transferPendingRewards(pid, addr, user.pendingRewards);
            emit WithdrawnReward(addr, pid, address(token), claimedAmount);
            user.pendingRewards = user.pendingRewards.sub(claimedAmount);
            pool.realTokenReceived = pool.realTokenReceived.add(claimedAmount);
        }

        updateRealizeReward(pid, addr);

        return claimedAmount;
    }

    function addPool(uint256 pid) internal {
        require(maxPid < 10, 'Staking: Cannot add more than 10 pools!');

        SuperPoolInfo memory superPool = getParentPoolInfo(pid);
        poolInfo.push(PoolInfo({
            lastBlock: 0,
            tokenPerShare: superPool.tokenPerShare,
            tokenRewarded: superPool.tokenRewarded,
            realTokenPerShare: 0,
            realTokenReceived: 0,
            realTokenRewarded: 0
        }));
        maxPid = maxPid.add(1);
    }

    function updatePool(uint256 pid) internal {
        if (pid >= maxPid) {
            return;
        }
        if (startBlock == 0 || block.number < startBlock) {
            return;
        }
        PoolInfo storage pool = poolInfo[pid];
        if (pool.lastBlock == 0) {
            pool.lastBlock = startBlock;
        }
        uint256 lastBlock = getLastRewardBlock();
        if (lastBlock <= pool.lastBlock) {
            return;
        }
        SuperPoolInfo memory superPool = getParentPoolInfo(pid);
        uint256 poolTokenRealStaked = superPool.tokenVirtStaked;
        if (poolTokenRealStaked == 0) {
            return;
        }

        // compute the difference between last update in vendor and last update in core staking contract
        // then multiply it by rewardPerBlock value to correctly compute reward
        uint256 multiplier = lastBlock.sub(pool.lastBlock);
        uint256 divisor = superPool.lastBlock.sub(pool.lastBlock);

        uint256 tokenRewarded = superPool.tokenRewarded.sub(pool.tokenRewarded);
        uint256 tokenPerShare = superPool.tokenPerShare.sub(pool.tokenPerShare);

        // if multiplier is different than divisor it means, that before update vendor contract has been closed, therefore
        // we need to multiply the values instead of overwtiitng as the block after close should not count here
        if (multiplier != divisor) {
            tokenRewarded = tokenRewarded.mul(multiplier).div(divisor);
            tokenPerShare = tokenPerShare.mul(multiplier).div(divisor);
        }
        pool.tokenRewarded = pool.tokenRewarded.add(tokenRewarded);
        pool.tokenPerShare = pool.tokenPerShare.add(tokenPerShare);

        pool.realTokenRewarded = pool.realTokenRewarded.add(tokenRewarded.mul(tokenPerBlock).div(tokenParentPrecision));
        pool.realTokenPerShare = pool.realTokenPerShare.add(tokenPerShare.mul(tokenPerBlock));
        pool.lastBlock = lastBlock;
    }

    function updatePendingReward(uint256 pid, address addr) internal {
        if (pid >= maxPid) {
            return;
        }
        PoolInfo storage pool = poolInfo[pid];
        UserInfo storage user = userInfo[pid][addr];
        SuperUserInfo memory superUser = getParentUserInfo(pid, addr);
        uint256 amount = superUser.virtAmount;

        uint256 reward;
        reward = amount.mul(pool.realTokenPerShare).div(1e12).div(tokenParentPrecision).sub(user.rewardDebt);
        if (reward > 0) {
            user.pendingRewards = user.pendingRewards.add(reward);
            user.rewardDebt = user.rewardDebt.add(reward);
        }
    }

    function updateRealizeReward(uint256 pid, address addr) internal {
        if (pid >= maxPid) {
            return;
        }
        SuperUserInfo memory superUser = getParentUserInfo(pid, addr);
        uint256 amount = superUser.virtAmount;
        return updateRealizeReward(pid, addr, amount);
    }

    function updateRealizeReward(uint256 pid, address addr, uint256 amount) internal {
        if (pid >= maxPid) {
            return;
        }
        PoolInfo storage pool = poolInfo[pid];
        UserInfo storage user = userInfo[pid][addr];
        uint256 reward;
        reward = amount.mul(pool.realTokenPerShare).div(1e12).div(tokenParentPrecision);
        user.rewardDebt = reward;
    }

    function transferPendingRewards(uint256 pid, address to, uint256 amount) internal returns (uint256) {
        if (pid >= maxPid) {
            return 0;
        }
        if (amount == 0) {
            return 0;
        }
        uint256 tokenAmount = token.balanceOf(address(parent));

        // if reward token is the same as deposit token deduct its balane from withdrawable amount
        if (tokenAmount != 0 && address(token) == address(parent.token())) {
            for (uint i=0; i<maxPid && tokenAmount > 0; i++) {
                uint256 tokenRealStaked = getParentPoolInfo(i).tokenRealStaked;
                tokenAmount = (tokenRealStaked >= tokenAmount) ? 0 : tokenAmount.sub(tokenRealStaked);
            }
        }
        if (tokenAmount == 0) {
            return 0;
        }
        if (tokenAmount > amount) {
            tokenAmount = amount;
        }
        token.safeTransferFrom(address(parent), to, tokenAmount);
        return tokenAmount;
    }

    function getLastRewardBlock() internal view returns (uint256) {
        if (startBlock == 0) return 0;
        if (closeBlock != 0 && closeBlock < block.number) return closeBlock;
        return block.number;
    }

    function getParentUserInfo(uint256 pid, address addr) internal view returns (SuperUserInfo memory) {
        ( uint256 amount, uint256 rewardDebt, uint256 pending, uint256 lockedTimestamp, uint256 lockupTimestamp,
        uint256 lockupTimerange, uint256 virtAmount ) = parent.userInfo(pid, addr);
        return SuperUserInfo({
            amount: amount, rewardDebt: rewardDebt, pendingRewards: pending, lockedTimestamp: lockedTimestamp,
            lockupTimestamp: lockupTimestamp, lockupTimerange: lockupTimerange, virtAmount: virtAmount
        });
    }

    function getParentPoolInfo(uint256 pid) internal view returns (SuperPoolInfo memory) {
        ( uint256 lastBlock, uint256 tokenPerShare, uint256 tokenRealStaked, uint256 tokenVirtStaked,
        uint256 tokenRewarded, uint256 tokenTotalLimit, uint256 lockupMaxTimerange, uint256 lockupMinTimerange ) = parent.poolInfo(pid);
        return SuperPoolInfo({
            lastBlock: lastBlock, tokenPerShare: tokenPerShare, tokenRealStaked: tokenRealStaked,
            tokenVirtStaked: tokenVirtStaked, tokenRewarded: tokenRewarded, tokenTotalLimit: tokenTotalLimit,
            lockupMaxTimerange: lockupMaxTimerange, lockupMinTimerange: lockupMinTimerange
        });
    }
}

File 3 of 16 : IStakingV2Vendor.sol
// SPDX-License-Identifier: MIT

pragma solidity =0.8.6;

abstract contract IStakingV2Vendor {

    function setTokenPerBlock(uint256 _tokenPerBlock) public virtual;

    function setTokenPerBlock(uint256 _tokenPerBlock, uint256 _startBlock, uint256 _closeBlock) public virtual;

    function startBlock() external view virtual returns (uint256);

    function closeBlock() external view virtual returns (uint256);

    function setStartBlock(uint256 _startBlock) public virtual;

    function setCloseBlock(uint256 _closeBlock) public virtual;

    function withdrawRemaining(address addr) external virtual;

    function pendingRewards(uint256 pid, address addr) external virtual view returns (uint256);

    function update(uint256 pid, address user, uint256 amount) external virtual;

    function claim(uint256 pid) external virtual returns (uint256);

    function claim(uint256 pid, address addr) external virtual returns (uint256);
}

File 4 of 16 : IStakingV2Factory.sol
// SPDX-License-Identifier: MIT

pragma solidity =0.8.6;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import './StakingV2Vendor.sol';
import './IStakingV2.sol';

/**
 * @title Token Staking
 * @dev BEP20 compatible token.
 */
interface IStakingV2Factory {

    function createVendor(address _parent, IERC20 _token) external returns (address);
}

File 5 of 16 : IStakingV2.sol
// SPDX-License-Identifier: MIT

pragma solidity =0.8.6;

abstract contract IStakingV2 {

    function userInfo(uint256 pid, address addr)
    public virtual view returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256);

    function poolInfo(uint256 pid)
    public virtual view returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256);

    function maxPid() public virtual view returns (uint256);

    function token() public virtual view returns (address);

    function tokenPerBlock() public virtual view returns (uint256);

    function pendingRewards(uint256 pid, address addr, address asset) external virtual view returns (uint256);

    function deposit(uint256 pid, address addr, uint256 amount, uint256 timerange) external virtual;

    function restake(uint256 pid, address addr, uint256 amount, uint256 timerange) external virtual;

    function withdraw(uint256 pid, address addr, uint256 amount) external virtual;

    function claim(uint256 pid) external virtual;
}

File 6 of 16 : IStakingDelegate.sol
// SPDX-License-Identifier: MIT

pragma solidity =0.8.6;

interface IStakingDelegate {

    function balanceChanged(address user, uint256 amount) external;
}

File 7 of 16 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 8 of 16 : IERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

File 9 of 16 : ERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC165.sol";

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

File 10 of 16 : Strings.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

File 11 of 16 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

File 12 of 16 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 13 of 16 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

File 14 of 16 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

File 16 of 16 : AccessControl.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    function hasRole(bytes32 role, address account) external view returns (bool);

    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    function grantRole(bytes32 role, address account) external;

    function revokeRole(bytes32 role, address account) external;

    function renounceRole(bytes32 role, address account) external;
}

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

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev 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 {_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 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]{20}) is missing role (0x[0-9a-f]{32})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role, _msgSender());
        _;
    }

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

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

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

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

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

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

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `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}.
     * ====
     */
    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 {
        emit RoleAdminChanged(role, getRoleAdmin(role), adminRole);
        _roles[role].adminRole = adminRole;
    }

    function _grantRole(bytes32 role, address account) private {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    function _revokeRole(bytes32 role, address account) private {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint256","name":"_minPoolTimer","type":"uint256"},{"internalType":"uint256","name":"_maxPoolTimer","type":"uint256"},{"internalType":"uint256","name":"_minAmount","type":"uint256"},{"internalType":"uint256","name":"_maxAmount","type":"uint256"},{"internalType":"uint256","name":"_poolLimit","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxAmount","type":"uint256"}],"name":"AllowedAmountsChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"block","type":"uint256"}],"name":"CloseBlockChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addr","type":"address"}],"name":"DelegateAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addr","type":"address"}],"name":"FactoryAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minTimer","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxTimer","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"limit","type":"uint256"}],"name":"PoolAdded","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":[],"name":"StakingInstanceChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"block","type":"uint256"}],"name":"StartBlockChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"vendor","type":"address"}],"name":"TokenVendorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawnRemain","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawnReward","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":"MAINTAINER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"addMaintainer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"stakingInstances","type":"address[]"},{"internalType":"bool","name":"status","type":"bool"}],"name":"addStakingInstances","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"allowedStakingInstances","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pid","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pid","type":"uint256"},{"internalType":"address","name":"asset","type":"address"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pid","type":"uint256"},{"internalType":"address","name":"addr","type":"address"}],"name":"claimFromVendor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"closeBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"delMaintainer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"delegate","outputs":[{"internalType":"contract IStakingDelegate","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"delistedVendors","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pid","type":"uint256"},{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"timerange","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract IStakingV2Factory","name":"","type":"address"}],"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":"uint256","name":"pid","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"timerange","type":"uint256"}],"name":"getVAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isMaintainer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxPid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"multipliers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pid","type":"uint256"},{"internalType":"address","name":"addr","type":"address"},{"internalType":"address","name":"asset","type":"address"}],"name":"pendingRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"poolInfo","outputs":[{"internalType":"uint256","name":"lastBlock","type":"uint256"},{"internalType":"uint256","name":"tokenPerShare","type":"uint256"},{"internalType":"uint256","name":"tokenRealStaked","type":"uint256"},{"internalType":"uint256","name":"tokenVirtStaked","type":"uint256"},{"internalType":"uint256","name":"tokenRewarded","type":"uint256"},{"internalType":"uint256","name":"tokenTotalLimit","type":"uint256"},{"internalType":"uint256","name":"lockupMaxTimerange","type":"uint256"},{"internalType":"uint256","name":"lockupMinTimerange","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pid","type":"uint256"},{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"pocket","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"timerange","type":"uint256"}],"name":"restake","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":"_minAmount","type":"uint256"},{"internalType":"uint256","name":"_maxAmount","type":"uint256"}],"name":"setAllowedAmounts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_closeBlock","type":"uint256"}],"name":"setCloseBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IStakingDelegate","name":"_delegate","type":"address"}],"name":"setDelegateAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IStakingV2Factory","name":"_factory","type":"address"}],"name":"setFactoryAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_startBlock","type":"uint256"}],"name":"setStartBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint256","name":"_tokenPerBlock","type":"uint256"},{"internalType":"uint256","name":"_blockRange","type":"uint256"}],"name":"setTokenPerBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint256","name":"_tokenPerBlock","type":"uint256"}],"name":"setTokenPerBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint256","name":"_tokenPerBlock","type":"uint256"},{"internalType":"uint256","name":"_startBlock","type":"uint256"},{"internalType":"uint256","name":"_closeBlock","type":"uint256"}],"name":"setTokenPerBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenPerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateVendors","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"userInfo","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"rewardDebt","type":"uint256"},{"internalType":"uint256","name":"pendingRewards","type":"uint256"},{"internalType":"uint256","name":"lockedTimestamp","type":"uint256"},{"internalType":"uint256","name":"lockupTimestamp","type":"uint256"},{"internalType":"uint256","name":"lockupTimerange","type":"uint256"},{"internalType":"uint256","name":"virtAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"vendorInfo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vendors","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pid","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawRemaining","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"withdrawRemaining","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102f15760003560e01c80638da5cb5b1161019d578063da12120a116100e9578063e9754f9e116100a2578063f35e4a6e1161007c578063f35e4a6e14610747578063f87422541461075a578063fc0c546a1461076f578063fd0cdcdb1461078257600080fd5b8063e9754f9e14610703578063ee38db951461072c578063f2fde38b1461073457600080fd5b8063da12120a1461069c578063dd57366a146106a4578063ddd5e1b2146106b7578063dea3537a146106ca578063e174101a146106dd578063e63697c8146106f057600080fd5b8063a6a85ff611610156578063c45a015511610130578063c45a015514610650578063c89e436114610663578063cbf20f8614610676578063d547741f1461068957600080fd5b8063a6a85ff614610617578063ac7fc2631461062a578063b1d4450a1461063d57600080fd5b80638da5cb5b1461052957806391d148541461054e57806393f1a40b146105615780639b2cb5d8146105f3578063a08d986d146105fc578063a217fddf1461060f57600080fd5b806344112c841161025c5780636b453c1f11610215578063715018a6116101ef578063715018a6146104de57806375b238fc146104e657806383c17c551461050d5780638cdcbdef1461052057600080fd5b80636b453c1f146104a55780636bde67d8146104b85780636f0372af146104cb57600080fd5b806344112c841461043757806344eefcda1461044a57806348cd4cb11461045d5780634d8caba1146104665780635e91d4a3146104895780635f48f3931461049c57600080fd5b80632f2ff15d116102ae5780632f2ff15d146103cf57806335c8518b146103e257806336568abe146103f5578063379607f5146104085780634198709a1461041b57806341b1c9b91461042457600080fd5b806301ffc9a7146102f65780630b6793091461031e5780631526fe27146103355780632214b39614610383578063248a9ca31461039857806329d55bfa146103bc575b600080fd5b610309610304366004613de4565b610795565b60405190151581526020015b60405180910390f35b61032760085481565b604051908152602001610315565b610348610343366004613d9b565b6107cc565b604080519889526020890197909752958701949094526060860192909252608085015260a084015260c083015260e082015261010001610315565b610396610391366004613e3a565b610824565b005b6103276103a6366004613d9b565b6000908152600160208190526040909120015490565b6103966103ca366004613e0e565b61099f565b6103966103dd366004613db4565b610cf3565b6103966103f0366004613d9b565b610d1a565b610396610403366004613db4565b61102c565b610396610416366004613d9b565b6110a6565b61032760055481565b610396610432366004613fc2565b6110f6565b610396610445366004613db4565b611160565b610396610458366004613e6f565b61123a565b61032760065481565b610309610474366004613c66565b600e6020526000908152604090205460ff1681565b610396610497366004613c66565b611978565b61032760045481565b6103096104b3366004613c66565b611a68565b6103966104c6366004613f7a565b611b11565b6103966104d9366004613f3d565b611cb7565b610396611cc4565b6103277fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177581565b61039661051b366004613c66565b611cfa565b61032760075481565b6000546001600160a01b03165b6040516001600160a01b039091168152602001610315565b61030961055c366004613db4565b611dea565b6105be61056f366004613db4565b600a602052816000526040600020602052806000526040600020600091509150508060000154908060010154908060020154908060030154908060040154908060050154908060060154905087565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e001610315565b61032760035481565b61053661060a366004613d9b565b611e15565b610327600081565b610327610625366004613fe4565b611e3f565b610327610638366004613d9b565b611f01565b61030961064b366004613c66565b611f22565b601154610536906001600160a01b031681565b601054610536906001600160a01b031681565b610396610684366004613ca0565b611f8a565b610396610697366004613db4565b612047565b61039661206e565b6103096106b2366004613c66565b612383565b6103966106c5366004613db4565b61239d565b6105366106d8366004613d9b565b6123aa565b6103276106eb366004613ec3565b6123ba565b6103966106fe366004613f05565b6124a0565b610536610711366004613c66565b600b602052600090815260409020546001600160a01b031681565b6103966124ac565b610396610742366004613c66565b612528565b610396610755366004613d9b565b6125c0565b6103276000805160206143ff83398151915281565b600254610536906001600160a01b031681565b610396610790366004613c66565b61289b565b60006001600160e01b03198216637965db0b60e01b14806107c657506301ffc9a760e01b6001600160e01b03198316145b92915050565b600981815481106107dc57600080fd5b90600052602060002090600802016000915090508060000154908060010154908060020154908060030154908060040154908060050154908060060154908060070154905088565b6000546001600160a01b031633148061085057506108506000805160206143ff83398151915233611dea565b6108755760405162461bcd60e51b815260040161086c90614168565b60405180910390fd5b6006546108945760405162461bcd60e51b815260040161086c9061422c565b6001600160a01b0383166108ba5760405162461bcd60e51b815260040161086c906140d4565b6001600160a01b038084166000908152600b602052604081205460065492169143116108e8576006546108ea565b435b905060008315610903576108fe84836142cb565b610906565b60005b90506001600160a01b0383161561098b57826001600160a01b03166348cd4cb16040518163ffffffff1660e01b815260040160206040518083038186803b15801561095057600080fd5b505afa158015610964573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109889190613eaa565b91505b6109978686848461123a565b505050505050565b6000546001600160a01b03163314806109cb57506109cb6000805160206143ff83398151915233611dea565b6109e75760405162461bcd60e51b815260040161086c90614168565b600654610a065760405162461bcd60e51b815260040161086c9061422c565b6001600160a01b038216610a2c5760405162461bcd60e51b815260040161086c906140d4565b6001600160a01b038083166000908152600b6020526040902054168015610ce15760008190506000816001600160a01b0316638cdcbdef6040518163ffffffff1660e01b815260040160206040518083038186803b158015610a8d57600080fd5b505afa158015610aa1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ac59190613eaa565b9050801580610ad45750804311155b15610cde5760005b600854811015610b0157610aef81612a62565b80610af98161437e565b915050610adc565b5060405163095ea7b360e01b81526001600160a01b038381166004830152600019602483015286169063095ea7b390604401602060405180830381600087803b158015610b4d57600080fd5b505af1158015610b61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b859190613d7e565b50816001600160a01b03166365b2c82985846001600160a01b03166348cd4cb16040518163ffffffff1660e01b815260040160206040518083038186803b158015610bcf57600080fd5b505afa158015610be3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c079190613eaa565b856001600160a01b0316638cdcbdef6040518163ffffffff1660e01b815260040160206040518083038186803b158015610c4057600080fd5b505afa158015610c54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c789190613eaa565b6040516001600160e01b031960e086901b1681526004810193909352602483019190915260448201526064015b600060405180830381600087803b158015610cbf57600080fd5b505af1158015610cd3573d6000803e3d6000fd5b505050505050505050565b50505b610ced83836000610824565b505b5050565b60008281526001602081905260409091200154610d108133612b56565b610ced8383612bba565b6000546001600160a01b03163314610d445760405162461bcd60e51b815260040161086c906141b6565b600654610d635760405162461bcd60e51b815260040161086c90614281565b6007541580610d73575043600754115b610dbf5760405162461bcd60e51b815260206004820181905260248201527f5374616b696e673a20636c6f736520626c6f636b20616c726561647920736574604482015260640161086c565b6006548111610e365760405162461bcd60e51b815260206004820152603760248201527f5374616b696e673a20636c6f736520626c6f636b206e6565647320746f20626560448201527f20686967686572207468616e207374617274206f6e6521000000000000000000606482015260840161086c565b60078190556000805b600c54811015610fec57600b6000600c8381548110610e6057610e606143af565b60009182526020808320909101546001600160a01b039081168452838201949094526040928301909120548251638cdcbdef60e01b81529251931694508492638cdcbdef926004808201939291829003018186803b158015610ec157600080fd5b505afa158015610ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef99190613eaa565b1580610f765750600754826001600160a01b0316638cdcbdef6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f3c57600080fd5b505afa158015610f50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f749190613eaa565b115b15610fda576007546040516335c8518b60e01b815260048101919091526001600160a01b038316906335c8518b90602401600060405180830381600087803b158015610fc157600080fd5b505af1158015610fd5573d6000803e3d6000fd5b505050505b80610fe48161437e565b915050610e3f565b507ff611389adc906823d7195c85599775805c1387992a80055df34e70b86bdde4d560075460405161102091815260200190565b60405180910390a15050565b6001600160a01b038116331461109c5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b606482015260840161086c565b610cef8282612c25565b60005b600c54811015610cef576110e482600c83815481106110ca576110ca6143af565b6000918252602090912001546001600160a01b031661239d565b806110ee8161437e565b9150506110a9565b6000546001600160a01b031633146111205760405162461bcd60e51b815260040161086c906141b6565b6003829055600481905560408051838152602081018390527f26351c8971f0ff45f250f0c7b77f7e669e593789661a6568de1158b176e5f8929101611020565b60085482106111815760405162461bcd60e51b815260040161086c906141eb565b600060065411801561119557506006544310155b6111b15760405162461bcd60e51b815260040161086c90614131565b6111ba82612a62565b604051636eeaf0d960e11b8152600481018390523360248201526001600160a01b0382169063ddd5e1b290604401602060405180830381600087803b15801561120257600080fd5b505af1158015611216573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ced9190613eaa565b6000546001600160a01b031633148061126657506112666000805160206143ff83398151915233611dea565b6112825760405162461bcd60e51b815260040161086c90614168565b6006546112a15760405162461bcd60e51b815260040161086c9061422c565b6006548210156113195760405162461bcd60e51b815260206004820152603b60248201527f5374616b696e673a20746f6b656e20737461727420626c6f636b206e6565647360448201527f20746f20626520646966666572656e74207468616e207a65726f210000000000606482015260840161086c565b81811180611325575080155b6113975760405162461bcd60e51b815260206004820152603f60248201527f5374616b696e673a20746f6b656e20636c6f736520626c6f636b206e6565647360448201527f20746f20626520686967686572207468616e20737461727420626c6f636b2100606482015260840161086c565b6001600160a01b0384166113bd5760405162461bcd60e51b815260040161086c906140d4565b60005b6008548110156113e5576113d381612a62565b806113dd8161437e565b9150506113c0565b506001600160a01b038085166000908152600b6020526040812054909116908115611687578190506000816001600160a01b03166348cd4cb16040518163ffffffff1660e01b815260040160206040518083038186803b15801561144857600080fd5b505afa15801561145c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114809190613eaa565b90506000826001600160a01b0316638cdcbdef6040518163ffffffff1660e01b815260040160206040518083038186803b1580156114bd57600080fd5b505afa1580156114d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114f59190613eaa565b90508015806115045750804311155b1561166c578186148061151657508143105b6115775760405162461bcd60e51b815260206004820152602c60248201527f5374616b696e673a20746f6b656e20737461727420626c6f636b2063616e6e6f60448201526b1d0818994818da185b99d95960a21b606482015260840161086c565b60405163095ea7b360e01b81526001600160a01b038481166004830152600019602483015289169063095ea7b390604401602060405180830381600087803b1580156115c257600080fd5b505af11580156115d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115fa9190613d7e565b506040516365b2c82960e01b81526004810188905260248101879052604481018690526001600160a01b038416906365b2c82990606401600060405180830381600087803b15801561164b57600080fd5b505af115801561165f573d6000803e3d6000fd5b5050505050505050611972565b801580159061167a57508581105b1561168457600093505b50505b6001600160a01b03821661191a5761169d61206e565b600c5460141161170d5760405162461bcd60e51b815260206004820152603560248201527f5374616b696e673a206c696d6974206f66206163746976656c792064697374726044820152741a589d5d1959081d1bdad95b9cc81c995858da1959605a1b606482015260840161086c565b60115460405163fa34253d60e01b81523060048201526001600160a01b0388811660248301529091169063fa34253d90604401602060405180830381600087803b15801561175a57600080fd5b505af115801561176e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117929190613c83565b60405163095ea7b360e01b81526001600160a01b03808316600483015260001960248301529193508392509087169063095ea7b390604401602060405180830381600087803b1580156117e457600080fd5b505af11580156117f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061181c9190613d7e565b506040516365b2c82960e01b81526004810186905260248101859052604481018490526001600160a01b038216906365b2c82990606401600060405180830381600087803b15801561186d57600080fd5b505af1158015611881573d6000803e3d6000fd5b505050506001600160a01b038681166000818152600b602052604080822080546001600160a01b0319908116958716958617909155600c80546001810182559084527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c701805490911684179055517f4373dba20343af31864250f901f41d417ca59e890d3dd9f5d0c84a8bd0f3c80a9190a35050611972565b60405162461bcd60e51b815260206004820152602760248201527f5374616b696e673a20696e76616c696420636f6e66696775726174696f6e20706044820152661c9bdd9a59195960ca1b606482015260840161086c565b50505050565b6000546001600160a01b031633146119a25760405162461bcd60e51b815260040161086c906141b6565b6001600160a01b038116611a1e5760405162461bcd60e51b815260206004820152603a60248201527f5374616b696e673a2064656c65676174652061646472657373206e656564732060448201527f746f20626520646966666572656e74207468616e207a65726f21000000000000606482015260840161086c565b601080546001600160a01b0319166001600160a01b0383169081179091556040517f69a4107abdc2802c71a34a194ad1d45e227763f036400dde83dd23862242990490600090a250565b600080546001600160a01b03163314611a935760405162461bcd60e51b815260040161086c906141b6565b6040516000805160206143ff83398151915260248201526001600160a01b0383166044820152632f2ff15d60e01b90611b059082906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091523090612c8c565b5060019150505b919050565b6001600160a01b0384166000908152600e602052604090205460ff16611b975760405162461bcd60e51b815260206004820152603560248201527f5374616b696e673a20756e61626c6520746f2072657374616b652066756e647360448201527420746f20737065636966696564206164647265737360581b606482015260840161086c565b8215611bb557600254611bb5906001600160a01b0316333086612cce565b611bc185333085612d39565b6002546001600160a01b031663095ea7b385611bdd85876142cb565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381600087803b158015611c2357600080fd5b505af1158015611c37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c5b9190613d7e565b506001600160a01b038416636f0372af8633611c7786886142cb565b6040516001600160e01b031960e086901b16815260048101939093526001600160a01b039091166024830152604482015260648101849052608401610ca5565b61197284338585856130fb565b6000546001600160a01b03163314611cee5760405162461bcd60e51b815260040161086c906141b6565b611cf8600061370c565b565b6000546001600160a01b03163314611d245760405162461bcd60e51b815260040161086c906141b6565b6001600160a01b038116611da05760405162461bcd60e51b815260206004820152603960248201527f5374616b696e673a20666163746f72792061646472657373206e65656473207460448201527f6f20626520646966666572656e74207468616e207a65726f2100000000000000606482015260840161086c565b601180546001600160a01b0319166001600160a01b0383169081179091556040517ff6f48d556c1c66026aceb2fa5ba6a4eedf21a0468512691e0cd42dc68a3c275c90600090a250565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b600c8181548110611e2557600080fd5b6000918252602090912001546001600160a01b0316905081565b60008060098581548110611e5557611e556143af565b90600052602060002090600802019050806006015460001415611e7b5783915050611efa565b6006810154600f5460009190611e92908690614305565b611e9c91906142e3565b905080611ea7575060015b620186a0600f611eb8600184614324565b81548110611ec857611ec86143af565b9060005260206000200154620186a0611ee191906142cb565b611eeb9087614305565b611ef591906142e3565b925050505b9392505050565b600f8181548110611f1157600080fd5b600091825260209091200154905081565b600080546001600160a01b03163314611f4d5760405162461bcd60e51b815260040161086c906141b6565b6040516000805160206143ff83398151915260248201526001600160a01b038316604482015263d547741f60e01b90611b05908290606401611acc565b6000546001600160a01b03163314611fb45760405162461bcd60e51b815260040161086c906141b6565b60005b82518110156120195781600e6000858481518110611fd757611fd76143af565b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff19169115159190911790556120128161437e565b9050611fb7565b506040517ffd8a781e7e7c3a923166188810e4bca52ca831b391f5bde5c2fe942545e6314b90600090a15050565b600082815260016020819052604090912001546120648133612b56565b610ced8383612c25565b3330148061208657506000546001600160a01b031633145b806120a457506120a46000805160206143ff83398151915233611dea565b6121215760405162461bcd60e51b815260206004820152604260248201527f5374616b696e673a2074686973206d6574686f642063616e206f6e6c7920626560448201527f2063616c6c656420696e7465726e616c6c79206f7220627920617574686f7269606482015261747960f01b608482015260a40161086c565b600c5460009067ffffffffffffffff81111561213f5761213f6143c5565b604051908082528060200260200182016040528015612168578160200160208202803683370190505b50905060008060005b600c5481101561230a57600b6000600c8381548110612192576121926143af565b60009182526020808320909101546001600160a01b0390811684528382019490945260409283018220548351638cdcbdef60e01b815293519416955090928592638cdcbdef926004808301939192829003018186803b1580156121f457600080fd5b505afa158015612208573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061222c9190613eaa565b9050801580159061223c57504381105b1561229157600d80546001810182556000919091527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50180546001600160a01b0319166001600160a01b0385161790556122f7565b600c82815481106122a4576122a46143af565b6000918252602090912001546001600160a01b031685856122c48161437e565b9650815181106122d6576122d66143af565b60200260200101906001600160a01b031690816001600160a01b0316815250505b50806123028161437e565b915050612171565b50612317600c6000613c1e565b60005b8281101561197257600c848281518110612336576123366143af565b60209081029190910181015182546001810184556000938452919092200180546001600160a01b0319166001600160a01b039092169190911790558061237b8161437e565b91505061231a565b60006107c66000805160206143ff83398151915283611dea565b610cef826104458361375c565b600d8181548110611e2557600080fd5b600060085484106123dd5760405162461bcd60e51b815260040161086c906141eb565b60006006541180156123f157506006544310155b61240d5760405162461bcd60e51b815260040161086c90614131565b6124168261375c565b6040516334637d4f60e21b8152600481018690526001600160a01b038581166024830152919091169063d18df53c9060440160206040518083038186803b15801561246057600080fd5b505afa158015612474573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124989190613eaa565b949350505050565b610ced83333384612d39565b6000546001600160a01b031633146124d65760405162461bcd60e51b815260040161086c906141b6565b60005b600c5481101561252557612513600c82815481106124f9576124f96143af565b6000918252602090912001546001600160a01b031661289b565b8061251d8161437e565b9150506124d9565b50565b6000546001600160a01b031633146125525760405162461bcd60e51b815260040161086c906141b6565b6001600160a01b0381166125b75760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161086c565b6125258161370c565b6000546001600160a01b031633146125ea5760405162461bcd60e51b815260040161086c906141b6565b60065415806125fa575043600654115b6126465760405162461bcd60e51b815260206004820181905260248201527f5374616b696e673a20737461727420626c6f636b20616c726561647920736574604482015260640161086c565b600081116126b15760405162461bcd60e51b815260206004820152603260248201527f5374616b696e673a20737461727420626c6f636b206e6565647320746f20626560448201527120686967686572207468616e207a65726f2160701b606482015260840161086c565b60068190556000805b600c5481101561286757600b6000600c83815481106126db576126db6143af565b60009182526020808320909101546001600160a01b0390811684528382019490945260409283019091205482516348cd4cb160e01b815292519316945084926348cd4cb1926004808201939291829003018186803b15801561273c57600080fd5b505afa158015612750573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127749190613eaa565b15806127f15750600654826001600160a01b03166348cd4cb16040518163ffffffff1660e01b815260040160206040518083038186803b1580156127b757600080fd5b505afa1580156127cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127ef9190613eaa565b105b15612855576006546040516379af253760e11b815260048101919091526001600160a01b0383169063f35e4a6e90602401600060405180830381600087803b15801561283c57600080fd5b505af1158015612850573d6000803e3d6000fd5b505050505b8061285f8161437e565b9150506126ba565b507f2ff5a4bd003b5c8c334ec018975259184173d0eabd4384b3ddbf74f447dd5a7460065460405161102091815260200190565b6000546001600160a01b031633146128c55760405162461bcd60e51b815260040161086c906141b6565b6006546128e45760405162461bcd60e51b815260040161086c90614281565b6007546129465760405162461bcd60e51b815260206004820152602a60248201527f5374616b696e673a20636c6f736520626c6f636b206e6565647320746f206265604482015269081cd95d08199a5c9cdd60b21b606482015260840161086c565b60075443116129b45760405162461bcd60e51b815260206004820152603460248201527f5374616b696e673a207769746864726177616c206f662072656d61696e696e6760448201527308199d5b991cc81b9bdd081c9958591e481e595d60621b606482015260840161086c565b60005b6008548110156129dc576129ca81612a62565b806129d48161437e565b9150506129b7565b506129e68161375c565b6001600160a01b031663fd0cdcdb612a066000546001600160a01b031690565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401600060405180830381600087803b158015612a4757600080fd5b505af1158015612a5b573d6000803e3d6000fd5b5050505050565b6008548110612a6e5750565b6006541580612a7e575060065443105b15612a865750565b600060098281548110612a9b57612a9b6143af565b90600052602060002090600802019050806000015460001415612abe5760065481555b6000612ac86137dc565b82549091508111612ad857505050565b600382015480612ae85750505050565b8254600090612af79084614324565b9050600060055482612b099190614305565b9050808560040154612b1b91906142cb565b600486015582612b308264e8d4a51000614305565b612b3a91906142e3565b8560010154612b4991906142cb565b6001860155505050905550565b612b608282611dea565b610cef57612b78816001600160a01b03166014613810565b612b83836020613810565b604051602001612b9492919061402c565b60408051601f198184030181529082905262461bcd60e51b825261086c916004016140a1565b612bc48282611dea565b610cef5760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b612c2f8282611dea565b15610cef5760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6060611efa83836040518060400160405280601e81526020017f416464726573733a206c6f772d6c6576656c2063616c6c206661696c656400008152506139ac565b6040516001600160a01b03808516602483015283166044820152606481018290526119729085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526139bb565b6008548410612d5a5760405162461bcd60e51b815260040161086c906141eb565b6000600654118015612d6e57506006544310155b612d8a5760405162461bcd60e51b815260040161086c90614131565b600060098581548110612d9f57612d9f6143af565b60009182526020808320888452600a825260408085206001600160a01b03808b168752935290932060089092029092019250908416301480612de5575080600301544210155b80612dff57506000600754118015612dff57504360075411155b612e555760405162461bcd60e51b815260206004820152602160248201527f5374616b696e673a20796f752063616e6e6f74207769746864726177207965746044820152602160f81b606482015260840161086c565b8054831115612ebf5760405162461bcd60e51b815260206004820152603060248201527f5374616b696e673a20796f752063616e6e6f74207769746864726177206d6f7260448201526f65207468616e20796f7520686176652160801b606482015260840161086c565b612ec886612a62565b6000612ee887858460000154612ede9190614324565b8460050154611e3f565b905060005b600c54811015612fa557612f27600c8281548110612f0d57612f0d6143af565b6000918252602090912001546001600160a01b031661375c565b60405163277108b960e21b8152600481018a90526001600160a01b038881166024830152604482018590529190911690639dc422e490606401600060405180830381600087803b158015612f7a57600080fd5b505af1158015612f8e573d6000803e3d6000fd5b505050508080612f9d9061437e565b915050612eed565b508315613022578154612fb9908590614324565b82556002830154612fcb908590614324565b6002840155600682015460038401548291612fe5916142cb565b612fef9190614324565b6003840155600682018190556001600160a01b038516301461302257600254613022906001600160a01b03168686613a8d565b60006003830181905560048301556002546040518581526001600160a01b03918216918991908916907f6562355720ad5e9816350753de199edab487334dc9b97f1b15e35e1907ca7ed59060200160405180910390a46010546001600160a01b0316156130f2576010548254604051631ee51a0b60e31b81526001600160a01b038981166004830152602482019290925291169063f728d05890604401600060405180830381600087803b1580156130d957600080fd5b505af11580156130ed573d6000803e3d6000fd5b505050505b50505050505050565b600854851061311c5760405162461bcd60e51b815260040161086c906141eb565b600060065411801561313057506006544310155b61314c5760405162461bcd60e51b815260040161086c90614131565b600754158061315d57506007544311155b6131cf5760405162461bcd60e51b815260206004820152603c60248201527f5374616b696e673a207374616b696e672068617320656e6465642c20706c656160448201527f73652077697468647261772072656d61696e696e6720746f6b656e7300000000606482015260840161086c565b6000600986815481106131e4576131e46143af565b60009182526020808320898452600a825260408085206001600160a01b038a16865290925292206006600890920290920190810154909250831180159061322f575081600701548310155b6132975760405162461bcd60e51b815260206004820152603360248201527f5374616b696e673a2063616e6e6f74206c6f636b2066756e647320666f72207460448201527268617420616d6f756e74206f662074696d652160681b606482015260840161086c565b60038101546132a642856142cb565b101561331a5760405162461bcd60e51b815260206004820152603c60248201527f5374616b696e673a2074696d6572616e6765206e6565647320746f206265206560448201527f7175616c206f72206869676865722066726f6d2070726576696f757300000000606482015260840161086c565b6005820154158061333e575083826002015461333691906142cb565b826005015410155b61339e5760405162461bcd60e51b815260206004820152602b60248201527f5374616b696e673a20796f752063616e6e6f74206465706f736974206f76657260448201526a20746865206c696d69742160a81b606482015260840161086c565b60035415806133bb575060035481546133b89086906142cb565b10155b6134125760405162461bcd60e51b815260206004820152602260248201527f5374616b696e673a20616d6f756e74206e6565647320746f206265206869676860448201526132b960f11b606482015260840161086c565b600454158061342f5750600454815461342c9086906142cb565b11155b6134865760405162461bcd60e51b815260206004820152602260248201527f5374616b696e673a20616d6f756e74206e6565647320746f206265206c65737360448201526132b960f11b606482015260840161086c565b61349083426142cb565b816003015411156134ee5760405162461bcd60e51b815260206004820152602260248201527f5374616b696e673a2063616e6e6f74206465637265617365206c6f636b2074696044820152616d6560f01b606482015260840161086c565b6134f787612a62565b60006135138886846000015461350d91906142cb565b86611e3f565b905060005b600c548110156135b657613538600c8281548110612f0d57612f0d6143af565b60405163277108b960e21b8152600481018b90526001600160a01b038981166024830152604482018590529190911690639dc422e490606401600060405180830381600087803b15801561358b57600080fd5b505af115801561359f573d6000803e3d6000fd5b5050505080806135ae9061437e565b915050613518565b5084156136245781546135ca9086906142cb565b825560028301546135dc9086906142cb565b60028401556006820154600384015482916135f691614324565b61360091906142cb565b600384015560068201819055600254613624906001600160a01b0316883088612cce565b61362e84426142cb565b6003830155426004830155600582018490556002546040518681526001600160a01b03918216918a91908916907fc490a74c1058132dffb93944d555ddd1817ae53b7367ea1126ff123b1b134a589060200160405180910390a46010546001600160a01b031615613702576010548254604051631ee51a0b60e31b81526001600160a01b038981166004830152602482019290925291169063f728d05890604401600060405180830381600087803b1580156136e957600080fd5b505af11580156136fd573d6000803e3d6000fd5b505050505b5050505050505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b038082166000908152600b6020526040812054909116806107c65760405162461bcd60e51b815260206004820152602d60248201527f5374616b696e673a2076656e646f7220666f72207468697320746f6b656e206460448201526c1bd95cc81b9bdd08195e1a5cdd609a1b606482015260840161086c565b6000600654600014156137ef5750600090565b6007546137fb57504390565b436007541061380957504390565b5060075490565b6060600061381f836002614305565b61382a9060026142cb565b67ffffffffffffffff811115613842576138426143c5565b6040519080825280601f01601f19166020018201604052801561386c576020820181803683370190505b509050600360fc1b81600081518110613887576138876143af565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106138b6576138b66143af565b60200101906001600160f81b031916908160001a90535060006138da846002614305565b6138e59060016142cb565b90505b600181111561395d576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110613919576139196143af565b1a60f81b82828151811061392f5761392f6143af565b60200101906001600160f81b031916908160001a90535060049490941c9361395681614367565b90506138e8565b508315611efa5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161086c565b60606124988484600085613abd565b6000613a10826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166139ac9092919063ffffffff16565b805190915015610ced5780806020019051810190613a2e9190613d7e565b610ced5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161086c565b6040516001600160a01b038316602482015260448101829052610ced90849063a9059cbb60e01b90606401612d02565b606082471015613b1e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840161086c565b843b613b6c5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161086c565b600080866001600160a01b03168587604051613b889190614010565b60006040518083038185875af1925050503d8060008114613bc5576040519150601f19603f3d011682016040523d82523d6000602084013e613bca565b606091505b5091509150613bda828286613be5565b979650505050505050565b60608315613bf4575081611efa565b825115613c045782518084602001fd5b8160405162461bcd60e51b815260040161086c91906140a1565b508054600082559060005260206000209081019061252591905b80821115613c4c5760008155600101613c38565b5090565b8035611b0c816143db565b8035611b0c816143f0565b600060208284031215613c7857600080fd5b8135611efa816143db565b600060208284031215613c9557600080fd5b8151611efa816143db565b60008060408385031215613cb357600080fd5b823567ffffffffffffffff80821115613ccb57600080fd5b818501915085601f830112613cdf57600080fd5b8135602082821115613cf357613cf36143c5565b8160051b604051601f19603f83011681018181108682111715613d1857613d186143c5565b604052838152828101945085830182870184018b1015613d3757600080fd5b600096505b84871015613d6157613d4d81613c50565b865260019690960195948301948301613d3c565b509650613d719050878201613c5b565b9450505050509250929050565b600060208284031215613d9057600080fd5b8151611efa816143f0565b600060208284031215613dad57600080fd5b5035919050565b60008060408385031215613dc757600080fd5b823591506020830135613dd9816143db565b809150509250929050565b600060208284031215613df657600080fd5b81356001600160e01b031981168114611efa57600080fd5b60008060408385031215613e2157600080fd5b8235613e2c816143db565b946020939093013593505050565b600080600060608486031215613e4f57600080fd5b8335613e5a816143db565b95602085013595506040909401359392505050565b60008060008060808587031215613e8557600080fd5b8435613e90816143db565b966020860135965060408601359560600135945092505050565b600060208284031215613ebc57600080fd5b5051919050565b600080600060608486031215613ed857600080fd5b833592506020840135613eea816143db565b91506040840135613efa816143db565b809150509250925092565b600080600060608486031215613f1a57600080fd5b833592506020840135613f2c816143db565b929592945050506040919091013590565b60008060008060808587031215613f5357600080fd5b843593506020850135613f65816143db565b93969395505050506040820135916060013590565b600080600080600060a08688031215613f9257600080fd5b853594506020860135613fa4816143db565b94979496505050506040830135926060810135926080909101359150565b60008060408385031215613fd557600080fd5b50508035926020909101359150565b600080600060608486031215613ff957600080fd5b505081359360208301359350604090920135919050565b6000825161402281846020870161433b565b9190910192915050565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161406481601785016020880161433b565b7001034b99036b4b9b9b4b733903937b6329607d1b601791840191820152835161409581602884016020880161433b565b01602801949350505050565b60208152600082518060208401526140c081604085016020870161433b565b601f01601f19169190910160400192915050565b60208082526037908201527f5374616b696e673a20746f6b656e2061646472657373206e6565647320746f2060408201527f626520646966666572656e74207468616e207a65726f21000000000000000000606082015260800190565b60208082526018908201527f5374616b696e673a206e6f742073746172746564207965740000000000000000604082015260600190565b6020808252602e908201527f5374616b696e673a206f6e6c7920617574686f7269746965732063616e20636160408201526d1b1b081d1a1a5cc81b595d1a1bd960921b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526021908201527f5374616b696e673a20696e76616c696420706f6f6c2049442070726f766964656040820152601960fa1b606082015260800190565b60208082526035908201527f5374616b696e673a2063616e6e6f742061646420726577617264206265666f72604082015274652073657474696e6720737461727420626c6f636b60581b606082015260800190565b6020808252602a908201527f5374616b696e673a20737461727420626c6f636b206e6565647320746f206265604082015269081cd95d08199a5c9cdd60b21b606082015260800190565b600082198211156142de576142de614399565b500190565b60008261430057634e487b7160e01b600052601260045260246000fd5b500490565b600081600019048311821515161561431f5761431f614399565b500290565b60008282101561433657614336614399565b500390565b60005b8381101561435657818101518382015260200161433e565b838111156119725750506000910152565b60008161437657614376614399565b506000190190565b600060001982141561439257614392614399565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461252557600080fd5b801515811461252557600080fdfe339759585899103d2ace64958e37e18ccb0504652c81d4a1b8aa80fe2126ab95a26469706673582212203778aa0c514777b42553d8a8220046975735b57d32ca70ac52bcd1dbac49b23b64736f6c63430008060033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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