ETH Price: $3,408.03 (+2.64%)

Contract

0x49685E0E495399b0D554Ea7603C48d9D1679b3F4
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040147622572022-05-12 16:57:47926 days ago1652374667IN
 Create: StkTruToken
0 ETH0.4451783100

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
StkTruToken

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, None license
File 1 of 15 : StkTruToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {IERC20} from "IERC20.sol";
import {SafeERC20} from "SafeERC20.sol";
import {SafeCast} from "SafeCast.sol";
import {Math} from "Math.sol";
import {ReentrancyGuard} from "ReentrancyGuard.sol";

import {VoteToken} from "VoteToken.sol";
import {ITrueDistributor} from "ITrueDistributor.sol";
import {StkClaimableContract} from "StkClaimableContract.sol";
import {IPauseableContract} from "IPauseableContract.sol";

/**
 * @title stkTRU
 * @dev Staking contract for TrueFi
 * TRU is staked and stored in the contract
 * stkTRU is minted when staking
 * Holders of stkTRU accrue rewards over time
 * Rewards are paid in TRU and tfUSD
 * stkTRU can be used to vote in governance
 * stkTRU can be used to rate and approve loans
 */
contract StkTruToken is VoteToken, StkClaimableContract, IPauseableContract, ReentrancyGuard {
    using SafeERC20 for IERC20;

    uint256 private constant PRECISION = 1e30;
    uint256 private constant MIN_DISTRIBUTED_AMOUNT = 100e8;
    uint256 private constant MAX_COOLDOWN = 100 * 365 days;
    uint256 private constant MAX_UNSTAKE_PERIOD = 100 * 365 days;
    uint32 private constant SCHEDULED_REWARDS_BATCH_SIZE = 32;

    struct FarmRewards {
        // track overall cumulative rewards
        uint256 cumulativeRewardPerToken;
        // track previous cumulate rewards for accounts
        mapping(address => uint256) previousCumulatedRewardPerToken;
        // track claimable rewards for accounts
        mapping(address => uint256) claimableReward;
        // track total rewards
        uint256 totalClaimedRewards;
        uint256 totalFarmRewards;
    }

    struct ScheduledTfUsdRewards {
        uint64 timestamp;
        uint96 amount;
    }

    // ================ WARNING ==================
    // ===== THIS CONTRACT IS INITIALIZABLE ======
    // === STORAGE VARIABLES ARE DECLARED BELOW ==
    // REMOVAL OR REORDER OF VARIABLES WILL RESULT
    // ========= IN STORAGE CORRUPTION ===========

    IERC20 public tru;
    IERC20 public tfusd;
    ITrueDistributor public distributor;
    address public liquidator;

    uint256 public stakeSupply;

    mapping(address => uint256) internal cooldowns;
    uint256 public cooldownTime;
    uint256 public unstakePeriodDuration;

    mapping(IERC20 => FarmRewards) public farmRewards;

    uint32[] public sortedScheduledRewardIndices;
    ScheduledTfUsdRewards[] public scheduledRewards;
    uint256 public undistributedTfusdRewards;
    uint32 public nextDistributionIndex;

    mapping(address => bool) public whitelistedFeePayers;

    mapping(address => uint256) public receivedDuringCooldown;

    // allow pausing of deposits
    bool public pauseStatus;

    IERC20 public feeToken;

    Checkpoint[] internal _totalSupplyCheckpoints;

    // ======= STORAGE DECLARATION END ============

    event Stake(address indexed staker, uint256 amount);
    event Unstake(address indexed staker, uint256 burntAmount);
    event Claim(address indexed who, IERC20 indexed token, uint256 amountClaimed);
    event Withdraw(uint256 amount);
    event Cooldown(address indexed who, uint256 endTime);
    event CooldownTimeChanged(uint256 newUnstakePeriodDuration);
    event UnstakePeriodDurationChanged(uint256 newUnstakePeriodDuration);
    event FeePayerWhitelistingStatusChanged(address payer, bool status);
    event PauseStatusChanged(bool pauseStatus);
    event FeeTokenChanged(IERC20 token);
    event LiquidatorChanged(address liquidator);

    /**
     * Get TRU from distributor
     */
    modifier distribute() {
        // pull TRU from distributor
        // do not pull small amounts to save some gas
        // only pull if there is distribution and distributor farm is set to this farm
        if (distributor.nextDistribution() >= MIN_DISTRIBUTED_AMOUNT && distributor.farm() == address(this)) {
            distributor.distribute();
        }
        _;
    }

    /**
     * Update all rewards when an account changes state
     * @param account Account to update rewards for
     */
    modifier update(address account) {
        _update(account);
        _;
    }

    function _update(address account) internal {
        updateTotalRewards(tru);
        updateClaimableRewards(tru, account);
        updateTotalRewards(tfusd);
        updateClaimableRewards(tfusd, account);
        updateTotalRewards(feeToken);
        updateClaimableRewards(feeToken, account);
    }

    /**
     * Update rewards for a specific token when an account changes state
     * @param account Account to update rewards for
     * @param token Token to update rewards for
     */
    modifier updateRewards(address account, IERC20 token) {
        if (token == tru || token == tfusd || token == feeToken) {
            updateTotalRewards(token);
            updateClaimableRewards(token, account);
        }
        _;
    }

    constructor() {
        initalized = true;
    }

    /**
     * @dev Initialize contract and set default values
     * @param _tru TRU token
     * @param _tfusd tfUSD token
     * @param _feeToken Token for fees, currently tfUSDC
     * @param _distributor Distributor for this contract
     * @param _liquidator Liquidator for staked TRU
     */
    function initialize(
        IERC20 _tru,
        IERC20 _tfusd,
        IERC20 _feeToken,
        ITrueDistributor _distributor,
        address _liquidator
    ) public {
        require(!initalized, "StkTruToken: Already initialized");
        require(address(_tru) != address(0), "StkTruToken: TRU token address must not be 0");
        require(address(_tfusd) != address(0), "StkTruToken: tfUSD token address must not be 0");
        require(address(_feeToken) != address(0), "StkTruToken: fee token address must not be 0");
        tru = _tru;
        tfusd = _tfusd;
        feeToken = _feeToken;
        distributor = _distributor;
        liquidator = _liquidator;

        cooldownTime = 14 days;
        unstakePeriodDuration = 2 days;

        initTotalSupplyCheckpoints();

        owner_ = msg.sender;
        initalized = true;
    }

    function initTotalSupplyCheckpoints() public onlyOwner {
        require(_totalSupplyCheckpoints.length == 0, "StakeTruToken: Total supply checkpoints already initialized");
        _totalSupplyCheckpoints.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint96(totalSupply)}));
    }

    function _mint(address account, uint256 amount) internal virtual override {
        super._mint(account, amount);
        _writeTotalSupplyCheckpoint(_add, amount);
    }

    function _burn(address account, uint256 amount) internal virtual override {
        super._burn(account, amount);
        _writeTotalSupplyCheckpoint(_subtract, amount);
    }

    /**
     * @dev Set tfUSDC address
     * @param _feeToken Address of tfUSDC to be set
     */
    function setFeeToken(IERC20 _feeToken) external onlyOwner {
        require(address(_feeToken) != address(0), "StkTruToken: fee token address must not be 0");
        require(rewardBalance(feeToken) == 0, "StkTruToken: Cannot replace fee token with underlying rewards");
        feeToken = _feeToken;
        emit FeeTokenChanged(_feeToken);
    }

    /**
     * @dev Set liquidator address
     * @param _liquidator Address of liquidator to be set
     */
    function setLiquidator(address _liquidator) external onlyOwner {
        liquidator = _liquidator;
        emit LiquidatorChanged(_liquidator);
    }

    /**
     * @dev Owner can use this function to add new addresses to payers whitelist
     * Only whitelisted payers can call payFee method
     * @param payer Address that is being added to or removed from whitelist
     * @param status New whitelisting status
     */
    function setPayerWhitelistingStatus(address payer, bool status) external onlyOwner {
        whitelistedFeePayers[payer] = status;
        emit FeePayerWhitelistingStatusChanged(payer, status);
    }

    /**
     * @dev Owner can use this function to set cooldown time
     * Cooldown time defines how long a staker waits to unstake TRU
     * @param newCooldownTime New cooldown time for stakers
     */
    function setCooldownTime(uint256 newCooldownTime) external onlyOwner {
        // Avoid overflow
        require(newCooldownTime <= MAX_COOLDOWN, "StkTruToken: Cooldown too large");

        cooldownTime = newCooldownTime;
        emit CooldownTimeChanged(newCooldownTime);
    }

    /**
     * @dev Allow pausing of deposits in case of emergency
     * @param status New deposit status
     */
    function setPauseStatus(bool status) external override onlyOwner {
        pauseStatus = status;
        emit PauseStatusChanged(status);
    }

    /**
     * @dev Owner can set unstake period duration
     * Unstake period defines how long after cooldown a user has to withdraw stake
     * @param newUnstakePeriodDuration New unstake period
     */
    function setUnstakePeriodDuration(uint256 newUnstakePeriodDuration) external onlyOwner {
        require(newUnstakePeriodDuration > 0, "StkTruToken: Unstake period cannot be 0");
        // Avoid overflow
        require(newUnstakePeriodDuration <= MAX_UNSTAKE_PERIOD, "StkTruToken: Unstake period too large");

        unstakePeriodDuration = newUnstakePeriodDuration;
        emit UnstakePeriodDurationChanged(newUnstakePeriodDuration);
    }

    /**
     * @dev Stake TRU for stkTRU
     * Updates rewards when staking
     * @param amount Amount of TRU to stake for stkTRU
     */
    function stake(uint256 amount) external distribute update(msg.sender) {
        require(!pauseStatus, "StkTruToken: Can be called only when not paused");
        _stakeWithoutTransfer(amount);
        tru.safeTransferFrom(msg.sender, address(this), amount);
    }

    /**
     * @dev Unstake stkTRU for TRU
     * Can only unstake when cooldown complete and within unstake period
     * Claims rewards when unstaking
     * @param amount Amount of stkTRU to unstake for TRU
     */
    // slither-disable-next-line reentrancy-eth
    function unstake(uint256 amount) external distribute update(msg.sender) nonReentrant {
        require(amount > 0, "StkTruToken: Cannot unstake 0");

        require(unstakable(msg.sender) >= amount, "StkTruToken: Insufficient balance");
        require(unlockTime(msg.sender) <= block.timestamp, "StkTruToken: Stake on cooldown");

        _claim(tru);
        _claim(tfusd);
        _claim(feeToken);

        uint256 amountToTransfer = (amount * stakeSupply) / totalSupply;

        _burn(msg.sender, amount);
        stakeSupply = stakeSupply - amountToTransfer;

        tru.safeTransfer(msg.sender, amountToTransfer);

        emit Unstake(msg.sender, amount);
    }

    /**
     * @dev Initiate cooldown period
     */
    function cooldown() public {
        cooldowns[msg.sender] = block.timestamp;
        receivedDuringCooldown[msg.sender] = 0;

        emit Cooldown(msg.sender, block.timestamp + cooldownTime);
    }

    /**
     * @dev Withdraw TRU from the contract for liquidation
     * @param amount Amount to withdraw for liquidation
     */
    function withdraw(uint256 amount) external {
        require(msg.sender == liquidator, "StkTruToken: Can be called only by the liquidator");
        require(amount <= stakeSupply, "StkTruToken: Insufficient stake supply");
        stakeSupply = stakeSupply - amount;
        tru.safeTransfer(liquidator, amount);

        emit Withdraw(amount);
    }

    /**
     * @dev View function to get unlock time for an account
     * @param account Account to get unlock time for
     * @return Unlock time for account
     */
    function unlockTime(address account) public view returns (uint256) {
        uint256 cooldownStart = cooldowns[account];
        if (cooldownStart == 0 || cooldownStart + cooldownTime + unstakePeriodDuration < block.timestamp) {
            return type(uint256).max;
        }
        return cooldownStart + cooldownTime;
    }

    /**
     * @dev Give tfUSD as origination fee to stake.this
     * 50% are given immediately and 50% after `endTime` passes
     */
    function payFee(uint256 amount, uint256 endTime) external {
        require(whitelistedFeePayers[msg.sender], "StkTruToken: Can be called only by whitelisted payers");
        require(endTime <= type(uint64).max, "StkTruToken: time overflow");
        require(amount <= type(uint96).max, "StkTruToken: amount overflow");

        tfusd.safeTransferFrom(msg.sender, address(this), amount);
        uint256 halfAmount = amount / 2;
        undistributedTfusdRewards = undistributedTfusdRewards + halfAmount;
        scheduledRewards.push(ScheduledTfUsdRewards({amount: uint96(amount - halfAmount), timestamp: uint64(endTime)}));

        uint32 newIndex = findPositionForTimestamp(endTime);
        insertAt(newIndex, uint32(scheduledRewards.length) - 1);
    }

    /**
     * @dev Claim all rewards
     */
    // slither-disable-next-line reentrancy-eth
    function claim() external distribute update(msg.sender) {
        _claim(tru);
        _claim(tfusd);
        _claim(feeToken);
    }

    /**
     * @dev Claim rewards for specific token
     * Allows account to claim specific token to save gas
     * @param token Token to claim rewards for
     */
    function claimRewards(IERC20 token) external distribute updateRewards(msg.sender, token) {
        require(token == tfusd || token == tru || token == feeToken, "Token not supported for rewards");
        _claim(token);
    }

    /**
     * @dev Claim TRU rewards, transfer in extraStakeAmount, and
     * stake both the rewards and the new amount.
     * Allows account to save more gas by avoiding out-and-back transfers of rewards
     */
    function claimRestake(uint256 extraStakeAmount) external distribute update(msg.sender) {
        uint256 amount = _claimWithoutTransfer(tru) + extraStakeAmount;
        _stakeWithoutTransfer(amount);
        if (extraStakeAmount > 0) {
            tru.safeTransferFrom(msg.sender, address(this), extraStakeAmount);
        }
    }

    /**
     * @dev View to estimate the claimable reward for an account
     * @param account Account to get claimable reward for
     * @param token Token to get rewards for
     * @return claimable rewards for account
     */
    function claimable(address account, IERC20 token) external view returns (uint256) {
        FarmRewards storage rewards = farmRewards[token];
        // estimate pending reward from distributor
        uint256 pendingReward = token == tru ? distributor.nextDistribution() : 0;
        // calculate total rewards (including pending)
        uint256 newTotalFarmRewards = (rewardBalance(token) +
            (pendingReward >= MIN_DISTRIBUTED_AMOUNT ? pendingReward : 0) +
            (rewards.totalClaimedRewards)) * PRECISION;
        // calculate block reward
        uint256 totalBlockReward = newTotalFarmRewards - rewards.totalFarmRewards;
        // calculate next cumulative reward per token
        uint256 nextCumulativeRewardPerToken = rewards.cumulativeRewardPerToken + (totalBlockReward / totalSupply);
        // return claimable reward for this account
        return
            rewards.claimableReward[account] +
            ((balanceOf[account] * (nextCumulativeRewardPerToken - (rewards.previousCumulatedRewardPerToken[account]))) / PRECISION);
    }

    /**
     * @dev max amount of stkTRU than can be unstaked after current cooldown period is over
     */
    function unstakable(address staker) public view returns (uint256) {
        uint256 stakerBalance = balanceOf[staker];

        if (unlockTime(staker) == type(uint256).max) {
            return stakerBalance;
        }

        if (receivedDuringCooldown[staker] > stakerBalance) {
            return 0;
        }
        return stakerBalance - receivedDuringCooldown[staker];
    }

    /**
     * @dev Prior votes votes are calculated as priorVotes * stakedSupply / totalSupply
     * This dilutes voting power when TRU is liquidated
     * @param account Account to get current voting power for
     * @param blockNumber Block to get prior votes at
     * @return prior voting power for account and block
     */
    function getPriorVotes(address account, uint256 blockNumber) public view override returns (uint96) {
        uint96 votes = super.getPriorVotes(account, blockNumber);
        return safe96((stakeSupply * votes) / totalSupply, "StkTruToken: uint96 overflow");
    }

    function getPastVotes(address account, uint256 blockNumber) public view returns (uint96) {
        return super.getPriorVotes(account, blockNumber);
    }

    function getPastTotalSupply(uint256 blockNumber) public view returns (uint256) {
        require(blockNumber < block.number, "ERC20Votes: block not yet mined");
        return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber);
    }

    /**
     * @dev Current votes are calculated as votes * stakedSupply / totalSupply
     * This dilutes voting power when TRU is liquidated
     * @param account Account to get current voting power for
     * @return voting power for account
     */
    function getCurrentVotes(address account) public view override returns (uint96) {
        uint96 votes = super.getCurrentVotes(account);
        return safe96((stakeSupply * votes) / totalSupply, "StkTruToken: uint96 overflow");
    }

    function decimals() public pure override returns (uint8) {
        return 8;
    }

    function rounding() public pure returns (uint8) {
        return 8;
    }

    function name() public pure override returns (string memory) {
        return "Staked TrueFi";
    }

    function symbol() public pure override returns (string memory) {
        return "stkTRU";
    }

    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal override distribute update(sender) {
        updateClaimableRewards(tru, recipient);
        updateClaimableRewards(tfusd, recipient);
        updateClaimableRewards(feeToken, recipient);
        // unlockTime returns MAX_UINT256 when there's no ongoing cooldown for the address
        if (unlockTime(recipient) != type(uint256).max) {
            receivedDuringCooldown[recipient] = receivedDuringCooldown[recipient] + amount;
        }
        if (unlockTime(sender) != type(uint256).max) {
            receivedDuringCooldown[sender] = receivedDuringCooldown[sender] - min(receivedDuringCooldown[sender], amount);
        }
        super._transfer(sender, recipient, amount);
    }

    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Internal claim function
     * Claim rewards for a specific ERC20 token
     * @param token Token to claim rewards for
     */
    function _claim(IERC20 token) internal {
        uint256 rewardToClaim = _claimWithoutTransfer(token);
        if (rewardToClaim > 0) {
            token.safeTransfer(msg.sender, rewardToClaim);
        }
    }

    /**
     * @dev Internal claim function that returns the transfer value
     * Claim rewards for a specific ERC20 token to return in a uint256
     * @param token Token to claim rewards for
     */
    function _claimWithoutTransfer(IERC20 token) internal returns (uint256) {
        FarmRewards storage rewards = farmRewards[token];

        uint256 rewardToClaim = rewards.claimableReward[msg.sender];
        rewards.totalClaimedRewards = rewards.totalClaimedRewards + rewardToClaim;
        rewards.claimableReward[msg.sender] = 0;
        emit Claim(msg.sender, token, rewardToClaim);
        return rewardToClaim;
    }

    /**
     * @dev Internal stake of TRU for stkTRU from a uint256
     * Caller is responsible for ensuring amount is transferred from a valid source
     * @param amount Amount of TRU to stake for stkTRU
     */
    function _stakeWithoutTransfer(uint256 amount) internal {
        require(amount > 0, "StkTruToken: Cannot stake 0");

        if (cooldowns[msg.sender] != 0 && cooldowns[msg.sender] + cooldownTime + unstakePeriodDuration > block.timestamp) {
            cooldown();
        }

        if (delegates[msg.sender] == address(0)) {
            delegates[msg.sender] = msg.sender;
        }

        uint256 amountToMint = stakeSupply == 0 ? amount : (amount * totalSupply) / stakeSupply;
        _mint(msg.sender, amountToMint);
        stakeSupply = stakeSupply + amount;
        emit Stake(msg.sender, amount);
    }

    /**
     * @dev Get reward balance of this contract for a token
     * @param token Token to get reward balance for
     * @return Reward balance for token
     */
    function rewardBalance(IERC20 token) internal view returns (uint256) {
        if (token == tru) {
            return token.balanceOf(address(this)) - stakeSupply;
        }
        if (token == tfusd) {
            return token.balanceOf(address(this)) - undistributedTfusdRewards;
        }
        if (token == feeToken) {
            return token.balanceOf(address(this));
        }
        return 0;
    }

    /**
     * @dev Check if any scheduled rewards should be distributed
     */
    function distributeScheduledRewards() internal {
        uint32 index = nextDistributionIndex;
        uint32 batchLimitIndex = index + SCHEDULED_REWARDS_BATCH_SIZE;
        uint32 end = batchLimitIndex < scheduledRewards.length ? batchLimitIndex : uint32(scheduledRewards.length);
        uint256 _undistributedTfusdRewards = undistributedTfusdRewards;

        while (index < end) {
            ScheduledTfUsdRewards storage rewards = scheduledRewards[sortedScheduledRewardIndices[index]];
            if (rewards.timestamp >= block.timestamp) {
                break;
            }
            _undistributedTfusdRewards = _undistributedTfusdRewards - rewards.amount;
            index++;
        }

        undistributedTfusdRewards = _undistributedTfusdRewards;

        if (nextDistributionIndex != index) {
            nextDistributionIndex = index;
        }
    }

    /**
     * @dev Update rewards state for `token`
     */
    function updateTotalRewards(IERC20 token) internal {
        if (token == tfusd) {
            distributeScheduledRewards();
        }
        FarmRewards storage rewards = farmRewards[token];

        // calculate total rewards
        uint256 newTotalFarmRewards = (rewardBalance(token) + rewards.totalClaimedRewards) * PRECISION;
        if (newTotalFarmRewards == rewards.totalFarmRewards) {
            return;
        }
        // calculate block reward
        uint256 totalBlockReward = newTotalFarmRewards - rewards.totalFarmRewards;
        // update farm rewards
        rewards.totalFarmRewards = newTotalFarmRewards;
        // if there are stakers
        if (totalSupply > 0) {
            rewards.cumulativeRewardPerToken = rewards.cumulativeRewardPerToken + totalBlockReward / totalSupply;
        }
    }

    /**
     * @dev Update claimable rewards for a token and account
     * @param token Token to update claimable rewards for
     * @param user Account to update claimable rewards for
     */
    function updateClaimableRewards(IERC20 token, address user) internal {
        FarmRewards storage rewards = farmRewards[token];

        // update claimable reward for sender
        if (balanceOf[user] > 0) {
            rewards.claimableReward[user] =
                rewards.claimableReward[user] +
                (balanceOf[user] * (rewards.cumulativeRewardPerToken - rewards.previousCumulatedRewardPerToken[user])) /
                PRECISION;
        }

        // update previous cumulative for sender
        rewards.previousCumulatedRewardPerToken[user] = rewards.cumulativeRewardPerToken;
    }

    /**
     * @dev Find next distribution index given a timestamp
     * @param timestamp Timestamp to find next distribution index for
     */
    function findPositionForTimestamp(uint256 timestamp) internal view returns (uint32 i) {
        uint256 length = sortedScheduledRewardIndices.length;
        for (i = nextDistributionIndex; i < length; i++) {
            if (scheduledRewards[sortedScheduledRewardIndices[i]].timestamp > timestamp) {
                return i;
            }
        }
        return i;
    }

    /**
     * @dev internal function to insert distribution index in a sorted list
     * @param index Index to insert at
     * @param value Value at index
     */
    function insertAt(uint32 index, uint32 value) internal {
        sortedScheduledRewardIndices.push(0);
        for (uint32 j = uint32(sortedScheduledRewardIndices.length) - 1; j > index; j--) {
            sortedScheduledRewardIndices[j] = sortedScheduledRewardIndices[j - 1];
        }
        sortedScheduledRewardIndices[index] = value;
    }

    function _writeTotalSupplyCheckpoint(function(uint256, uint256) view returns (uint256) op, uint256 delta)
        internal
        returns (uint256 oldWeight, uint256 newWeight)
    {
        uint256 checkpointsNumber = _totalSupplyCheckpoints.length;
        require(checkpointsNumber > 0, "StakeTruToken: total supply checkpoints not initialized");
        Checkpoint storage lastCheckpoint = _totalSupplyCheckpoints[checkpointsNumber - 1];

        oldWeight = lastCheckpoint.votes;
        newWeight = op(oldWeight, delta);

        if (lastCheckpoint.fromBlock == block.number) {
            lastCheckpoint.votes = SafeCast.toUint96(newWeight);
        } else {
            _totalSupplyCheckpoints.push(
                Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint96(newWeight)})
            );
        }
    }

    function _add(uint256 a, uint256 b) private pure returns (uint256) {
        return a + b;
    }

    function _subtract(uint256 a, uint256 b) private pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Lookup a value in a list of (sorted) checkpoints.
     */
    function _checkpointsLookup(Checkpoint[] storage checkpoints, uint256 blockNumber) internal view returns (uint256) {
        // We run a binary search to look for the earliest checkpoint taken after `blockNumber`.
        //
        // During the loop, the index of the wanted checkpoint remains in the range [low-1, high).
        // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant.
        // - If the middle checkpoint is after `blockNumber`, we look in [low, mid)
        // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high)
        // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not
        // out of bounds (in which case we're looking too far in the past and the result is 0).
        // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is
        // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out
        // the same.
        uint256 high = checkpoints.length;
        uint256 low = 0;
        while (low < high) {
            uint256 mid = Math.average(low, high);
            if (checkpoints[mid].fromBlock > blockNumber) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        return high == 0 ? 0 : checkpoints[high - 1].votes;
    }
}

File 2 of 15 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from,
        address to,
        uint256 amount
    ) external returns (bool);
}

File 3 of 15 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @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
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

import "IERC20.sol";
import "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 5 of 15 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
        return int128(value);
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
        return int64(value);
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
        return int32(value);
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
        return int16(value);
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 6 of 15 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

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

        _;

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

File 8 of 15 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

File 9 of 15 : ProxyStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

/**
 * All storage must be declared here
 * New storage must be appended to the end
 * Never remove items from this list
 */
contract ProxyStorage {
    bool public initalized;
    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;
    mapping(uint144 => uint256) attributes_Depricated;

    address owner_;
    address pendingOwner_;

    mapping(address => address) public delegates; // A record of votes checkpoints for each account, by index
    struct Checkpoint {
        // A checkpoint for marking number of votes from a given block
        uint32 fromBlock;
        uint96 votes;
    }
    mapping(address => mapping(uint32 => Checkpoint)) public checkpoints; // A record of votes checkpoints for each account, by index
    mapping(address => uint32) public numCheckpoints; // The number of checkpoints for each account
    mapping(address => uint256) public nonces;

    /* Additionally, we have several keccak-based storage locations.
     * If you add more keccak-based storage mappings, such as mappings, you must document them here.
     * If the length of the keccak input is the same as an existing mapping, it is possible there could be a preimage collision.
     * A preimage collision can be used to attack the contract by treating one storage location as another,
     * which would always be a critical issue.
     * Carefully examine future keccak-based storage to ensure there can be no preimage collisions.
     *******************************************************************************************************
     ** length     input                                                         usage
     *******************************************************************************************************
     ** 19         "trueXXX.proxy.owner"                                         Proxy Owner
     ** 27         "trueXXX.pending.proxy.owner"                                 Pending Proxy Owner
     ** 28         "trueXXX.proxy.implementation"                                Proxy Implementation
     ** 64         uint256(address),uint256(1)                                   balanceOf
     ** 64         uint256(address),keccak256(uint256(address),uint256(2))       allowance
     ** 64         uint256(address),keccak256(bytes32,uint256(3))                attributes
     **/
}

File 10 of 15 : ERC20.sol
/**
 * @notice This is a copy of openzeppelin ERC20 contract with removed state variables.
 * Removing state variables has been necessary due to proxy pattern usage.
 * Changes to Openzeppelin ERC20 https://github.com/OpenZeppelin/openzeppelin-contracts/blob/de99bccbfd4ecd19d7369d01b070aa72c64423c9/contracts/token/ERC20/ERC20.sol:
 * - Remove state variables _name, _symbol, _decimals
 * - Use state variables balances, allowances, totalSupply from ProxyStorage
 * - Remove constructor
 * - Solidity version changed from ^0.6.0 to 0.6.10
 * - Contract made abstract
 * - Remove inheritance from IERC20 because of ProxyStorage name conflicts
 *
 * See also: ClaimableOwnable.sol and ProxyStorage.sol
 */

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

import {Context} from "Context.sol";
import {Address} from "Address.sol";

import {ProxyStorage} from "ProxyStorage.sol";

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

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


    /**
     * @dev Returns the name of the token.
     */
    function name() public virtual pure returns (string memory);

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public virtual pure returns (string memory);

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

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

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

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

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

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

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

        _beforeTokenTransfer(sender, recipient, amount);

        balanceOf[sender] = balanceOf[sender] - amount;
        balanceOf[recipient] = balanceOf[recipient] + amount;
        emit Transfer(sender, recipient, amount);
    }

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

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

        totalSupply = totalSupply + amount;
        balanceOf[account] = balanceOf[account] + amount;
        emit Transfer(address(0), account, amount);
    }

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

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

        balanceOf[account] = balanceOf[account] - amount;
        totalSupply = totalSupply - amount;
        emit Transfer(account, address(0), amount);
    }

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

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

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

File 11 of 15 : IVoteToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {IERC20} from "IERC20.sol";

interface IVoteToken {
    function delegate(address delegatee) external;

    function delegateBySig(
        address delegatee,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    function getCurrentVotes(address account) external view returns (uint96);

    function getPriorVotes(address account, uint256 blockNumber) external view returns (uint96);
}

interface IVoteTokenWithERC20 is IVoteToken, IERC20 {}

File 12 of 15 : VoteToken.sol
// SPDX-License-Identifier: MIT
// AND COPIED FROM https://github.com/compound-finance/compound-protocol/blob/c5fcc34222693ad5f547b14ed01ce719b5f4b000/contracts/Governance/Comp.sol
// Copyright 2020 Compound Labs, Inc.
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Ctrl+f for OLD to see all the modifications.

pragma solidity ^0.8.10;

import {ERC20} from "ERC20.sol";
import {IVoteToken} from "IVoteToken.sol";

/**
 * @title VoteToken
 * @notice Custom token which tracks voting power for governance
 * @dev This is an abstraction of a fork of the Compound governance contract
 * VoteToken is used by TRU and stkTRU to allow tracking voting power
 * Checkpoints are created every time state is changed which record voting power
 * Inherits standard ERC20 behavior
 */
abstract contract VoteToken is ERC20, IVoteToken {
    bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
    bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");

    event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
    event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);

    function delegate(address delegatee) public override {
        return _delegate(msg.sender, delegatee);
    }

    /**
     * @dev Delegate votes using signature
     */
    function delegateBySig(
        address delegatee,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public override {
        require(block.timestamp <= expiry, "TrustToken::delegateBySig: signature expired");
        bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name())), block.chainid, address(this)));
        bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));
        bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
        address signatory = ecrecover(digest, v, r, s);
        require(signatory != address(0), "TrustToken::delegateBySig: invalid signature");
        require(nonce == nonces[signatory]++, "TrustToken::delegateBySig: invalid nonce");
        return _delegate(signatory, delegatee);
    }

    /**
     * @dev Get current voting power for an account
     * @param account Account to get voting power for
     * @return Voting power for an account
     */
    function getCurrentVotes(address account) public view virtual override returns (uint96) {
        uint32 nCheckpoints = numCheckpoints[account];
        return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
    }

    /**
     * @dev Get voting power at a specific block for an account
     * @param account Account to get voting power for
     * @param blockNumber Block to get voting power at
     * @return Voting power for an account at specific block
     */
    function getPriorVotes(address account, uint256 blockNumber) public view virtual override returns (uint96) {
        require(blockNumber < block.number, "TrustToken::getPriorVotes: not yet determined");

        uint32 checkpointsNumber = numCheckpoints[account];
        if (checkpointsNumber == 0) {
            return 0;
        }

        mapping(uint32 => Checkpoint) storage userCheckpoints = checkpoints[account];

        // First check most recent balance
        if (userCheckpoints[checkpointsNumber - 1].fromBlock <= blockNumber) {
            return userCheckpoints[checkpointsNumber - 1].votes;
        }

        // Next check implicit zero balance
        if (userCheckpoints[0].fromBlock > blockNumber) {
            return 0;
        }

        uint32 lower = 0;
        uint32 upper = checkpointsNumber - 1;
        while (upper > lower) {
            uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
            Checkpoint memory checkpoint = userCheckpoints[center];
            if (checkpoint.fromBlock == blockNumber) {
                return checkpoint.votes;
            } else if (checkpoint.fromBlock < blockNumber) {
                lower = center;
            } else {
                upper = center - 1;
            }
        }
        return userCheckpoints[lower].votes;
    }

    /**
     * @dev Internal function to delegate voting power to an account
     * @param delegator Account to delegate votes from
     * @param delegatee Account to delegate votes to
     */
    function _delegate(address delegator, address delegatee) internal {
        address currentDelegate = delegates[delegator];
        // OLD: uint96 delegatorBalance = balanceOf(delegator);
        uint96 delegatorBalance = safe96(_balanceOf(delegator), "StkTruToken: uint96 overflow");
        delegates[delegator] = delegatee;

        emit DelegateChanged(delegator, currentDelegate, delegatee);

        _moveDelegates(currentDelegate, delegatee, delegatorBalance);
    }

    function _balanceOf(address account) internal view virtual returns (uint256) {
        return balanceOf[account];
    }

    function _transfer(
        address _from,
        address _to,
        uint256 _value
    ) internal virtual override {
        super._transfer(_from, _to, _value);
        _moveDelegates(delegates[_from], delegates[_to], safe96(_value, "StkTruToken: uint96 overflow"));
    }

    function _mint(address account, uint256 amount) internal virtual override {
        super._mint(account, amount);
        _moveDelegates(address(0), delegates[account], safe96(amount, "StkTruToken: uint96 overflow"));
    }

    function _burn(address account, uint256 amount) internal virtual override {
        super._burn(account, amount);
        _moveDelegates(delegates[account], address(0), safe96(amount, "StkTruToken: uint96 overflow"));
    }

    /**
     * @dev internal function to move delegates between accounts
     */
    function _moveDelegates(
        address source,
        address destination,
        uint96 amount
    ) internal {
        if (source != destination && amount > 0) {
            if (source != address(0)) {
                uint32 sourceCheckpointsNumber = numCheckpoints[source];
                uint96 sourceOldVotes = sourceCheckpointsNumber > 0 ? checkpoints[source][sourceCheckpointsNumber - 1].votes : 0;
                uint96 sourceNewVotes = sourceOldVotes - amount;
                _writeCheckpoint(source, sourceCheckpointsNumber, sourceOldVotes, sourceNewVotes);
            }

            if (destination != address(0)) {
                uint32 destinationCheckpointsNumber = numCheckpoints[destination];
                uint96 destinationOldVotes = destinationCheckpointsNumber > 0
                    ? checkpoints[destination][destinationCheckpointsNumber - 1].votes
                    : 0;
                uint96 destinationNewVotes = destinationOldVotes + amount;
                _writeCheckpoint(destination, destinationCheckpointsNumber, destinationOldVotes, destinationNewVotes);
            }
        }
    }

    /**
     * @dev internal function to write a checkpoint for voting power
     */
    function _writeCheckpoint(
        address delegatee,
        uint32 nCheckpoints,
        uint96 oldVotes,
        uint96 newVotes
    ) internal {
        uint32 blockNumber = safe32(block.number, "TrustToken::_writeCheckpoint: block number exceeds 32 bits");

        if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
            checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
        } else {
            checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
            numCheckpoints[delegatee] = nCheckpoints + 1;
        }

        emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
    }

    /**
     * @dev internal function to convert from uint256 to uint32
     */
    function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {
        require(n < 2**32, errorMessage);
        return uint32(n);
    }

    /**
     * @dev internal function to convert from uint256 to uint96
     */
    function safe96(uint256 n, string memory errorMessage) internal pure returns (uint96) {
        require(n < 2**96, errorMessage);
        return uint96(n);
    }
}

File 13 of 15 : ITrueDistributor.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {IERC20} from "IERC20.sol";

interface ITrueDistributor {
    function trustToken() external view returns (IERC20);

    function farm() external view returns (address);

    function distribute() external;

    function nextDistribution() external view returns (uint256);

    function empty() external;
}

File 14 of 15 : StkClaimableContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {ProxyStorage} from "ProxyStorage.sol";

/**
 * @title ClaimableContract
 * @dev The ClaimableContract contract is a copy of Claimable Contract by Zeppelin.
 and provides basic authorization control functions. Inherits storage layout of
 ProxyStorage.
 */
contract StkClaimableContract is ProxyStorage {
    function owner() public view returns (address) {
        return owner_;
    }

    function pendingOwner() public view returns (address) {
        return pendingOwner_;
    }

    event OwnershipTransferred(address indexed currentOwner, address indexed newPendingOwner);
    event OwnershipClaimed(address indexed currentOwner, address indexed newOwner);

    /**
     * @dev sets the original `owner` of the contract to the sender
     * at construction. Must then be reinitialized
     */
    constructor() {
        owner_ = msg.sender;
        emit OwnershipClaimed(address(0), msg.sender);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(msg.sender == owner_, "only owner");
        _;
    }

    /**
     * @dev Modifier throws if called by any account other than the pendingOwner.
     */
    modifier onlyPendingOwner() {
        require(msg.sender == pendingOwner_);
        _;
    }

    /**
     * @dev Allows the current owner to set the pendingOwner address.
     * @param newOwner The address to transfer ownership to.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        pendingOwner_ = newOwner;
        emit OwnershipTransferred(owner_, newOwner);
    }

    /**
     * @dev Allows the pendingOwner address to finalize the transfer.
     */
    function claimOwnership() public onlyPendingOwner {
        address _pendingOwner = pendingOwner_;
        emit OwnershipClaimed(owner_, _pendingOwner);
        owner_ = _pendingOwner;
        pendingOwner_ = address(0);
    }
}

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

pragma solidity ^0.8.10;

/**
 * @dev interface to allow standard pause function
 */
interface IPauseableContract {
    function setPauseStatus(bool pauseStatus) external;
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"who","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountClaimed","type":"uint256"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"who","type":"address"},{"indexed":false,"internalType":"uint256","name":"endTime","type":"uint256"}],"name":"Cooldown","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newUnstakePeriodDuration","type":"uint256"}],"name":"CooldownTimeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"FeePayerWhitelistingStatusChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"}],"name":"FeeTokenChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator","type":"address"}],"name":"LiquidatorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"currentOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"currentOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newPendingOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"pauseStatus","type":"bool"}],"name":"PauseStatusChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Stake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"burntAmount","type":"uint256"}],"name":"Unstake","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newUnstakePeriodDuration","type":"uint256"}],"name":"UnstakePeriodDurationChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DELEGATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"checkpoints","outputs":[{"internalType":"uint32","name":"fromBlock","type":"uint32"},{"internalType":"uint96","name":"votes","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"extraStakeAmount","type":"uint256"}],"name":"claimRestake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"claimable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cooldown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cooldownTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"distributor","outputs":[{"internalType":"contract ITrueDistributor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"name":"farmRewards","outputs":[{"internalType":"uint256","name":"cumulativeRewardPerToken","type":"uint256"},{"internalType":"uint256","name":"totalClaimedRewards","type":"uint256"},{"internalType":"uint256","name":"totalFarmRewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getCurrentVotes","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPriorVotes","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initTotalSupplyCheckpoints","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initalized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_tru","type":"address"},{"internalType":"contract IERC20","name":"_tfusd","type":"address"},{"internalType":"contract IERC20","name":"_feeToken","type":"address"},{"internalType":"contract ITrueDistributor","name":"_distributor","type":"address"},{"internalType":"address","name":"_liquidator","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"nextDistributionIndex","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseStatus","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"}],"name":"payFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"receivedDuringCooldown","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rounding","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"scheduledRewards","outputs":[{"internalType":"uint64","name":"timestamp","type":"uint64"},{"internalType":"uint96","name":"amount","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newCooldownTime","type":"uint256"}],"name":"setCooldownTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_feeToken","type":"address"}],"name":"setFeeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_liquidator","type":"address"}],"name":"setLiquidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"status","type":"bool"}],"name":"setPauseStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"payer","type":"address"},{"internalType":"bool","name":"status","type":"bool"}],"name":"setPayerWhitelistingStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newUnstakePeriodDuration","type":"uint256"}],"name":"setUnstakePeriodDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"sortedScheduledRewardIndices","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakeSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"tfusd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tru","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"undistributedTfusdRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"unlockTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"unstakable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unstakePeriodDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistedFeePayers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50600580546001600160a01b031916339081179091556040516000907f935c51778db70bb63d01ba9b319c9b8f13d608e43d550a91d8896bd1368857df908290a36001600b8190556000805460ff19169091179055614e4e806100746000396000f3fe608060405234801561001057600080fd5b50600436106103d05760003560e01c8063782d6fe1116101ff578063c12675bb1161011a578063e30c3978116100ad578063f1127ed81161007c578063f1127ed814610935578063f2fde38b1461099c578063f5525afc146109af578063f64ff185146109c257600080fd5b8063e30c3978146108d7578063e7a324dc146108e8578063e92b87121461090f578063ef5cfb8c1461092257600080fd5b8063d4570c1c116100e9578063d4570c1c14610873578063d9eedb3814610886578063db8510cd14610899578063dd62ed3e146108ac57600080fd5b8063c12675bb14610824578063c2778b2814610844578063c38bb5371461084d578063c3cda5201461086057600080fd5b8063a1cf5cb611610192578063b319c6b711610161578063b319c6b7146107e2578063b4b5ea57146107eb578063b9fecb2c146107fe578063bfe109281461081157600080fd5b8063a1cf5cb6146107a1578063a457c2d7146107a9578063a694fc3a146107bc578063a9059cbb146107cf57600080fd5b80638da5cb5b116101ce5780638da5cb5b146107485780638e539e8c1461075957806395d89b411461076c5780639ff4bf161461078e57600080fd5b8063782d6fe1146106ea578063787a08a6146106fd5780637ecebe00146107055780637fcf7ef41461072557600080fd5b80633a46b1a8116102ef578063647846a5116102825780636fcfff45116102515780636fcfff451461067e5780636ff73201146106a457806370a08231146106b757806376b467b7146106d757600080fd5b8063647846a51461060357806364df21e51461061b57806368a74a6f14610665578063695bcc7f1461066e57600080fd5b80634e71e0c8116102be5780634e71e0c8146105b2578063587cde1e146105ba5780635be1a091146105e35780635c19a95c146105f057600080fd5b80633a46b1a8146105475780634046ebae14610572578063466916ca1461059d5780634e71d92d146105aa57600080fd5b806320606b70116103675780632e1a7d4d116103365780632e1a7d4d146105125780632e44040314610525578063313ce56714610525578063395093511461053457600080fd5b806320606b701461049d57806323b872dd146104c45780632b33734c146104d75780632e17de78146104ff57600080fd5b80631459457a116103a35780631459457a1461045b57806315cce2241461046e57806318160ddd146104815780631858f8d31461048a57600080fd5b806301c76f81146103d5578063034599c8146103ea57806306fdde0314610406578063095ea7b314610438575b600080fd5b6103e86103e336600461486a565b6109fd565b005b6103f360175481565b6040519081526020015b60405180910390f35b60408051808201909152600d81526c5374616b65642054727565466960981b60208201525b6040516103fd91906148b3565b61044b6104463660046148e6565b610a85565b60405190151581526020016103fd565b6103e8610469366004614912565b610a9c565b6103e861047c36600461486a565b610c8c565b6103f360015481565b6103e8610498366004614983565b610dbf565b6103f37f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681565b61044b6104d23660046149a5565b610fc7565b6104ea6104e53660046149e6565b61101a565b60405163ffffffff90911681526020016103fd565b6103e861050d3660046149e6565b611054565b6103e86105203660046149e6565b6113f2565b604051600881526020016103fd565b61044b6105423660046148e6565b611525565b61055a6105553660046148e6565b61155c565b6040516001600160601b0390911681526020016103fd565b600f54610585906001600160a01b031681565b6040516001600160a01b0390911681526020016103fd565b601b5461044b9060ff1681565b6103e8611568565b6103e861171a565b6105856105c836600461486a565b6007602052600090815260409020546001600160a01b031681565b60005461044b9060ff1681565b6103e86105fe36600461486a565b611799565b601b546105859061010090046001600160a01b031681565b61064a61062936600461486a565b60146020526000908152604090208054600382015460049092015490919083565b604080519384526020840192909252908201526060016103fd565b6103f360135481565b6018546104ea9063ffffffff1681565b6104ea61068c36600461486a565b60096020526000908152604090205463ffffffff1681565b6103e86106b23660046149e6565b6117a3565b6103f36106c536600461486a565b60026020526000908152604090205481565b6103f36106e536600461486a565b611856565b61055a6106f83660046148e6565b6118b1565b6103e8611918565b6103f361071336600461486a565b600a6020526000908152604090205481565b61044b61073336600461486a565b60196020526000908152604090205460ff1681565b6005546001600160a01b0316610585565b6103f36107673660046149e6565b61197c565b60408051808201909152600681526573746b54525560d01b602082015261042b565b6103f361079c36600461486a565b6119d8565b6103e8611a55565b61044b6107b73660046148e6565b611b73565b6103e86107ca3660046149e6565b611baa565b61044b6107dd3660046148e6565b611da5565b6103f360125481565b61055a6107f936600461486a565b611db2565b6103e861080c366004614a0d565b611ddd565b600e54610585906001600160a01b031681565b6103f361083236600461486a565b601a6020526000908152604090205481565b6103f360105481565b6103e861085b366004614a46565b611e6a565b6103e861086e366004614a63565b611ed5565b6103f3610881366004614ac5565b6121ce565b6103e86108943660046149e6565b612391565b6103e86108a73660046149e6565b6124b2565b6103f36108ba366004614ac5565b600360209081526000928352604080842090915290825290205481565b6006546001600160a01b0316610585565b6103f37fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf81565b600d54610585906001600160a01b031681565b6103e861093036600461486a565b61266f565b610978610943366004614af3565b600860209081526000928352604080842090915290825290205463ffffffff811690600160201b90046001600160601b031682565b6040805163ffffffff90931683526001600160601b039091166020830152016103fd565b6103e86109aa36600461486a565b6128cd565b600c54610585906001600160a01b031681565b6109d56109d03660046149e6565b612949565b6040805167ffffffffffffffff90931683526001600160601b039091166020830152016103fd565b6005546001600160a01b03163314610a305760405162461bcd60e51b8152600401610a2790614b2a565b60405180910390fd5b600f80546001600160a01b0319166001600160a01b0383169081179091556040519081527ffb3a1ab9431c6e1881d294e372cc5ea7578b8116f4f8c8dd0cbb8a47831a9510906020015b60405180910390a150565b6000610a92338484612985565b5060015b92915050565b60005460ff1615610aef5760405162461bcd60e51b815260206004820181905260248201527f53746b547275546f6b656e3a20416c726561647920696e697469616c697a65646044820152606401610a27565b6001600160a01b038516610b5a5760405162461bcd60e51b815260206004820152602c60248201527f53746b547275546f6b656e3a2054525520746f6b656e2061646472657373206d60448201526b0757374206e6f7420626520360a41b6064820152608401610a27565b6001600160a01b038416610bc75760405162461bcd60e51b815260206004820152602e60248201527f53746b547275546f6b656e3a20746655534420746f6b656e206164647265737360448201526d0206d757374206e6f7420626520360941b6064820152608401610a27565b6001600160a01b038316610bed5760405162461bcd60e51b8152600401610a2790614b4e565b600c80546001600160a01b03199081166001600160a01b0388811691909117909255600d80548216878416179055601b8054610100600160a81b03191661010087851602179055600e80548216858416179055600f8054909116918316919091179055621275006012556202a300601355610c66611a55565b5050600580546001600160a01b0319163317905550506000805460ff1916600117905550565b6005546001600160a01b03163314610cb65760405162461bcd60e51b8152600401610a2790614b2a565b6001600160a01b038116610cdc5760405162461bcd60e51b8152600401610a2790614b4e565b601b54610cf69061010090046001600160a01b0316612aaa565b15610d695760405162461bcd60e51b815260206004820152603d60248201527f53746b547275546f6b656e3a2043616e6e6f74207265706c616365206665652060448201527f746f6b656e207769746820756e6465726c79696e6720726577617264730000006064820152608401610a27565b601b8054610100600160a81b0319166101006001600160a01b038416908102919091179091556040519081527fcb8c2156aad54c05780be84f46fd46c30c34524ac9501a0824d09f51a48c453090602001610a7a565b3360009081526019602052604090205460ff16610e3c5760405162461bcd60e51b815260206004820152603560248201527f53746b547275546f6b656e3a2043616e2062652063616c6c6564206f6e6c792060448201527462792077686974656c69737465642070617965727360581b6064820152608401610a27565b67ffffffffffffffff811115610e945760405162461bcd60e51b815260206004820152601a60248201527f53746b547275546f6b656e3a2074696d65206f766572666c6f770000000000006044820152606401610a27565b6001600160601b03821115610eeb5760405162461bcd60e51b815260206004820152601c60248201527f53746b547275546f6b656e3a20616d6f756e74206f766572666c6f77000000006044820152606401610a27565b600d54610f03906001600160a01b0316333085612c09565b6000610f10600284614bc6565b905080601754610f209190614bda565b6017556040805180820190915267ffffffffffffffff8316815260169060208101610f4b8487614bf2565b6001600160601b039081169091528254600181018455600093845260208085208451920180549190940151909216600160401b026001600160a01b031990921667ffffffffffffffff90911617179055610fa483612c74565b9050610fc1816001601680549050610fbc9190614c09565b612d22565b50505050565b6000610fd4848484612e91565b6001600160a01b03841660009081526003602090815260408083203380855292529091205461100f91869161100a908690614bf2565b612985565b5060015b9392505050565b6015818154811061102a57600080fd5b9060005260206000209060089182820401919006600402915054906101000a900463ffffffff1681565b600e54604080516338b0789d60e01b815290516402540be400926001600160a01b0316916338b0789d9160048083019260209291908290030181865afa1580156110a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110c69190614c2e565b101580156111475750600e54604080516336e9332d60e01b8152905130926001600160a01b0316916336e9332d9160048083019260209291908290030181865afa158015611118573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113c9190614c47565b6001600160a01b0316145b156111b557600e60009054906101000a90046001600160a01b03166001600160a01b031663e4fc6b6d6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561119c57600080fd5b505af11580156111b0573d6000803e3d6000fd5b505050505b336111bf8161310e565b6002600b5414156112125760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a27565b6002600b55816112645760405162461bcd60e51b815260206004820152601d60248201527f53746b547275546f6b656e3a2043616e6e6f7420756e7374616b6520300000006044820152606401610a27565b8161126e336119d8565b10156112c65760405162461bcd60e51b815260206004820152602160248201527f53746b547275546f6b656e3a20496e73756666696369656e742062616c616e636044820152606560f81b6064820152608401610a27565b426112d033611856565b111561131e5760405162461bcd60e51b815260206004820152601e60248201527f53746b547275546f6b656e3a205374616b65206f6e20636f6f6c646f776e00006044820152606401610a27565b600c54611333906001600160a01b0316613199565b600d54611348906001600160a01b0316613199565b601b546113629061010090046001600160a01b0316613199565b6000600154601054846113759190614c64565b61137f9190614bc6565b905061138b33846131c0565b806010546113999190614bf2565b601055600c546113b3906001600160a01b031633836131d6565b60405183815233907f85082129d87b2fe11527cb1b3b7a520aeb5aa6913f88a3d8757fe40d1db02fdd9060200160405180910390a250506001600b5550565b600f546001600160a01b031633146114665760405162461bcd60e51b815260206004820152603160248201527f53746b547275546f6b656e3a2043616e2062652063616c6c6564206f6e6c7920604482015270313c903a3432903634b8bab4b230ba37b960791b6064820152608401610a27565b6010548111156114c75760405162461bcd60e51b815260206004820152602660248201527f53746b547275546f6b656e3a20496e73756666696369656e74207374616b6520604482015265737570706c7960d01b6064820152608401610a27565b806010546114d59190614bf2565b601055600f54600c546114f5916001600160a01b039182169116836131d6565b6040518181527f5b6b431d4476a211bb7d41c20d1aab9ae2321deee0d20be3d9fc9b1093fa6e3d90602001610a7a565b3360008181526003602090815260408083206001600160a01b03871684529091528120549091610a9291859061100a908690614bda565b60006110138383613206565b600e54604080516338b0789d60e01b815290516402540be400926001600160a01b0316916338b0789d9160048083019260209291908290030181865afa1580156115b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115da9190614c2e565b1015801561165b5750600e54604080516336e9332d60e01b8152905130926001600160a01b0316916336e9332d9160048083019260209291908290030181865afa15801561162c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116509190614c47565b6001600160a01b0316145b156116c957600e60009054906101000a90046001600160a01b03166001600160a01b031663e4fc6b6d6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156116b057600080fd5b505af11580156116c4573d6000803e3d6000fd5b505050505b336116d38161310e565b600c546116e8906001600160a01b0316613199565b600d546116fd906001600160a01b0316613199565b601b546117179061010090046001600160a01b0316613199565b50565b6006546001600160a01b0316331461173157600080fd5b6006546005546040516001600160a01b0392831692839216907f935c51778db70bb63d01ba9b319c9b8f13d608e43d550a91d8896bd1368857df90600090a3600580546001600160a01b039092166001600160a01b0319928316179055600680549091169055565b6117173382613441565b6005546001600160a01b031633146117cd5760405162461bcd60e51b8152600401610a2790614b2a565b63bbf81e008111156118215760405162461bcd60e51b815260206004820152601f60248201527f53746b547275546f6b656e3a20436f6f6c646f776e20746f6f206c61726765006044820152606401610a27565b60128190556040518181527f54e58dda72d0b1aedf1317f3df46d6b618e837707d31c3ec1b0ff018919bf14990602001610a7a565b6001600160a01b038116600090815260116020526040812054801580611895575042601354601254836118899190614bda565b6118939190614bda565b105b156118a4575060001992915050565b6012546110139082614bda565b6000806118be8484613206565b9050611910600154826001600160601b03166010546118dd9190614c64565b6118e79190614bc6565b6040518060400160405280601c8152602001600080516020614dbf8339815191528152506134da565b949350505050565b3360008181526011602090815260408083204290819055601a9092528220919091556012547f8a05f911d8ab7fc50fec37ef4ba7f9bfcb1a3c191c81dcd824ad0946c4e20d65916119699190614bda565b60405190815260200160405180910390a2565b60004382106119cd5760405162461bcd60e51b815260206004820152601f60248201527f4552433230566f7465733a20626c6f636b206e6f7420796574206d696e6564006044820152606401610a27565b610a96601c83613509565b6001600160a01b0381166000908152600260205260408120546000196119fd84611856565b1415611a095792915050565b6001600160a01b0383166000908152601a6020526040902054811015611a325750600092915050565b6001600160a01b0383166000908152601a60205260409020546110139082614bf2565b6005546001600160a01b03163314611a7f5760405162461bcd60e51b8152600401610a2790614b2a565b601c5415611af55760405162461bcd60e51b815260206004820152603b60248201527f5374616b65547275546f6b656e3a20546f74616c20737570706c79206368656360448201527f6b706f696e747320616c726561647920696e697469616c697a656400000000006064820152608401610a27565b601c6040518060400160405280611b0b436135c5565b63ffffffff168152602001611b2160015461362e565b6001600160601b03908116909152825460018101845560009384526020938490208351910180549490930151909116600160201b026001600160801b031990931663ffffffff90911617919091179055565b3360008181526003602090815260408083206001600160a01b03871684529091528120549091610a9291859061100a908690614bf2565b600e54604080516338b0789d60e01b815290516402540be400926001600160a01b0316916338b0789d9160048083019260209291908290030181865afa158015611bf8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1c9190614c2e565b10158015611c9d5750600e54604080516336e9332d60e01b8152905130926001600160a01b0316916336e9332d9160048083019260209291908290030181865afa158015611c6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c929190614c47565b6001600160a01b0316145b15611d0b57600e60009054906101000a90046001600160a01b03166001600160a01b031663e4fc6b6d6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611cf257600080fd5b505af1158015611d06573d6000803e3d6000fd5b505050505b33611d158161310e565b601b5460ff1615611d805760405162461bcd60e51b815260206004820152602f60248201527f53746b547275546f6b656e3a2043616e2062652063616c6c6564206f6e6c792060448201526e1dda195b881b9bdd081c185d5cd959608a1b6064820152608401610a27565b611d8982613696565b600c54611da1906001600160a01b0316333085612c09565b5050565b6000610a92338484612e91565b600080611dbe836137fb565b9050611013600154826001600160601b03166010546118dd9190614c64565b6005546001600160a01b03163314611e075760405162461bcd60e51b8152600401610a2790614b2a565b6001600160a01b038216600081815260196020908152604091829020805460ff19168515159081179091558251938452908301527f81edd49ff39678a23948277008fa4040a2bdaffea286bab3e17649c452119cd0910160405180910390a15050565b6005546001600160a01b03163314611e945760405162461bcd60e51b8152600401610a2790614b2a565b601b805460ff19168215159081179091556040519081527fef37df9624f797913e7585c7f7b5d004ba6704be3c64b0561c157728ccc8698590602001610a7a565b83421115611f3a5760405162461bcd60e51b815260206004820152602c60248201527f5472757374546f6b656e3a3a64656c656761746542795369673a207369676e6160448201526b1d1d5c9948195e1c1a5c995960a21b6064820152608401610a27565b60007f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866611f8760408051808201909152600d81526c5374616b65642054727565466960981b602082015290565b8051602091820120604080518084019490945283810191909152466060840152306080808501919091528151808503909101815260a0840182528051908301207fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf60c08501526001600160a01b038b1660e085015261010084018a90526101208085018a90528251808603909101815261014085019092528151919092012061190160f01b61016084015261016283018290526101828301819052909250906000906101a20160408051601f198184030181528282528051602091820120600080855291840180845281905260ff8a169284019290925260608301889052608083018790529092509060019060a0016020604051602081039080840390855afa1580156120b8573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166121305760405162461bcd60e51b815260206004820152602c60248201527f5472757374546f6b656e3a3a64656c656761746542795369673a20696e76616c60448201526b6964207369676e617475726560a01b6064820152608401610a27565b6001600160a01b0381166000908152600a6020526040812080549161215483614c83565b9190505589146121b75760405162461bcd60e51b815260206004820152602860248201527f5472757374546f6b656e3a3a64656c656761746542795369673a20696e76616c6044820152676964206e6f6e636560c01b6064820152608401610a27565b6121c1818b613441565b505050505b505050505050565b6001600160a01b038082166000818152601460205260408120600c54919390928492909116146121ff576000612276565b600e60009054906101000a90046001600160a01b03166001600160a01b03166338b0789d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612252573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122769190614c2e565b905060006c0c9f2c9cd04674edea4000000083600301546402540be4008410156122a15760006122a3565b835b6122ac88612aaa565b6122b69190614bda565b6122c09190614bda565b6122ca9190614c64565b905060008360040154826122de9190614bf2565b90506000600154826122f09190614bc6565b85546122fc9190614bda565b6001600160a01b03891660009081526001870160205260409020549091506c0c9f2c9cd04674edea40000000906123339083614bf2565b6001600160a01b038a166000908152600260205260409020546123569190614c64565b6123609190614bc6565b6001600160a01b03891660009081526002870160205260409020546123859190614bda565b98975050505050505050565b6005546001600160a01b031633146123bb5760405162461bcd60e51b8152600401610a2790614b2a565b6000811161241b5760405162461bcd60e51b815260206004820152602760248201527f53746b547275546f6b656e3a20556e7374616b6520706572696f642063616e6e60448201526606f7420626520360cc1b6064820152608401610a27565b63bbf81e0081111561247d5760405162461bcd60e51b815260206004820152602560248201527f53746b547275546f6b656e3a20556e7374616b6520706572696f6420746f6f206044820152646c6172676560d81b6064820152608401610a27565b60138190556040518181527f7b5074fc5cfe79cd171874bd284e9f6d867931e19df1c0d98600fcc8650ad92a90602001610a7a565b600e54604080516338b0789d60e01b815290516402540be400926001600160a01b0316916338b0789d9160048083019260209291908290030181865afa158015612500573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125249190614c2e565b101580156125a55750600e54604080516336e9332d60e01b8152905130926001600160a01b0316916336e9332d9160048083019260209291908290030181865afa158015612576573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061259a9190614c47565b6001600160a01b0316145b1561261357600e60009054906101000a90046001600160a01b03166001600160a01b031663e4fc6b6d6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156125fa57600080fd5b505af115801561260e573d6000803e3d6000fd5b505050505b3361261d8161310e565b600c546000908390612637906001600160a01b0316613878565b6126419190614bda565b905061264c81613696565b821561266a57600c5461266a906001600160a01b0316333086612c09565b505050565b600e54604080516338b0789d60e01b815290516402540be400926001600160a01b0316916338b0789d9160048083019260209291908290030181865afa1580156126bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126e19190614c2e565b101580156127625750600e54604080516336e9332d60e01b8152905130926001600160a01b0316916336e9332d9160048083019260209291908290030181865afa158015612733573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127579190614c47565b6001600160a01b0316145b156127d057600e60009054906101000a90046001600160a01b03166001600160a01b031663e4fc6b6d6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156127b757600080fd5b505af11580156127cb573d6000803e3d6000fd5b505050505b600c54339082906001600160a01b03808316911614806127fd5750600d546001600160a01b038281169116145b8061281a5750601b546001600160a01b0382811661010090920416145b156128325761282881613916565b61283281836139d8565b600d546001600160a01b038481169116148061285b5750600c546001600160a01b038481169116145b806128785750601b546001600160a01b0384811661010090920416145b6128c45760405162461bcd60e51b815260206004820152601f60248201527f546f6b656e206e6f7420737570706f7274656420666f722072657761726473006044820152606401610a27565b61266a83613199565b6005546001600160a01b031633146128f75760405162461bcd60e51b8152600401610a2790614b2a565b600680546001600160a01b0319166001600160a01b03838116918217909255600554604051919216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6016818154811061295957600080fd5b60009182526020909120015467ffffffffffffffff81169150600160401b90046001600160601b031682565b6001600160a01b0383166129e75760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610a27565b6001600160a01b038216612a485760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610a27565b6001600160a01b0383811660008181526003602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b600c546000906001600160a01b0383811691161415612b39576010546040516370a0823160e01b81523060048201526001600160a01b038416906370a08231906024015b602060405180830381865afa158015612b0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b2f9190614c2e565b610a969190614bf2565b600d546001600160a01b0383811691161415612b7e576017546040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401612aee565b601b546001600160a01b03838116610100909204161415612c01576040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015612bdd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a969190614c2e565b506000919050565b6040516001600160a01b0380851660248301528316604482015260648101829052610fc19085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613acb565b60155460185463ffffffff16905b808263ffffffff161015612d1c5782601660158463ffffffff1681548110612cac57612cac614c9e565b90600052602060002090600891828204019190066004029054906101000a900463ffffffff1663ffffffff1681548110612ce857612ce8614c9e565b60009182526020909120015467ffffffffffffffff161115612d0a5750919050565b81612d1481614cb4565b925050612c82565b50919050565b601580546001808201835560008381527f55f448fdea98c4d29eb340757ef0a66cd03dbb9538908a6a81d96026b71ec4756008840401805460079094166004026101000a63ffffffff021990931690925591549091612d8091614c09565b90505b8263ffffffff168163ffffffff161115612e3f576015612da4600183614c09565b63ffffffff1681548110612dba57612dba614c9e565b90600052602060002090600891828204019190066004029054906101000a900463ffffffff1660158263ffffffff1681548110612df957612df9614c9e565b90600052602060002090600891828204019190066004026101000a81548163ffffffff021916908363ffffffff1602179055508080612e3790614cd8565b915050612d83565b508060158363ffffffff1681548110612e5a57612e5a614c9e565b90600052602060002090600891828204019190066004026101000a81548163ffffffff021916908363ffffffff1602179055505050565b600e54604080516338b0789d60e01b815290516402540be400926001600160a01b0316916338b0789d9160048083019260209291908290030181865afa158015612edf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f039190614c2e565b10158015612f845750600e54604080516336e9332d60e01b8152905130926001600160a01b0316916336e9332d9160048083019260209291908290030181865afa158015612f55573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f799190614c47565b6001600160a01b0316145b15612ff257600e60009054906101000a90046001600160a01b03166001600160a01b031663e4fc6b6d6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612fd957600080fd5b505af1158015612fed573d6000803e3d6000fd5b505050505b82612ffc8161310e565b600c54613012906001600160a01b0316846139d8565b600d54613028906001600160a01b0316846139d8565b601b546130439061010090046001600160a01b0316846139d8565b60001961304f84611856565b14613092576001600160a01b0383166000908152601a6020526040902054613078908390614bda565b6001600160a01b0384166000908152601a60205260409020555b60001961309e85611856565b14613103576001600160a01b0384166000908152601a60205260409020546130c69083613b9d565b6001600160a01b0385166000908152601a60205260409020546130e99190614bf2565b6001600160a01b0385166000908152601a60205260409020555b610fc1848484613bb3565b600c54613123906001600160a01b0316613916565b600c54613139906001600160a01b0316826139d8565b600d5461314e906001600160a01b0316613916565b600d54613164906001600160a01b0316826139d8565b601b5461317e9061010090046001600160a01b0316613916565b601b546117179061010090046001600160a01b0316826139d8565b60006131a482613878565b90508015611da157611da16001600160a01b03831633836131d6565b6131ca8282613c24565b610fc1613c9982613ca5565b6040516001600160a01b03831660248201526044810182905261266a90849063a9059cbb60e01b90606401612c3d565b600043821061326d5760405162461bcd60e51b815260206004820152602d60248201527f5472757374546f6b656e3a3a6765745072696f72566f7465733a206e6f74207960448201526c195d0819195d195c9b5a5b9959609a1b6064820152608401610a27565b6001600160a01b03831660009081526009602052604090205463ffffffff168061329b576000915050610a96565b6001600160a01b038416600090815260086020526040812090849082906132c3600186614c09565b63ffffffff90811682526020820192909252604001600020541611613321578060006132f0600185614c09565b63ffffffff168152602081019190915260400160002054600160201b90046001600160601b03169250610a96915050565b60008080526020829052604090205463ffffffff1684101561334857600092505050610a96565b600080613356600185614c09565b90505b8163ffffffff168163ffffffff161115613412576000600261337b8484614c09565b6133859190614cf8565b61338f9083614c09565b63ffffffff81811660009081526020878152604091829020825180840190935254928316808352600160201b9093046001600160601b0316908201529192508814156133e657602001519550610a96945050505050565b805163ffffffff168811156133fd5781935061340b565b613408600183614c09565b92505b5050613359565b5063ffffffff1660009081526020919091526040902054600160201b90046001600160601b0316949350505050565b6001600160a01b038281166000908152600760209081526040808320546002909252822054921691613472906118e7565b6001600160a01b0385811660008181526007602052604080822080546001600160a01b031916898616908117909155905194955093928616927f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a4610fc1828483613e3e565b600081600160601b84106135015760405162461bcd60e51b8152600401610a2791906148b3565b509192915050565b8154600090815b8181101561356d5760006135248284613fb8565b90508486828154811061353957613539614c9e565b60009182526020909120015463ffffffff16111561355957809250613567565b613564816001614bda565b91505b50613510565b81156135b0578461357f600184614bf2565b8154811061358f5761358f614c9e565b600091825260209091200154600160201b90046001600160601b03166135b3565b60005b6001600160601b031695945050505050565b600063ffffffff82111561362a5760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610a27565b5090565b60006001600160601b0382111561362a5760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201526536206269747360d01b6064820152608401610a27565b600081116136e65760405162461bcd60e51b815260206004820152601b60248201527f53746b547275546f6b656e3a2043616e6e6f74207374616b65203000000000006044820152606401610a27565b336000908152601160205260409020541580159061372d57506013546012543360009081526011602052604090205442929161372191614bda565b61372b9190614bda565b115b1561373a5761373a611918565b336000908152600760205260409020546001600160a01b03166137785733600081815260076020526040902080546001600160a01b03191690911790555b60006010546000146137a3576010546001546137949084614c64565b61379e9190614bc6565b6137a5565b815b90506137b13382613fd3565b816010546137bf9190614bda565b60105560405182815233907febedb8b3c678666e7f36970bc8f57abf6d8fa2e828c0da91ea5b75bf68ed101a9060200160405180910390a25050565b6001600160a01b03811660009081526009602052604081205463ffffffff1680613826576000611013565b6001600160a01b03831660009081526008602052604081209061384a600184614c09565b63ffffffff168152602081019190915260400160002054600160201b90046001600160601b03169392505050565b6001600160a01b03811660009081526014602090815260408083203384526002810190925282205460038201546138b0908290614bda565b600383015533600081815260028401602052604080822091909155516001600160a01b03861691907f70eb43c4a8ae8c40502dcf22436c509c28d6ff421cf07c491be56984bd987068906139079085815260200190565b60405180910390a39392505050565b600d546001600160a01b038281169116141561393457613934613fe9565b6001600160a01b038116600090815260146020526040812060038101549091906c0c9f2c9cd04674edea400000009061396c85612aaa565b6139769190614bda565b6139809190614c64565b9050816004015481141561399357505050565b60008260040154826139a59190614bf2565b6004840183905560015490915015610fc1576001546139c49082614bc6565b83546139d09190614bda565b835550505050565b6001600160a01b0380831660009081526014602090815260408083209385168352600290915290205415613aa9576001600160a01b038216600090815260018201602052604090205481546c0c9f2c9cd04674edea4000000091613a3b91614bf2565b6001600160a01b038416600090815260026020526040902054613a5e9190614c64565b613a689190614bc6565b6001600160a01b0383166000908152600283016020526040902054613a8d9190614bda565b6001600160a01b03831660009081526002830160205260409020555b80546001600160a01b0390921660009081526001909101602052604090205550565b6000613b20826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661411f9092919063ffffffff16565b80519091501561266a5780806020019051810190613b3e9190614d1b565b61266a5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a27565b6000818310613bac5781611013565b5090919050565b613bbe83838361412e565b6001600160a01b03808416600090815260076020908152604080832054868516845292819020548151808301909252601c8252600080516020614dbf8339815191529282019290925261266a93928316929190911690613c1f9085906134da565b613e3e565b613c2e828261429c565b611da160076000846001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a90046001600160a01b03166000613c1f846040518060400160405280601c8152602001600080516020614dbf8339815191528152506134da565b60006110138284614bf2565b601c54600090819080613d205760405162461bcd60e51b815260206004820152603760248201527f5374616b65547275546f6b656e3a20746f74616c20737570706c79206368656360448201527f6b706f696e7473206e6f7420696e697469616c697a65640000000000000000006064820152608401610a27565b6000601c613d2f600184614bf2565b81548110613d3f57613d3f614c9e565b60009182526020909120018054600160201b90046001600160601b031694509050613d6e848663ffffffff8916565b815490935063ffffffff16431415613dba57613d898361362e565b81546001600160601b0391909116600160201b026fffffffffffffffffffffffff0000000019909116178155613e35565b601c6040518060400160405280613dd0436135c5565b63ffffffff168152602001613de48661362e565b6001600160601b03908116909152825460018101845560009384526020938490208351910180549490930151909116600160201b026001600160801b031990931663ffffffff909116179190911790555b50509250929050565b816001600160a01b0316836001600160a01b031614158015613e6957506000816001600160601b0316115b1561266a576001600160a01b03831615613f15576001600160a01b03831660009081526009602052604081205463ffffffff169081613ea9576000613ef5565b6001600160a01b038516600090815260086020526040812090613ecd600185614c09565b63ffffffff168152602081019190915260400160002054600160201b90046001600160601b03165b90506000613f038483614d38565b9050613f1186848484614390565b5050505b6001600160a01b0382161561266a576001600160a01b03821660009081526009602052604081205463ffffffff169081613f50576000613f9c565b6001600160a01b038416600090815260086020526040812090613f74600185614c09565b63ffffffff168152602081019190915260400160002054600160201b90046001600160601b03165b90506000613faa8483614d58565b90506121c685848484614390565b6000613fc76002848418614bc6565b61101390848416614bda565b613fdd828261457f565b610fc16145dc82613ca5565b60185463ffffffff166000613fff602083614d83565b60165490915060009063ffffffff83161061401c5760165461401e565b815b6017549091505b8163ffffffff168463ffffffff1610156140eb576000601660158663ffffffff168154811061405657614056614c9e565b90600052602060002090600891828204019190066004029054906101000a900463ffffffff1663ffffffff168154811061409257614092614c9e565b600091825260209091200180549091504267ffffffffffffffff909116106140ba57506140eb565b80546140d690600160401b90046001600160601b031683614bf2565b9150846140e281614cb4565b95505050614025565b601781905560185463ffffffff858116911614610fc1576018805463ffffffff861663ffffffff1990911617905550505050565b606061191084846000856145e8565b6001600160a01b0383166141925760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610a27565b6001600160a01b0382166141f45760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610a27565b6001600160a01b038316600090815260026020526040902054614218908290614bf2565b6001600160a01b038085166000908152600260205260408082209390935590841681522054614248908290614bda565b6001600160a01b0380841660008181526002602052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612a9d9085815260200190565b6001600160a01b0382166142fc5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610a27565b6001600160a01b038216600090815260026020526040902054614320908290614bf2565b6001600160a01b038316600090815260026020526040902055600154614347908290614bf2565b6001556040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b60006143b4436040518060600160405280603a8152602001614ddf603a9139614719565b905060008463ffffffff1611801561440e57506001600160a01b038516600090815260086020526040812063ffffffff8316916143f2600188614c09565b63ffffffff908116825260208201929092526040016000205416145b15614482576001600160a01b03851660009081526008602052604081208391614438600188614c09565b63ffffffff168152602081019190915260400160002080546001600160601b0392909216600160201b026fffffffffffffffffffffffff000000001990921691909117905561452a565b60408051808201825263ffffffff80841682526001600160601b0380861660208085019182526001600160a01b038b166000908152600882528681208b8616825290915294909420925183549451909116600160201b026001600160801b03199094169116179190911790556144f9846001614d83565b6001600160a01b0386166000908152600960205260409020805463ffffffff191663ffffffff929092169190911790555b604080516001600160601b038086168252841660208201526001600160a01b038716917fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a724910160405180910390a25050505050565b6145898282614740565b6001600160a01b038083166000908152600760209081526040808320548151808301909252601c8252600080516020614dbf83398151915292820192909252611da1939190911690613c1f9085906134da565b60006110138284614bda565b6060824710156146495760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a27565b6001600160a01b0385163b6146a05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a27565b600080866001600160a01b031685876040516146bc9190614da2565b60006040518083038185875af1925050503d80600081146146f9576040519150601f19603f3d011682016040523d82523d6000602084013e6146fe565b606091505b509150915061470e82828661481c565b979650505050505050565b600081600160201b84106135015760405162461bcd60e51b8152600401610a2791906148b3565b6001600160a01b0382166147965760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610a27565b806001546147a49190614bda565b6001556001600160a01b0382166000908152600260205260409020546147cb908290614bda565b6001600160a01b0383166000818152600260205260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906143849085815260200190565b6060831561482b575081611013565b82511561483b5782518084602001fd5b8160405162461bcd60e51b8152600401610a2791906148b3565b6001600160a01b038116811461171757600080fd5b60006020828403121561487c57600080fd5b813561101381614855565b60005b838110156148a257818101518382015260200161488a565b83811115610fc15750506000910152565b60208152600082518060208401526148d2816040850160208701614887565b601f01601f19169190910160400192915050565b600080604083850312156148f957600080fd5b823561490481614855565b946020939093013593505050565b600080600080600060a0868803121561492a57600080fd5b853561493581614855565b9450602086013561494581614855565b9350604086013561495581614855565b9250606086013561496581614855565b9150608086013561497581614855565b809150509295509295909350565b6000806040838503121561499657600080fd5b50508035926020909101359150565b6000806000606084860312156149ba57600080fd5b83356149c581614855565b925060208401356149d581614855565b929592945050506040919091013590565b6000602082840312156149f857600080fd5b5035919050565b801515811461171757600080fd5b60008060408385031215614a2057600080fd5b8235614a2b81614855565b91506020830135614a3b816149ff565b809150509250929050565b600060208284031215614a5857600080fd5b8135611013816149ff565b60008060008060008060c08789031215614a7c57600080fd5b8635614a8781614855565b95506020870135945060408701359350606087013560ff81168114614aab57600080fd5b9598949750929560808101359460a0909101359350915050565b60008060408385031215614ad857600080fd5b8235614ae381614855565b91506020830135614a3b81614855565b60008060408385031215614b0657600080fd5b8235614b1181614855565b9150602083013563ffffffff81168114614a3b57600080fd5b6020808252600a908201526937b7363c9037bbb732b960b11b604082015260600190565b6020808252602c908201527f53746b547275546f6b656e3a2066656520746f6b656e2061646472657373206d60408201526b0757374206e6f7420626520360a41b606082015260800190565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600082614bd557614bd5614b9a565b500490565b60008219821115614bed57614bed614bb0565b500190565b600082821015614c0457614c04614bb0565b500390565b600063ffffffff83811690831681811015614c2657614c26614bb0565b039392505050565b600060208284031215614c4057600080fd5b5051919050565b600060208284031215614c5957600080fd5b815161101381614855565b6000816000190483118215151615614c7e57614c7e614bb0565b500290565b6000600019821415614c9757614c97614bb0565b5060010190565b634e487b7160e01b600052603260045260246000fd5b600063ffffffff80831681811415614cce57614cce614bb0565b6001019392505050565b600063ffffffff821680614cee57614cee614bb0565b6000190192915050565b600063ffffffff80841680614d0f57614d0f614b9a565b92169190910492915050565b600060208284031215614d2d57600080fd5b8151611013816149ff565b60006001600160601b0383811690831681811015614c2657614c26614bb0565b60006001600160601b03808316818516808303821115614d7a57614d7a614bb0565b01949350505050565b600063ffffffff808316818516808303821115614d7a57614d7a614bb0565b60008251614db4818460208701614887565b919091019291505056fe53746b547275546f6b656e3a2075696e743936206f766572666c6f77000000005472757374546f6b656e3a3a5f7772697465436865636b706f696e743a20626c6f636b206e756d62657220657863656564732033322062697473a264697066735822122003918d5029d611c8205a1fa8e65322f986c8b47d0f5441f723028b3532eed5cd64736f6c634300080a0033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103d05760003560e01c8063782d6fe1116101ff578063c12675bb1161011a578063e30c3978116100ad578063f1127ed81161007c578063f1127ed814610935578063f2fde38b1461099c578063f5525afc146109af578063f64ff185146109c257600080fd5b8063e30c3978146108d7578063e7a324dc146108e8578063e92b87121461090f578063ef5cfb8c1461092257600080fd5b8063d4570c1c116100e9578063d4570c1c14610873578063d9eedb3814610886578063db8510cd14610899578063dd62ed3e146108ac57600080fd5b8063c12675bb14610824578063c2778b2814610844578063c38bb5371461084d578063c3cda5201461086057600080fd5b8063a1cf5cb611610192578063b319c6b711610161578063b319c6b7146107e2578063b4b5ea57146107eb578063b9fecb2c146107fe578063bfe109281461081157600080fd5b8063a1cf5cb6146107a1578063a457c2d7146107a9578063a694fc3a146107bc578063a9059cbb146107cf57600080fd5b80638da5cb5b116101ce5780638da5cb5b146107485780638e539e8c1461075957806395d89b411461076c5780639ff4bf161461078e57600080fd5b8063782d6fe1146106ea578063787a08a6146106fd5780637ecebe00146107055780637fcf7ef41461072557600080fd5b80633a46b1a8116102ef578063647846a5116102825780636fcfff45116102515780636fcfff451461067e5780636ff73201146106a457806370a08231146106b757806376b467b7146106d757600080fd5b8063647846a51461060357806364df21e51461061b57806368a74a6f14610665578063695bcc7f1461066e57600080fd5b80634e71e0c8116102be5780634e71e0c8146105b2578063587cde1e146105ba5780635be1a091146105e35780635c19a95c146105f057600080fd5b80633a46b1a8146105475780634046ebae14610572578063466916ca1461059d5780634e71d92d146105aa57600080fd5b806320606b70116103675780632e1a7d4d116103365780632e1a7d4d146105125780632e44040314610525578063313ce56714610525578063395093511461053457600080fd5b806320606b701461049d57806323b872dd146104c45780632b33734c146104d75780632e17de78146104ff57600080fd5b80631459457a116103a35780631459457a1461045b57806315cce2241461046e57806318160ddd146104815780631858f8d31461048a57600080fd5b806301c76f81146103d5578063034599c8146103ea57806306fdde0314610406578063095ea7b314610438575b600080fd5b6103e86103e336600461486a565b6109fd565b005b6103f360175481565b6040519081526020015b60405180910390f35b60408051808201909152600d81526c5374616b65642054727565466960981b60208201525b6040516103fd91906148b3565b61044b6104463660046148e6565b610a85565b60405190151581526020016103fd565b6103e8610469366004614912565b610a9c565b6103e861047c36600461486a565b610c8c565b6103f360015481565b6103e8610498366004614983565b610dbf565b6103f37f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681565b61044b6104d23660046149a5565b610fc7565b6104ea6104e53660046149e6565b61101a565b60405163ffffffff90911681526020016103fd565b6103e861050d3660046149e6565b611054565b6103e86105203660046149e6565b6113f2565b604051600881526020016103fd565b61044b6105423660046148e6565b611525565b61055a6105553660046148e6565b61155c565b6040516001600160601b0390911681526020016103fd565b600f54610585906001600160a01b031681565b6040516001600160a01b0390911681526020016103fd565b601b5461044b9060ff1681565b6103e8611568565b6103e861171a565b6105856105c836600461486a565b6007602052600090815260409020546001600160a01b031681565b60005461044b9060ff1681565b6103e86105fe36600461486a565b611799565b601b546105859061010090046001600160a01b031681565b61064a61062936600461486a565b60146020526000908152604090208054600382015460049092015490919083565b604080519384526020840192909252908201526060016103fd565b6103f360135481565b6018546104ea9063ffffffff1681565b6104ea61068c36600461486a565b60096020526000908152604090205463ffffffff1681565b6103e86106b23660046149e6565b6117a3565b6103f36106c536600461486a565b60026020526000908152604090205481565b6103f36106e536600461486a565b611856565b61055a6106f83660046148e6565b6118b1565b6103e8611918565b6103f361071336600461486a565b600a6020526000908152604090205481565b61044b61073336600461486a565b60196020526000908152604090205460ff1681565b6005546001600160a01b0316610585565b6103f36107673660046149e6565b61197c565b60408051808201909152600681526573746b54525560d01b602082015261042b565b6103f361079c36600461486a565b6119d8565b6103e8611a55565b61044b6107b73660046148e6565b611b73565b6103e86107ca3660046149e6565b611baa565b61044b6107dd3660046148e6565b611da5565b6103f360125481565b61055a6107f936600461486a565b611db2565b6103e861080c366004614a0d565b611ddd565b600e54610585906001600160a01b031681565b6103f361083236600461486a565b601a6020526000908152604090205481565b6103f360105481565b6103e861085b366004614a46565b611e6a565b6103e861086e366004614a63565b611ed5565b6103f3610881366004614ac5565b6121ce565b6103e86108943660046149e6565b612391565b6103e86108a73660046149e6565b6124b2565b6103f36108ba366004614ac5565b600360209081526000928352604080842090915290825290205481565b6006546001600160a01b0316610585565b6103f37fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf81565b600d54610585906001600160a01b031681565b6103e861093036600461486a565b61266f565b610978610943366004614af3565b600860209081526000928352604080842090915290825290205463ffffffff811690600160201b90046001600160601b031682565b6040805163ffffffff90931683526001600160601b039091166020830152016103fd565b6103e86109aa36600461486a565b6128cd565b600c54610585906001600160a01b031681565b6109d56109d03660046149e6565b612949565b6040805167ffffffffffffffff90931683526001600160601b039091166020830152016103fd565b6005546001600160a01b03163314610a305760405162461bcd60e51b8152600401610a2790614b2a565b60405180910390fd5b600f80546001600160a01b0319166001600160a01b0383169081179091556040519081527ffb3a1ab9431c6e1881d294e372cc5ea7578b8116f4f8c8dd0cbb8a47831a9510906020015b60405180910390a150565b6000610a92338484612985565b5060015b92915050565b60005460ff1615610aef5760405162461bcd60e51b815260206004820181905260248201527f53746b547275546f6b656e3a20416c726561647920696e697469616c697a65646044820152606401610a27565b6001600160a01b038516610b5a5760405162461bcd60e51b815260206004820152602c60248201527f53746b547275546f6b656e3a2054525520746f6b656e2061646472657373206d60448201526b0757374206e6f7420626520360a41b6064820152608401610a27565b6001600160a01b038416610bc75760405162461bcd60e51b815260206004820152602e60248201527f53746b547275546f6b656e3a20746655534420746f6b656e206164647265737360448201526d0206d757374206e6f7420626520360941b6064820152608401610a27565b6001600160a01b038316610bed5760405162461bcd60e51b8152600401610a2790614b4e565b600c80546001600160a01b03199081166001600160a01b0388811691909117909255600d80548216878416179055601b8054610100600160a81b03191661010087851602179055600e80548216858416179055600f8054909116918316919091179055621275006012556202a300601355610c66611a55565b5050600580546001600160a01b0319163317905550506000805460ff1916600117905550565b6005546001600160a01b03163314610cb65760405162461bcd60e51b8152600401610a2790614b2a565b6001600160a01b038116610cdc5760405162461bcd60e51b8152600401610a2790614b4e565b601b54610cf69061010090046001600160a01b0316612aaa565b15610d695760405162461bcd60e51b815260206004820152603d60248201527f53746b547275546f6b656e3a2043616e6e6f74207265706c616365206665652060448201527f746f6b656e207769746820756e6465726c79696e6720726577617264730000006064820152608401610a27565b601b8054610100600160a81b0319166101006001600160a01b038416908102919091179091556040519081527fcb8c2156aad54c05780be84f46fd46c30c34524ac9501a0824d09f51a48c453090602001610a7a565b3360009081526019602052604090205460ff16610e3c5760405162461bcd60e51b815260206004820152603560248201527f53746b547275546f6b656e3a2043616e2062652063616c6c6564206f6e6c792060448201527462792077686974656c69737465642070617965727360581b6064820152608401610a27565b67ffffffffffffffff811115610e945760405162461bcd60e51b815260206004820152601a60248201527f53746b547275546f6b656e3a2074696d65206f766572666c6f770000000000006044820152606401610a27565b6001600160601b03821115610eeb5760405162461bcd60e51b815260206004820152601c60248201527f53746b547275546f6b656e3a20616d6f756e74206f766572666c6f77000000006044820152606401610a27565b600d54610f03906001600160a01b0316333085612c09565b6000610f10600284614bc6565b905080601754610f209190614bda565b6017556040805180820190915267ffffffffffffffff8316815260169060208101610f4b8487614bf2565b6001600160601b039081169091528254600181018455600093845260208085208451920180549190940151909216600160401b026001600160a01b031990921667ffffffffffffffff90911617179055610fa483612c74565b9050610fc1816001601680549050610fbc9190614c09565b612d22565b50505050565b6000610fd4848484612e91565b6001600160a01b03841660009081526003602090815260408083203380855292529091205461100f91869161100a908690614bf2565b612985565b5060015b9392505050565b6015818154811061102a57600080fd5b9060005260206000209060089182820401919006600402915054906101000a900463ffffffff1681565b600e54604080516338b0789d60e01b815290516402540be400926001600160a01b0316916338b0789d9160048083019260209291908290030181865afa1580156110a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110c69190614c2e565b101580156111475750600e54604080516336e9332d60e01b8152905130926001600160a01b0316916336e9332d9160048083019260209291908290030181865afa158015611118573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113c9190614c47565b6001600160a01b0316145b156111b557600e60009054906101000a90046001600160a01b03166001600160a01b031663e4fc6b6d6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561119c57600080fd5b505af11580156111b0573d6000803e3d6000fd5b505050505b336111bf8161310e565b6002600b5414156112125760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a27565b6002600b55816112645760405162461bcd60e51b815260206004820152601d60248201527f53746b547275546f6b656e3a2043616e6e6f7420756e7374616b6520300000006044820152606401610a27565b8161126e336119d8565b10156112c65760405162461bcd60e51b815260206004820152602160248201527f53746b547275546f6b656e3a20496e73756666696369656e742062616c616e636044820152606560f81b6064820152608401610a27565b426112d033611856565b111561131e5760405162461bcd60e51b815260206004820152601e60248201527f53746b547275546f6b656e3a205374616b65206f6e20636f6f6c646f776e00006044820152606401610a27565b600c54611333906001600160a01b0316613199565b600d54611348906001600160a01b0316613199565b601b546113629061010090046001600160a01b0316613199565b6000600154601054846113759190614c64565b61137f9190614bc6565b905061138b33846131c0565b806010546113999190614bf2565b601055600c546113b3906001600160a01b031633836131d6565b60405183815233907f85082129d87b2fe11527cb1b3b7a520aeb5aa6913f88a3d8757fe40d1db02fdd9060200160405180910390a250506001600b5550565b600f546001600160a01b031633146114665760405162461bcd60e51b815260206004820152603160248201527f53746b547275546f6b656e3a2043616e2062652063616c6c6564206f6e6c7920604482015270313c903a3432903634b8bab4b230ba37b960791b6064820152608401610a27565b6010548111156114c75760405162461bcd60e51b815260206004820152602660248201527f53746b547275546f6b656e3a20496e73756666696369656e74207374616b6520604482015265737570706c7960d01b6064820152608401610a27565b806010546114d59190614bf2565b601055600f54600c546114f5916001600160a01b039182169116836131d6565b6040518181527f5b6b431d4476a211bb7d41c20d1aab9ae2321deee0d20be3d9fc9b1093fa6e3d90602001610a7a565b3360008181526003602090815260408083206001600160a01b03871684529091528120549091610a9291859061100a908690614bda565b60006110138383613206565b600e54604080516338b0789d60e01b815290516402540be400926001600160a01b0316916338b0789d9160048083019260209291908290030181865afa1580156115b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115da9190614c2e565b1015801561165b5750600e54604080516336e9332d60e01b8152905130926001600160a01b0316916336e9332d9160048083019260209291908290030181865afa15801561162c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116509190614c47565b6001600160a01b0316145b156116c957600e60009054906101000a90046001600160a01b03166001600160a01b031663e4fc6b6d6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156116b057600080fd5b505af11580156116c4573d6000803e3d6000fd5b505050505b336116d38161310e565b600c546116e8906001600160a01b0316613199565b600d546116fd906001600160a01b0316613199565b601b546117179061010090046001600160a01b0316613199565b50565b6006546001600160a01b0316331461173157600080fd5b6006546005546040516001600160a01b0392831692839216907f935c51778db70bb63d01ba9b319c9b8f13d608e43d550a91d8896bd1368857df90600090a3600580546001600160a01b039092166001600160a01b0319928316179055600680549091169055565b6117173382613441565b6005546001600160a01b031633146117cd5760405162461bcd60e51b8152600401610a2790614b2a565b63bbf81e008111156118215760405162461bcd60e51b815260206004820152601f60248201527f53746b547275546f6b656e3a20436f6f6c646f776e20746f6f206c61726765006044820152606401610a27565b60128190556040518181527f54e58dda72d0b1aedf1317f3df46d6b618e837707d31c3ec1b0ff018919bf14990602001610a7a565b6001600160a01b038116600090815260116020526040812054801580611895575042601354601254836118899190614bda565b6118939190614bda565b105b156118a4575060001992915050565b6012546110139082614bda565b6000806118be8484613206565b9050611910600154826001600160601b03166010546118dd9190614c64565b6118e79190614bc6565b6040518060400160405280601c8152602001600080516020614dbf8339815191528152506134da565b949350505050565b3360008181526011602090815260408083204290819055601a9092528220919091556012547f8a05f911d8ab7fc50fec37ef4ba7f9bfcb1a3c191c81dcd824ad0946c4e20d65916119699190614bda565b60405190815260200160405180910390a2565b60004382106119cd5760405162461bcd60e51b815260206004820152601f60248201527f4552433230566f7465733a20626c6f636b206e6f7420796574206d696e6564006044820152606401610a27565b610a96601c83613509565b6001600160a01b0381166000908152600260205260408120546000196119fd84611856565b1415611a095792915050565b6001600160a01b0383166000908152601a6020526040902054811015611a325750600092915050565b6001600160a01b0383166000908152601a60205260409020546110139082614bf2565b6005546001600160a01b03163314611a7f5760405162461bcd60e51b8152600401610a2790614b2a565b601c5415611af55760405162461bcd60e51b815260206004820152603b60248201527f5374616b65547275546f6b656e3a20546f74616c20737570706c79206368656360448201527f6b706f696e747320616c726561647920696e697469616c697a656400000000006064820152608401610a27565b601c6040518060400160405280611b0b436135c5565b63ffffffff168152602001611b2160015461362e565b6001600160601b03908116909152825460018101845560009384526020938490208351910180549490930151909116600160201b026001600160801b031990931663ffffffff90911617919091179055565b3360008181526003602090815260408083206001600160a01b03871684529091528120549091610a9291859061100a908690614bf2565b600e54604080516338b0789d60e01b815290516402540be400926001600160a01b0316916338b0789d9160048083019260209291908290030181865afa158015611bf8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1c9190614c2e565b10158015611c9d5750600e54604080516336e9332d60e01b8152905130926001600160a01b0316916336e9332d9160048083019260209291908290030181865afa158015611c6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c929190614c47565b6001600160a01b0316145b15611d0b57600e60009054906101000a90046001600160a01b03166001600160a01b031663e4fc6b6d6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611cf257600080fd5b505af1158015611d06573d6000803e3d6000fd5b505050505b33611d158161310e565b601b5460ff1615611d805760405162461bcd60e51b815260206004820152602f60248201527f53746b547275546f6b656e3a2043616e2062652063616c6c6564206f6e6c792060448201526e1dda195b881b9bdd081c185d5cd959608a1b6064820152608401610a27565b611d8982613696565b600c54611da1906001600160a01b0316333085612c09565b5050565b6000610a92338484612e91565b600080611dbe836137fb565b9050611013600154826001600160601b03166010546118dd9190614c64565b6005546001600160a01b03163314611e075760405162461bcd60e51b8152600401610a2790614b2a565b6001600160a01b038216600081815260196020908152604091829020805460ff19168515159081179091558251938452908301527f81edd49ff39678a23948277008fa4040a2bdaffea286bab3e17649c452119cd0910160405180910390a15050565b6005546001600160a01b03163314611e945760405162461bcd60e51b8152600401610a2790614b2a565b601b805460ff19168215159081179091556040519081527fef37df9624f797913e7585c7f7b5d004ba6704be3c64b0561c157728ccc8698590602001610a7a565b83421115611f3a5760405162461bcd60e51b815260206004820152602c60248201527f5472757374546f6b656e3a3a64656c656761746542795369673a207369676e6160448201526b1d1d5c9948195e1c1a5c995960a21b6064820152608401610a27565b60007f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866611f8760408051808201909152600d81526c5374616b65642054727565466960981b602082015290565b8051602091820120604080518084019490945283810191909152466060840152306080808501919091528151808503909101815260a0840182528051908301207fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf60c08501526001600160a01b038b1660e085015261010084018a90526101208085018a90528251808603909101815261014085019092528151919092012061190160f01b61016084015261016283018290526101828301819052909250906000906101a20160408051601f198184030181528282528051602091820120600080855291840180845281905260ff8a169284019290925260608301889052608083018790529092509060019060a0016020604051602081039080840390855afa1580156120b8573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166121305760405162461bcd60e51b815260206004820152602c60248201527f5472757374546f6b656e3a3a64656c656761746542795369673a20696e76616c60448201526b6964207369676e617475726560a01b6064820152608401610a27565b6001600160a01b0381166000908152600a6020526040812080549161215483614c83565b9190505589146121b75760405162461bcd60e51b815260206004820152602860248201527f5472757374546f6b656e3a3a64656c656761746542795369673a20696e76616c6044820152676964206e6f6e636560c01b6064820152608401610a27565b6121c1818b613441565b505050505b505050505050565b6001600160a01b038082166000818152601460205260408120600c54919390928492909116146121ff576000612276565b600e60009054906101000a90046001600160a01b03166001600160a01b03166338b0789d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612252573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122769190614c2e565b905060006c0c9f2c9cd04674edea4000000083600301546402540be4008410156122a15760006122a3565b835b6122ac88612aaa565b6122b69190614bda565b6122c09190614bda565b6122ca9190614c64565b905060008360040154826122de9190614bf2565b90506000600154826122f09190614bc6565b85546122fc9190614bda565b6001600160a01b03891660009081526001870160205260409020549091506c0c9f2c9cd04674edea40000000906123339083614bf2565b6001600160a01b038a166000908152600260205260409020546123569190614c64565b6123609190614bc6565b6001600160a01b03891660009081526002870160205260409020546123859190614bda565b98975050505050505050565b6005546001600160a01b031633146123bb5760405162461bcd60e51b8152600401610a2790614b2a565b6000811161241b5760405162461bcd60e51b815260206004820152602760248201527f53746b547275546f6b656e3a20556e7374616b6520706572696f642063616e6e60448201526606f7420626520360cc1b6064820152608401610a27565b63bbf81e0081111561247d5760405162461bcd60e51b815260206004820152602560248201527f53746b547275546f6b656e3a20556e7374616b6520706572696f6420746f6f206044820152646c6172676560d81b6064820152608401610a27565b60138190556040518181527f7b5074fc5cfe79cd171874bd284e9f6d867931e19df1c0d98600fcc8650ad92a90602001610a7a565b600e54604080516338b0789d60e01b815290516402540be400926001600160a01b0316916338b0789d9160048083019260209291908290030181865afa158015612500573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125249190614c2e565b101580156125a55750600e54604080516336e9332d60e01b8152905130926001600160a01b0316916336e9332d9160048083019260209291908290030181865afa158015612576573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061259a9190614c47565b6001600160a01b0316145b1561261357600e60009054906101000a90046001600160a01b03166001600160a01b031663e4fc6b6d6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156125fa57600080fd5b505af115801561260e573d6000803e3d6000fd5b505050505b3361261d8161310e565b600c546000908390612637906001600160a01b0316613878565b6126419190614bda565b905061264c81613696565b821561266a57600c5461266a906001600160a01b0316333086612c09565b505050565b600e54604080516338b0789d60e01b815290516402540be400926001600160a01b0316916338b0789d9160048083019260209291908290030181865afa1580156126bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126e19190614c2e565b101580156127625750600e54604080516336e9332d60e01b8152905130926001600160a01b0316916336e9332d9160048083019260209291908290030181865afa158015612733573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127579190614c47565b6001600160a01b0316145b156127d057600e60009054906101000a90046001600160a01b03166001600160a01b031663e4fc6b6d6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156127b757600080fd5b505af11580156127cb573d6000803e3d6000fd5b505050505b600c54339082906001600160a01b03808316911614806127fd5750600d546001600160a01b038281169116145b8061281a5750601b546001600160a01b0382811661010090920416145b156128325761282881613916565b61283281836139d8565b600d546001600160a01b038481169116148061285b5750600c546001600160a01b038481169116145b806128785750601b546001600160a01b0384811661010090920416145b6128c45760405162461bcd60e51b815260206004820152601f60248201527f546f6b656e206e6f7420737570706f7274656420666f722072657761726473006044820152606401610a27565b61266a83613199565b6005546001600160a01b031633146128f75760405162461bcd60e51b8152600401610a2790614b2a565b600680546001600160a01b0319166001600160a01b03838116918217909255600554604051919216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6016818154811061295957600080fd5b60009182526020909120015467ffffffffffffffff81169150600160401b90046001600160601b031682565b6001600160a01b0383166129e75760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610a27565b6001600160a01b038216612a485760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610a27565b6001600160a01b0383811660008181526003602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b600c546000906001600160a01b0383811691161415612b39576010546040516370a0823160e01b81523060048201526001600160a01b038416906370a08231906024015b602060405180830381865afa158015612b0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b2f9190614c2e565b610a969190614bf2565b600d546001600160a01b0383811691161415612b7e576017546040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401612aee565b601b546001600160a01b03838116610100909204161415612c01576040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015612bdd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a969190614c2e565b506000919050565b6040516001600160a01b0380851660248301528316604482015260648101829052610fc19085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613acb565b60155460185463ffffffff16905b808263ffffffff161015612d1c5782601660158463ffffffff1681548110612cac57612cac614c9e565b90600052602060002090600891828204019190066004029054906101000a900463ffffffff1663ffffffff1681548110612ce857612ce8614c9e565b60009182526020909120015467ffffffffffffffff161115612d0a5750919050565b81612d1481614cb4565b925050612c82565b50919050565b601580546001808201835560008381527f55f448fdea98c4d29eb340757ef0a66cd03dbb9538908a6a81d96026b71ec4756008840401805460079094166004026101000a63ffffffff021990931690925591549091612d8091614c09565b90505b8263ffffffff168163ffffffff161115612e3f576015612da4600183614c09565b63ffffffff1681548110612dba57612dba614c9e565b90600052602060002090600891828204019190066004029054906101000a900463ffffffff1660158263ffffffff1681548110612df957612df9614c9e565b90600052602060002090600891828204019190066004026101000a81548163ffffffff021916908363ffffffff1602179055508080612e3790614cd8565b915050612d83565b508060158363ffffffff1681548110612e5a57612e5a614c9e565b90600052602060002090600891828204019190066004026101000a81548163ffffffff021916908363ffffffff1602179055505050565b600e54604080516338b0789d60e01b815290516402540be400926001600160a01b0316916338b0789d9160048083019260209291908290030181865afa158015612edf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f039190614c2e565b10158015612f845750600e54604080516336e9332d60e01b8152905130926001600160a01b0316916336e9332d9160048083019260209291908290030181865afa158015612f55573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f799190614c47565b6001600160a01b0316145b15612ff257600e60009054906101000a90046001600160a01b03166001600160a01b031663e4fc6b6d6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612fd957600080fd5b505af1158015612fed573d6000803e3d6000fd5b505050505b82612ffc8161310e565b600c54613012906001600160a01b0316846139d8565b600d54613028906001600160a01b0316846139d8565b601b546130439061010090046001600160a01b0316846139d8565b60001961304f84611856565b14613092576001600160a01b0383166000908152601a6020526040902054613078908390614bda565b6001600160a01b0384166000908152601a60205260409020555b60001961309e85611856565b14613103576001600160a01b0384166000908152601a60205260409020546130c69083613b9d565b6001600160a01b0385166000908152601a60205260409020546130e99190614bf2565b6001600160a01b0385166000908152601a60205260409020555b610fc1848484613bb3565b600c54613123906001600160a01b0316613916565b600c54613139906001600160a01b0316826139d8565b600d5461314e906001600160a01b0316613916565b600d54613164906001600160a01b0316826139d8565b601b5461317e9061010090046001600160a01b0316613916565b601b546117179061010090046001600160a01b0316826139d8565b60006131a482613878565b90508015611da157611da16001600160a01b03831633836131d6565b6131ca8282613c24565b610fc1613c9982613ca5565b6040516001600160a01b03831660248201526044810182905261266a90849063a9059cbb60e01b90606401612c3d565b600043821061326d5760405162461bcd60e51b815260206004820152602d60248201527f5472757374546f6b656e3a3a6765745072696f72566f7465733a206e6f74207960448201526c195d0819195d195c9b5a5b9959609a1b6064820152608401610a27565b6001600160a01b03831660009081526009602052604090205463ffffffff168061329b576000915050610a96565b6001600160a01b038416600090815260086020526040812090849082906132c3600186614c09565b63ffffffff90811682526020820192909252604001600020541611613321578060006132f0600185614c09565b63ffffffff168152602081019190915260400160002054600160201b90046001600160601b03169250610a96915050565b60008080526020829052604090205463ffffffff1684101561334857600092505050610a96565b600080613356600185614c09565b90505b8163ffffffff168163ffffffff161115613412576000600261337b8484614c09565b6133859190614cf8565b61338f9083614c09565b63ffffffff81811660009081526020878152604091829020825180840190935254928316808352600160201b9093046001600160601b0316908201529192508814156133e657602001519550610a96945050505050565b805163ffffffff168811156133fd5781935061340b565b613408600183614c09565b92505b5050613359565b5063ffffffff1660009081526020919091526040902054600160201b90046001600160601b0316949350505050565b6001600160a01b038281166000908152600760209081526040808320546002909252822054921691613472906118e7565b6001600160a01b0385811660008181526007602052604080822080546001600160a01b031916898616908117909155905194955093928616927f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a4610fc1828483613e3e565b600081600160601b84106135015760405162461bcd60e51b8152600401610a2791906148b3565b509192915050565b8154600090815b8181101561356d5760006135248284613fb8565b90508486828154811061353957613539614c9e565b60009182526020909120015463ffffffff16111561355957809250613567565b613564816001614bda565b91505b50613510565b81156135b0578461357f600184614bf2565b8154811061358f5761358f614c9e565b600091825260209091200154600160201b90046001600160601b03166135b3565b60005b6001600160601b031695945050505050565b600063ffffffff82111561362a5760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610a27565b5090565b60006001600160601b0382111561362a5760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201526536206269747360d01b6064820152608401610a27565b600081116136e65760405162461bcd60e51b815260206004820152601b60248201527f53746b547275546f6b656e3a2043616e6e6f74207374616b65203000000000006044820152606401610a27565b336000908152601160205260409020541580159061372d57506013546012543360009081526011602052604090205442929161372191614bda565b61372b9190614bda565b115b1561373a5761373a611918565b336000908152600760205260409020546001600160a01b03166137785733600081815260076020526040902080546001600160a01b03191690911790555b60006010546000146137a3576010546001546137949084614c64565b61379e9190614bc6565b6137a5565b815b90506137b13382613fd3565b816010546137bf9190614bda565b60105560405182815233907febedb8b3c678666e7f36970bc8f57abf6d8fa2e828c0da91ea5b75bf68ed101a9060200160405180910390a25050565b6001600160a01b03811660009081526009602052604081205463ffffffff1680613826576000611013565b6001600160a01b03831660009081526008602052604081209061384a600184614c09565b63ffffffff168152602081019190915260400160002054600160201b90046001600160601b03169392505050565b6001600160a01b03811660009081526014602090815260408083203384526002810190925282205460038201546138b0908290614bda565b600383015533600081815260028401602052604080822091909155516001600160a01b03861691907f70eb43c4a8ae8c40502dcf22436c509c28d6ff421cf07c491be56984bd987068906139079085815260200190565b60405180910390a39392505050565b600d546001600160a01b038281169116141561393457613934613fe9565b6001600160a01b038116600090815260146020526040812060038101549091906c0c9f2c9cd04674edea400000009061396c85612aaa565b6139769190614bda565b6139809190614c64565b9050816004015481141561399357505050565b60008260040154826139a59190614bf2565b6004840183905560015490915015610fc1576001546139c49082614bc6565b83546139d09190614bda565b835550505050565b6001600160a01b0380831660009081526014602090815260408083209385168352600290915290205415613aa9576001600160a01b038216600090815260018201602052604090205481546c0c9f2c9cd04674edea4000000091613a3b91614bf2565b6001600160a01b038416600090815260026020526040902054613a5e9190614c64565b613a689190614bc6565b6001600160a01b0383166000908152600283016020526040902054613a8d9190614bda565b6001600160a01b03831660009081526002830160205260409020555b80546001600160a01b0390921660009081526001909101602052604090205550565b6000613b20826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661411f9092919063ffffffff16565b80519091501561266a5780806020019051810190613b3e9190614d1b565b61266a5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a27565b6000818310613bac5781611013565b5090919050565b613bbe83838361412e565b6001600160a01b03808416600090815260076020908152604080832054868516845292819020548151808301909252601c8252600080516020614dbf8339815191529282019290925261266a93928316929190911690613c1f9085906134da565b613e3e565b613c2e828261429c565b611da160076000846001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a90046001600160a01b03166000613c1f846040518060400160405280601c8152602001600080516020614dbf8339815191528152506134da565b60006110138284614bf2565b601c54600090819080613d205760405162461bcd60e51b815260206004820152603760248201527f5374616b65547275546f6b656e3a20746f74616c20737570706c79206368656360448201527f6b706f696e7473206e6f7420696e697469616c697a65640000000000000000006064820152608401610a27565b6000601c613d2f600184614bf2565b81548110613d3f57613d3f614c9e565b60009182526020909120018054600160201b90046001600160601b031694509050613d6e848663ffffffff8916565b815490935063ffffffff16431415613dba57613d898361362e565b81546001600160601b0391909116600160201b026fffffffffffffffffffffffff0000000019909116178155613e35565b601c6040518060400160405280613dd0436135c5565b63ffffffff168152602001613de48661362e565b6001600160601b03908116909152825460018101845560009384526020938490208351910180549490930151909116600160201b026001600160801b031990931663ffffffff909116179190911790555b50509250929050565b816001600160a01b0316836001600160a01b031614158015613e6957506000816001600160601b0316115b1561266a576001600160a01b03831615613f15576001600160a01b03831660009081526009602052604081205463ffffffff169081613ea9576000613ef5565b6001600160a01b038516600090815260086020526040812090613ecd600185614c09565b63ffffffff168152602081019190915260400160002054600160201b90046001600160601b03165b90506000613f038483614d38565b9050613f1186848484614390565b5050505b6001600160a01b0382161561266a576001600160a01b03821660009081526009602052604081205463ffffffff169081613f50576000613f9c565b6001600160a01b038416600090815260086020526040812090613f74600185614c09565b63ffffffff168152602081019190915260400160002054600160201b90046001600160601b03165b90506000613faa8483614d58565b90506121c685848484614390565b6000613fc76002848418614bc6565b61101390848416614bda565b613fdd828261457f565b610fc16145dc82613ca5565b60185463ffffffff166000613fff602083614d83565b60165490915060009063ffffffff83161061401c5760165461401e565b815b6017549091505b8163ffffffff168463ffffffff1610156140eb576000601660158663ffffffff168154811061405657614056614c9e565b90600052602060002090600891828204019190066004029054906101000a900463ffffffff1663ffffffff168154811061409257614092614c9e565b600091825260209091200180549091504267ffffffffffffffff909116106140ba57506140eb565b80546140d690600160401b90046001600160601b031683614bf2565b9150846140e281614cb4565b95505050614025565b601781905560185463ffffffff858116911614610fc1576018805463ffffffff861663ffffffff1990911617905550505050565b606061191084846000856145e8565b6001600160a01b0383166141925760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610a27565b6001600160a01b0382166141f45760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610a27565b6001600160a01b038316600090815260026020526040902054614218908290614bf2565b6001600160a01b038085166000908152600260205260408082209390935590841681522054614248908290614bda565b6001600160a01b0380841660008181526002602052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612a9d9085815260200190565b6001600160a01b0382166142fc5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610a27565b6001600160a01b038216600090815260026020526040902054614320908290614bf2565b6001600160a01b038316600090815260026020526040902055600154614347908290614bf2565b6001556040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b60006143b4436040518060600160405280603a8152602001614ddf603a9139614719565b905060008463ffffffff1611801561440e57506001600160a01b038516600090815260086020526040812063ffffffff8316916143f2600188614c09565b63ffffffff908116825260208201929092526040016000205416145b15614482576001600160a01b03851660009081526008602052604081208391614438600188614c09565b63ffffffff168152602081019190915260400160002080546001600160601b0392909216600160201b026fffffffffffffffffffffffff000000001990921691909117905561452a565b60408051808201825263ffffffff80841682526001600160601b0380861660208085019182526001600160a01b038b166000908152600882528681208b8616825290915294909420925183549451909116600160201b026001600160801b03199094169116179190911790556144f9846001614d83565b6001600160a01b0386166000908152600960205260409020805463ffffffff191663ffffffff929092169190911790555b604080516001600160601b038086168252841660208201526001600160a01b038716917fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a724910160405180910390a25050505050565b6145898282614740565b6001600160a01b038083166000908152600760209081526040808320548151808301909252601c8252600080516020614dbf83398151915292820192909252611da1939190911690613c1f9085906134da565b60006110138284614bda565b6060824710156146495760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a27565b6001600160a01b0385163b6146a05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a27565b600080866001600160a01b031685876040516146bc9190614da2565b60006040518083038185875af1925050503d80600081146146f9576040519150601f19603f3d011682016040523d82523d6000602084013e6146fe565b606091505b509150915061470e82828661481c565b979650505050505050565b600081600160201b84106135015760405162461bcd60e51b8152600401610a2791906148b3565b6001600160a01b0382166147965760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610a27565b806001546147a49190614bda565b6001556001600160a01b0382166000908152600260205260409020546147cb908290614bda565b6001600160a01b0383166000818152600260205260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906143849085815260200190565b6060831561482b575081611013565b82511561483b5782518084602001fd5b8160405162461bcd60e51b8152600401610a2791906148b3565b6001600160a01b038116811461171757600080fd5b60006020828403121561487c57600080fd5b813561101381614855565b60005b838110156148a257818101518382015260200161488a565b83811115610fc15750506000910152565b60208152600082518060208401526148d2816040850160208701614887565b601f01601f19169190910160400192915050565b600080604083850312156148f957600080fd5b823561490481614855565b946020939093013593505050565b600080600080600060a0868803121561492a57600080fd5b853561493581614855565b9450602086013561494581614855565b9350604086013561495581614855565b9250606086013561496581614855565b9150608086013561497581614855565b809150509295509295909350565b6000806040838503121561499657600080fd5b50508035926020909101359150565b6000806000606084860312156149ba57600080fd5b83356149c581614855565b925060208401356149d581614855565b929592945050506040919091013590565b6000602082840312156149f857600080fd5b5035919050565b801515811461171757600080fd5b60008060408385031215614a2057600080fd5b8235614a2b81614855565b91506020830135614a3b816149ff565b809150509250929050565b600060208284031215614a5857600080fd5b8135611013816149ff565b60008060008060008060c08789031215614a7c57600080fd5b8635614a8781614855565b95506020870135945060408701359350606087013560ff81168114614aab57600080fd5b9598949750929560808101359460a0909101359350915050565b60008060408385031215614ad857600080fd5b8235614ae381614855565b91506020830135614a3b81614855565b60008060408385031215614b0657600080fd5b8235614b1181614855565b9150602083013563ffffffff81168114614a3b57600080fd5b6020808252600a908201526937b7363c9037bbb732b960b11b604082015260600190565b6020808252602c908201527f53746b547275546f6b656e3a2066656520746f6b656e2061646472657373206d60408201526b0757374206e6f7420626520360a41b606082015260800190565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600082614bd557614bd5614b9a565b500490565b60008219821115614bed57614bed614bb0565b500190565b600082821015614c0457614c04614bb0565b500390565b600063ffffffff83811690831681811015614c2657614c26614bb0565b039392505050565b600060208284031215614c4057600080fd5b5051919050565b600060208284031215614c5957600080fd5b815161101381614855565b6000816000190483118215151615614c7e57614c7e614bb0565b500290565b6000600019821415614c9757614c97614bb0565b5060010190565b634e487b7160e01b600052603260045260246000fd5b600063ffffffff80831681811415614cce57614cce614bb0565b6001019392505050565b600063ffffffff821680614cee57614cee614bb0565b6000190192915050565b600063ffffffff80841680614d0f57614d0f614b9a565b92169190910492915050565b600060208284031215614d2d57600080fd5b8151611013816149ff565b60006001600160601b0383811690831681811015614c2657614c26614bb0565b60006001600160601b03808316818516808303821115614d7a57614d7a614bb0565b01949350505050565b600063ffffffff808316818516808303821115614d7a57614d7a614bb0565b60008251614db4818460208701614887565b919091019291505056fe53746b547275546f6b656e3a2075696e743936206f766572666c6f77000000005472757374546f6b656e3a3a5f7772697465436865636b706f696e743a20626c6f636b206e756d62657220657863656564732033322062697473a264697066735822122003918d5029d611c8205a1fa8e65322f986c8b47d0f5441f723028b3532eed5cd64736f6c634300080a0033

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.