Contract Address Details

0xeB4511C90F9387De8F8945ABD8C803d5cB275509

Contract Name
SoyStaking
Creator
0xc7d98c–7f3521 at 0xb7a41b–7ef598
Balance
0 CLO ( )
Tokens
Fetching tokens...
Transactions
4,676 Transactions
Transfers
8,708 Transfers
Gas Used
457,438,411
Last Balance Update
14691083
Contract name:
SoyStaking




Optimization enabled
true
Compiler version
v0.6.12+commit.27d51765




Optimization runs
200
EVM Version
default




Verified at
2023-06-19T11:39:22.815459Z

Contract source code

// SPDX-License-Identifier: No License (None)
pragma solidity ^0.6.0;

/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */
library SafeMath {
  function mul(uint a, uint b) internal pure returns (uint) {
    if (a == 0) {
      return 0;
    }
    uint c = a * b;
    require(c / a == b);
    return c;
  }

  function div(uint a, uint b) internal pure returns (uint) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    uint c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return c;
  }

  function sub(uint a, uint b) internal pure returns (uint) {
    require(b <= a);
    return a - b;
  }

  function add(uint a, uint b) internal pure returns (uint) {
    uint c = a + b;
    require(c >= a);
    return c;
  }
}

interface ISimplifiedGlobalFarm {
    function mintFarmingReward(address _localFarm) external;
    function getAllocationX1000(address _farm) external view returns (uint256);
    function getRewardPerSecond() external view returns (uint256);
    function rewardMintingAvailable(address _farm) external view returns (bool);
    function farmExists(address _farmAddress) external view returns (bool);
}

interface IERC223 {
    /**
     * @dev Returns the balance of the `who` address.
     */
    function balanceOf(address who) external view returns (uint);
        
    /**
     * @dev Transfers `value` tokens from `msg.sender` to `to` address
     * and returns `true` on success.
     */
    function transfer(address to, uint value) external returns (bool success);
        
    /**
     * @dev Transfers `value` tokens from `msg.sender` to `to` address with `data` parameter
     * and returns `true` on success.
     */
    function transfer(address to, uint value, bytes memory data) external returns (bool success);
     
     /**
     * @dev Event that is fired on successful transfer.
     */
    event Transfer(address indexed from, address indexed to, uint value);
    
     /**
     * @dev Additional event that is fired on successful transfer and logs transfer metadata,
     *      this event is implemented to keep Transfer event compatible with ERC20.
     */
    event TransferData(bytes data);
}

contract SoyStaking {
    
    // NOTE: The contract only works for intervals of time > round_interval

    using SafeMath for uint;

    event StartStaking(address addr, uint value, uint amount, uint time, uint end_time);
    event WithdrawStake(address staker, uint amount);
    event Claim(address staker, uint reward);
    event DonationDeposited(address _address, uint value);

    struct Staker
    {
        uint amount;
        uint time;              // Staking start time or last claim rewards
        uint multiplier;        // Rewards multiplier = 0.40 + (0.05 * rounds). [0.45..1] (max rounds 12)
        uint end_time;          // Time when staking ends and user may withdraw. After this time user will not receive rewards.
    }


    uint public LastBlock = block.number;
    uint public Timestamp = now;    //timestamp of the last interaction with the contract.

    uint public TotalStakingWeight; //total weight = sum (each_staking_amount * each_staking_time).
    uint public TotalStakingAmount; //currently frozen amount for Staking.
    uint public StakingRewardPool;  //available amount for paying rewards.
    uint public staking_threshold = 0 ether;

    uint public constant round_interval   = 27 days;     // 1 month.
    uint public constant max_delay        = 365 days;    // 1 year after staking ends.
    uint public constant BlockStartStaking = 7600000;

    uint constant NOMINATOR = 10**18;           // Nominator / denominator used for float point numbers

    address public constant SOY = 0x9FaE2529863bD691B4A7171bDfCf33C7ebB10a65;
    address public constant globalFarm = 0x64Fa36ACD0d13472FD786B03afC9C52aD5FCf023;
    uint public stake_until = 1662033600; // 1 September 2022 12:00:00 GMT (all staking should be finished until this time)
    address public admin;   // admin address who can set stake_until

    //========== TESTNET VALUES ===========
    //uint public constant round_interval   = 1 hours; 
    //uint public constant max_delay        = 2 days;
    //uint public constant BlockStartStaking = 0;
    
    //https://github.com/SoyFinance/smart-contracts/tree/main/Farming#testsoy223-token
    //address public constant SOY = 0xC8227f810FB2F4FacBf9D3CAbca21e47f51d87a3;

    // https://github.com/SoyFinance/smart-contracts/tree/main/Farming#test-global-farm-contract-3-minutes
    //address public constant globalFarm = 0xE8B2Fee5D18ec30f5625a5f7F1f06E5df17E1774;
    //========== END TEST VALUES ==========
    
    mapping(address => Staker) public staker;


    constructor() public {
        admin = msg.sender;
    }

    function setStakeUntil(uint newDate) external {
        require(admin == msg.sender, "Only admin");
        stake_until = newDate;
    }

    // ERC223 token transfer callback
    // bytes _data = abi.encode(address receiver, uint256 toChainId)
    function tokenReceived(address _from, uint _value, bytes calldata _data) external {
        require(msg.sender == SOY, "Only SOY");
        if (_from == globalFarm || _from == admin) return; // if globalFarm or admin transfer tokens, they will be added to reward pool

        // No donations accepted to fallback!
        // Consider value deposit is an attempt to become staker.
        // May not accept deposit from other contracts due GAS limit.
        // by default stake for 1 round
        uint rounds;
        if (_data.length >= 32) {
            rounds = abi.decode(_data, (uint256));  // _data should contain ABI encoded UINT =  number of rounds
        }
        if (rounds == 0) rounds = 1;
        start_staking(_from, _value, rounds);
    }

    function notifyRewardAmount(uint256 reward) external {}

    function reward_request() internal
    {
        if(ISimplifiedGlobalFarm(globalFarm).rewardMintingAvailable(address(this)))
        {
            ISimplifiedGlobalFarm(globalFarm).mintFarmingReward(address(this));
        }
    }

    // update TotalStakingAmount value.
    function new_block(uint amount) internal
    {
        if (block.number > LastBlock)   //run once per block.
        {
            reward_request();
            uint _LastBlock = LastBlock;
            LastBlock = block.number;

            StakingRewardPool = IERC223(SOY).balanceOf(address(this)).sub(TotalStakingAmount + amount);   //fix rewards pool for this block.
            // amount here for case new_block() is calling from start_staking(), and amount will be added to CurrentBlockDeposits.

            //The consensus protocol enforces block timestamps are always at least +1 from their parent, so a node cannot "lie into the past". 
            if (now > Timestamp) //But with this condition I feel safer :) May be removed.
            {
                uint _blocks = block.number - _LastBlock;
                uint _seconds = now - Timestamp;
                if (_seconds > _blocks * 25) //if time goes far in the future, then use new time as 25 second * blocks.
                {
                    _seconds = _blocks * 25;
                }
                TotalStakingWeight += _seconds.mul(TotalStakingAmount);
                Timestamp += _seconds;
            }
        }
    }

    function start_staking(address user, uint amount, uint rounds) internal staking_available
    {
        assert(amount >= staking_threshold);
        require(rounds > 0);
        new_block(amount); //run once per block.
        // to reduce gas cost we will use local variable instead of global
        uint _Timestamp = Timestamp;
        uint staker_amount = staker[user].amount;
        uint r = rounds;
        if (r > 6) r = 6;
        uint multiplier = (40 + (10 * r)) * NOMINATOR / 100;  // staker multiplier = 0.40 + (0.05 * rounds). [0.45..1]
        uint end_time = _Timestamp.add(round_interval.mul(rounds));
        require(end_time <= stake_until, "Too long staking time");  // do not allow stake longer than "stake_until"
        // claim reward if available.
        if (staker_amount > 0)
        {
            if (_Timestamp >= staker[user].time + round_interval)
            { 
                _claim(payable(user)); 
            }
            uint staker_end_time = staker[user].end_time;
            if (staker_end_time > end_time) {
                end_time = staker_end_time;     // Staking end time is the bigger from previous and new one.
                r = (end_time.sub(_Timestamp)).div(round_interval);  // update number of rounds
                if (r > 12) r = 12;
                multiplier = (40 + (5 * r)) * NOMINATOR / 100;  // staker multiplier = 0.40 + (0.05 * rounds). [0.45..1]
            }
            // if there is active staking with bigger multiplier
            if (staker[user].multiplier > multiplier && staker_end_time > _Timestamp) {
                // recalculate multiplier = (staker.multiplier * staker.amount + new.multiplier * new.amount) / ( staker.amount + new.amount)
                multiplier = ((staker[user].multiplier.mul(staker_amount)).add(multiplier.mul(amount))) / (staker_amount.add(amount));
                if (multiplier > NOMINATOR) multiplier = NOMINATOR; // multiplier can't be more then 1
            }
            TotalStakingWeight = TotalStakingWeight.sub((_Timestamp.sub(staker[user].time)).mul(staker_amount)); // remove from Weight
        }

        TotalStakingAmount = TotalStakingAmount.add(amount);
        staker[user].time = _Timestamp;
        staker[user].amount = staker_amount.add(amount);
        staker[user].multiplier = multiplier;
        staker[user].end_time = end_time;

        emit StartStaking(
            user,
            amount,
            staker[user].amount,
            _Timestamp,
            end_time
        );
    }

    function withdraw_stake() external {
        _withdraw_stake(msg.sender);
    }

    function withdraw_stake(address payable user) external {
        _withdraw_stake(user);
    }

    function _withdraw_stake(address payable user) internal
    {
        new_block(0); //run once per block.
        require(Timestamp >= staker[user].end_time); //reject withdrawal before end time.

        uint _amount = staker[user].amount;
        require(_amount != 0);
        // claim reward if available.
        _claim(user); 
        TotalStakingAmount = TotalStakingAmount.sub(_amount);
        TotalStakingWeight = TotalStakingWeight.sub((Timestamp.sub(staker[user].time)).mul(staker[user].amount)); // remove from Weight.
        
        staker[user].amount = 0;
        IERC223(SOY).transfer(user, _amount);
        emit WithdrawStake(user, _amount);
    }

    //claim rewards
    function claim() external only_staker
    {
        _claim(msg.sender);
    }


    function _claim(address payable user) internal
    {
        new_block(0); //run once per block
        // to reduce gas cost we will use local variable instead of global
        uint _Timestamp = Timestamp;
        if (_Timestamp > staker[user].end_time) _Timestamp = staker[user].end_time; // rewards calculates until staking ends
        uint _StakingInterval = _Timestamp.sub(staker[user].time);  //time interval of deposit.
        if (_StakingInterval >= round_interval)
        {
            uint _CompleteRoundsInterval = (_StakingInterval / round_interval).mul(round_interval); //only complete rounds.
            uint _StakerWeight = _CompleteRoundsInterval.mul(staker[user].amount); //Weight of completed rounds.
            uint _reward = StakingRewardPool.mul(_StakerWeight).div(TotalStakingWeight);  //StakingRewardPool * _StakerWeight/TotalStakingWeight
            _reward = _reward.mul(staker[user].multiplier) / NOMINATOR;   // reduce rewards if staked on less then 12 rounds.
            StakingRewardPool = StakingRewardPool.sub(_reward);
            TotalStakingWeight = TotalStakingWeight.sub(_StakerWeight); // remove paid Weight.

            staker[user].time = staker[user].time.add(_CompleteRoundsInterval); // reset to paid time, staking continue without a loss of incomplete rounds.
	    
            IERC223(SOY).transfer(user, _reward);
            emit Claim(user, _reward);
        }
    }

    //This function may be used for info only. This can show estimated user reward at current time.
    function stake_reward(address _addr) external view returns (uint _reward)
    {
        //require(staker[_addr].amount > 0);

        uint _blocks = block.number - LastBlock;
        uint _seconds = now - Timestamp;
        if (_seconds > _blocks * 25) //if time goes far in the future, then use new time as 25 second * blocks.
        {
            _seconds = _blocks * 25;
        }
        uint _Timestamp = Timestamp + _seconds;
        if (_Timestamp > staker[_addr].end_time) _Timestamp = staker[_addr].end_time; // rewards calculates until staking ends
        uint _TotalStakingWeight = TotalStakingWeight + _seconds.mul(TotalStakingAmount);
        uint _StakingInterval = _Timestamp.sub(staker[_addr].time); //time interval of deposit.
	
        //uint _StakerWeight = _StakingInterval.mul(staker[_addr].amount); //Staker weight.
        uint _CompleteRoundsInterval = (_StakingInterval / round_interval).mul(round_interval); //only complete rounds.
        uint _StakerWeight = _CompleteRoundsInterval.mul(staker[_addr].amount); //Weight of completed rounds.
        uint _StakingRewardPool = IERC223(SOY).balanceOf(address(this)).sub(TotalStakingAmount);
        _reward = _StakingRewardPool.mul(_StakerWeight).div(_TotalStakingWeight);  //StakingRewardPool * _StakerWeight/TotalStakingWeight
        _reward = _reward.mul(staker[_addr].multiplier) / NOMINATOR;   // reduce rewards if staked on less then 12 rounds.
    }

    modifier only_staker
    {
        require(staker[msg.sender].amount > 0);
        _;
    }

    modifier staking_available
    {
        require(block.number >= BlockStartStaking);
        _;
    }

    //return deposit to inactive staker after 1 year when staking ends.
    function report_abuse(address payable _addr) public only_staker
    {
        require(staker[_addr].amount > 0);
        new_block(0); //run once per block.
        require(Timestamp > staker[_addr].end_time.add(max_delay));
        
        uint _amount = staker[_addr].amount;
        
        TotalStakingAmount = TotalStakingAmount.sub(_amount);
        TotalStakingWeight = TotalStakingWeight.sub((Timestamp.sub(staker[_addr].time)).mul(_amount)); // remove from Weight.

        staker[_addr].amount = 0;
        IERC223(SOY).transfer(_addr, _amount);
    }
}
        

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[]},{"type":"event","name":"Claim","inputs":[{"type":"address","name":"staker","internalType":"address","indexed":false},{"type":"uint256","name":"reward","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"DonationDeposited","inputs":[{"type":"address","name":"_address","internalType":"address","indexed":false},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"StartStaking","inputs":[{"type":"address","name":"addr","internalType":"address","indexed":false},{"type":"uint256","name":"value","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"time","internalType":"uint256","indexed":false},{"type":"uint256","name":"end_time","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"WithdrawStake","inputs":[{"type":"address","name":"staker","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"BlockStartStaking","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"LastBlock","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"SOY","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"StakingRewardPool","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"Timestamp","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"TotalStakingAmount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"TotalStakingWeight","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"admin","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claim","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"globalFarm","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"max_delay","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"notifyRewardAmount","inputs":[{"type":"uint256","name":"reward","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"report_abuse","inputs":[{"type":"address","name":"_addr","internalType":"address payable"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"round_interval","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setStakeUntil","inputs":[{"type":"uint256","name":"newDate","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_reward","internalType":"uint256"}],"name":"stake_reward","inputs":[{"type":"address","name":"_addr","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stake_until","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"time","internalType":"uint256"},{"type":"uint256","name":"multiplier","internalType":"uint256"},{"type":"uint256","name":"end_time","internalType":"uint256"}],"name":"staker","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"staking_threshold","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"tokenReceived","inputs":[{"type":"address","name":"_from","internalType":"address"},{"type":"uint256","name":"_value","internalType":"uint256"},{"type":"bytes","name":"_data","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdraw_stake","inputs":[{"type":"address","name":"user","internalType":"address payable"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdraw_stake","inputs":[]}]
            

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106101425760003560e01c8063aaee69d9116100b8578063cd9488551161007c578063cd94885514610327578063e2d405681461032f578063e6369e4114610337578063edbbc33b1461033f578063f851a44014610347578063f88400711461034f57610142565b8063aaee69d9146102c3578063b3fefcf5146102e9578063b43e92fa146102f1578063be9e2c9a146102f9578063bf92b4ef1461030157610142565b80635499eae61161010a5780635499eae6146101b45780635c854aa9146101bc5780637157c113146101c457806378be0ad4146101cc57806382e4eda4146101f25780638943ec021461023e57610142565b806314657ef0146101475780631e37de4c146101615780633c6b16ab146101695780634e71d92d146101885780634f3a1ff814610190575b600080fd5b61014f61036c565b60408051918252519081900360200190f35b61014f610372565b6101866004803603602081101561017f57600080fd5b5035610378565b005b61018661037b565b61019861039f565b604080516001600160a01b039092168252519081900360200190f35b61014f6103b7565b61014f6103bd565b61014f6103c3565b610186600480360360208110156101e257600080fd5b50356001600160a01b03166103ca565b6102186004803603602081101561020857600080fd5b50356001600160a01b03166103d3565b604080519485526020850193909352838301919091526060830152519081900360800190f35b6101866004803603606081101561025457600080fd5b6001600160a01b038235169160208101359181019060608101604082013564010000000081111561028457600080fd5b82018360208201111561029657600080fd5b803590602001918460018302840111640100000000831117156102b857600080fd5b5090925090506103fa565b610186600480360360208110156102d957600080fd5b50356001600160a01b03166104ca565b61014f610653565b61014f610659565b61019861065f565b61014f6004803603602081101561031757600080fd5b50356001600160a01b0316610677565b610186610861565b61014f61086a565b61014f610872565b61014f610878565b61019861087f565b6101866004803603602081101561036557600080fd5b503561088e565b60025481565b60045481565b50565b3360009081526008602052604090205461039457600080fd5b61039d336108df565b565b7364fa36acd0d13472fd786b03afc9c52ad5fcf02381565b60065481565b60055481565b6223988081565b61037881610b37565b60086020526000908152604090208054600182015460028301546003909301549192909184565b33739fae2529863bd691b4a7171bdfcf33c7ebb10a651461044d576040805162461bcd60e51b81526020600482015260086024820152674f6e6c7920534f5960c01b604482015290519081900360640190fd5b6001600160a01b0384167364fa36acd0d13472fd786b03afc9c52ad5fcf023148061048557506007546001600160a01b038581169116145b1561048f576104c4565b6000602082106104ae57828260208110156104a957600080fd5b503590505b806104b7575060015b6104c2858583610cc6565b505b50505050565b336000908152600860205260409020546104e357600080fd5b6001600160a01b03811660009081526008602052604090205461050557600080fd5b61050f6000610fbb565b6001600160a01b038116600090815260086020526040902060030154610539906301e1338061107f565b6001541161054657600080fd5b6001600160a01b03811660009081526008602052604090205460035461056c908261109a565b6003556001600160a01b038216600090815260086020526040902060019081015490546105b2916105a99184916105a3919061109a565b906110af565b6002549061109a565b6002556001600160a01b0382166000818152600860209081526040808320839055805163a9059cbb60e01b815260048101949094526024840185905251739fae2529863bd691b4a7171bdfcf33c7ebb10a659363a9059cbb9360448083019493928390030190829087803b15801561062957600080fd5b505af115801561063d573d6000803e3d6000fd5b505050506040513d60208110156104c457600080fd5b60035481565b60005481565b739fae2529863bd691b4a7171bdfcf33c7ebb10a6581565b600080546001544391909103904203601982028111156106975750601981025b6001546001600160a01b038516600090815260086020526040902060030154908201908111156106df57506001600160a01b0384166000908152600860205260409020600301545b60006106f6600354846110af90919063ffffffff16565b6002546001600160a01b0388166000908152600860205260408120600101549290910192509061072790849061109a565b9050600061073c6223988080845b04906110af565b6001600160a01b038916600090815260086020526040812054919250906107649083906110af565b90506000610801600354739fae2529863bd691b4a7171bdfcf33c7ebb10a656001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156107cf57600080fd5b505afa1580156107e3573d6000803e3d6000fd5b505050506040513d60208110156107f957600080fd5b50519061109a565b90506108178561081183856110af565b906110d6565b6001600160a01b038b16600090815260086020526040902060020154909950670de0b6b3a76400009061084b908b906110af565b8161085257fe5b049a9950505050505050505050565b61039d33610b37565b6301e1338081565b60015481565b6273f78081565b6007546001600160a01b031681565b6007546001600160a01b031633146108da576040805162461bcd60e51b815260206004820152600a60248201526927b7363c9030b236b4b760b11b604482015290519081900360640190fd5b600655565b6108e96000610fbb565b6001546001600160a01b03821660009081526008602052604090206003015481111561092d57506001600160a01b0381166000908152600860205260409020600301545b6001600160a01b03821660009081526008602052604081206001015461095490839061109a565b9050622398808110610b32576000610970622398808084610735565b6001600160a01b038516600090815260086020526040812054919250906109989083906110af565b905060006109b7600254610811846004546110af90919063ffffffff16565b6001600160a01b038716600090815260086020526040902060020154909150670de0b6b3a7640000906109eb9083906110af565b816109f257fe5b049050610a0a8160045461109a90919063ffffffff16565b600455600254610a1a908361109a565b6002556001600160a01b038616600090815260086020526040902060010154610a43908461107f565b6001600160a01b038716600081815260086020908152604080832060010194909455835163a9059cbb60e01b81526004810193909352602483018590529251739fae2529863bd691b4a7171bdfcf33c7ebb10a659363a9059cbb93604480820194929392918390030190829087803b158015610abe57600080fd5b505af1158015610ad2573d6000803e3d6000fd5b505050506040513d6020811015610ae857600080fd5b5050604080516001600160a01b03881681526020810183905281517f47cee97cb7acd717b3c0aa1435d004cd5b3c8c57d70dbceb4e4458bbd60e39d4929181900390910190a15050505b505050565b610b416000610fbb565b6001600160a01b0381166000908152600860205260409020600301546001541015610b6b57600080fd5b6001600160a01b03811660009081526008602052604090205480610b8e57600080fd5b610b97826108df565b600354610ba4908261109a565b6003556001600160a01b038216600090815260086020526040902080546001918201549154610bdb926105a992916105a39161109a565b6002556001600160a01b0382166000818152600860209081526040808320839055805163a9059cbb60e01b815260048101949094526024840185905251739fae2529863bd691b4a7171bdfcf33c7ebb10a659363a9059cbb9360448083019493928390030190829087803b158015610c5257600080fd5b505af1158015610c66573d6000803e3d6000fd5b505050506040513d6020811015610c7c57600080fd5b5050604080516001600160a01b03841681526020810183905281517f141ef67c4a6d3ec2adfb2f66d33c2b11de5b4f34344757554d430570b18a92ec929181900390910190a15050565b6273f780431015610cd657600080fd5b600554821015610ce257fe5b60008111610cef57600080fd5b610cf882610fbb565b6001546001600160a01b038416600090815260086020526040902054826006811115610d22575060065b6064670de0b6b3a7640000600a830260280102046000610d4f610d4862239880886110af565b869061107f565b9050600654811115610da0576040805162461bcd60e51b8152602060048201526015602482015274546f6f206c6f6e67207374616b696e672074696d6560581b604482015290519081900360640190fd5b8315610f02576001600160a01b03881660009081526008602052604090206001015462239880018510610dd657610dd6886108df565b6001600160a01b03881660009081526008602052604090206003015481811115610e3557905080610e0e62239880610811838961109a565b9350600c841115610e1e57600c93505b606460286005860201670de0b6b3a7640000020492505b6001600160a01b03891660009081526008602052604090206002015483108015610e5e57508581115b15610ecc57610e6d858961107f565b610ea6610e7a858b6110af565b6001600160a01b038c16600090815260086020526040902060020154610ea090896110af565b9061107f565b81610ead57fe5b049250670de0b6b3a7640000831115610ecc57670de0b6b3a764000092505b6001600160a01b038916600090815260086020526040902060010154610efd906105a99087906105a3908a9061109a565b600255505b600354610f0f908861107f565b6003556001600160a01b0388166000908152600860205260409020600101859055610f3a848861107f565b6001600160a01b03891660008181526008602090815260409182902084815560028101879055600301859055815192835282018a905281810192909252606081018790526080810183905290517f59314c5dd1994b45a3987ac7bace43129e2616c39c4ce9a995c6cc5e4adc5cb49181900360a00190a15050505050505050565b60005443111561037857610fcd6110eb565b6000805443909155600354604080516370a0823160e01b8152306004820152905161103192850191739fae2529863bd691b4a7171bdfcf33c7ebb10a65916370a0823191602480820192602092909190829003018186803b1580156107cf57600080fd5b60045560015442111561107b5760015443829003904203601982028111156110595750601981025b6003546110679082906110af565b600280549091019055600180549091019055505b5050565b60008282018381101561109157600080fd5b90505b92915050565b6000828211156110a957600080fd5b50900390565b6000826110be57506000611094565b828202828482816110cb57fe5b041461109157600080fd5b6000808284816110e257fe5b04949350505050565b6040805163445c92c560e01b815230600482015290517364fa36acd0d13472fd786b03afc9c52ad5fcf0239163445c92c5916024808301926020929190829003018186803b15801561113c57600080fd5b505afa158015611150573d6000803e3d6000fd5b505050506040513d602081101561116657600080fd5b50511561039d576040805163032453f160e51b815230600482015290517364fa36acd0d13472fd786b03afc9c52ad5fcf0239163648a7e2091602480830192600092919082900301818387803b1580156111bf57600080fd5b505af11580156104c4573d6000803e3d6000fdfea2646970667358221220abb7a91ba61042161509e9d29db3c6b81d3287416e8ad32f421aeea5a8b6787064736f6c634300060c0033