Transactions
Token Transfers
Tokens
Internal Transactions
Coin Balance History
Logs
Code
Read Contract
Write Contract
- 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