Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- Vesting
- Optimization enabled
- true
- Compiler version
- v0.8.19+commit.7dd6d404
- Optimization runs
- 200
- EVM Version
- default
- Verified at
- 2025-01-26T20:03:54.898124Z
Contract source code
// SPDX-License-Identifier: No License (None)
pragma solidity 0.8.19;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable {
address internal _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () {
_owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == msg.sender, "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
}
contract Vesting is Ownable {
address public vestedToken;
uint256 public totalAllocated;
uint256 public totalClaimed;
struct Allocation {
uint256 amount; // amount of token
uint256 cliffFinish; // Timestamp (unix time) when finish vesting. First release will be at this time
uint256 vestingPercentage; // percentage (with 2 decimals) of tokens will be unlocked every interval (i.e. 10% per 30 days)
uint256 vestingInterval; // interval (in seconds) of vesting (i.e. 30 days)
}
mapping(address beneficiary => Allocation[]) public beneficiaries; // beneficiary => Allocation
mapping(address => uint256) public claimedAmount; // beneficiary => already claimed amount
mapping(address => bool) public depositors; // address of users who has right to deposit and allocate tokens
event SetDepositor(address depositor, bool enable);
event Claim(address indexed beneficiary, uint256 amount);
event AddAllocation(
address indexed to, // beneficiary of tokens
uint256 amount, // amount of token
uint256 cliffFinish, // Timestamp (unix time) when finish vesting. First release will be at this time
uint256 vestingPercentage, // percentage (with 2 decimals) of tokens will be unlocked every interval (i.e. 10% per 30 days)
uint256 vestingInterval // interval (in seconds) of vesting (i.e. 30 days)
);
event Rescue(address _token, uint256 _amount);
modifier onlyDepositor() {
require(depositors[msg.sender], "Only depositors allowed");
_;
}
function initialize(address owner_, address vestedToken_) external {
require(_owner == address(0), "Already init");
_owner = owner_;
emit OwnershipTransferred(address(0), owner_);
depositors[msg.sender] = true;
vestedToken = vestedToken_;
}
// Depositor has right to transfer token to contract and allocate token to the beneficiary
function setDepositor(address depositor, bool enable) external onlyOwner {
depositors[depositor] = enable;
emit SetDepositor(depositor, enable);
}
function allocateTokens(
address to, // beneficiary of tokens
uint256 amount, // amount of token
uint256 cliffFinish, // Timestamp (unix time) when starts vesting. First vesting will be at this time
uint256 vestingPercentage, // percentage (with 2 decimals) of tokens will be unlocked every interval (i.e. 10% per 30 days)
uint256 vestingInterval // interval (in seconds) of vesting (i.e. 30 days)
)
external
onlyDepositor
{
require(vestingPercentage <= 10000, "Incorrect vestingPercentage");
require(beneficiaries[to].length < 100, "Too many allocations for one address, use another address");
safeTransferFrom(vestedToken, msg.sender, address(this), amount);
require(amount <= getUnallocatedAmount(), "Not enough tokens");
beneficiaries[to].push(Allocation(amount, cliffFinish, vestingPercentage, vestingInterval));
totalAllocated += amount;
/*// Check ERC223 compatibility of the beneficiary
if (isContract(to)) {
ERC223Recipient(to).tokenReceived(address(this), 0, "");
}*/
emit AddAllocation(to, amount, cliffFinish, vestingPercentage, vestingInterval);
}
function claim() external {
claimBehalf(msg.sender);
}
function claimBehalf(address beneficiary) public {
(uint256 unlockedAmount,,) = getUnlockedAmount(beneficiary);
if(unlockedAmount != 0) {
claimedAmount[beneficiary] += unlockedAmount;
totalClaimed += unlockedAmount;
safeTransfer(vestedToken, beneficiary, unlockedAmount);
}
emit Claim(beneficiary, unlockedAmount);
}
function getUnlockedAmount(address beneficiary) public view returns(uint256 unlockedAmount, uint256 lockedAmount, uint256 nextUnlock) {
uint256 len = beneficiaries[beneficiary].length;
nextUnlock = 10000000000;
for (uint256 i = 0; i < len; i++) {
Allocation memory b = beneficiaries[beneficiary][i];
uint256 amount = b.amount;
lockedAmount += amount;
uint256 unlocked;
if (b.cliffFinish <= block.timestamp) {
uint256 intervals = (block.timestamp - b.cliffFinish) / b.vestingInterval + 1;
unlocked = unlocked + (amount * intervals * b.vestingPercentage / 10000);
uint256 next = intervals * b.vestingInterval + b.cliffFinish;
if(nextUnlock > next) nextUnlock = next;
} else if (nextUnlock > b.cliffFinish) nextUnlock = b.cliffFinish;
if (unlocked > amount) unlocked = amount;
unlockedAmount += unlocked;
}
lockedAmount -= unlockedAmount;
unlockedAmount = unlockedAmount - claimedAmount[beneficiary];
}
function getBeneficiary(address beneficiary) external view returns(Allocation[] memory) {
return beneficiaries[beneficiary];
}
function getUnallocatedAmount() public view returns(uint256 amount) {
amount = IERC20(vestedToken).balanceOf(address(this));
uint256 unclaimed = totalAllocated - totalClaimed;
amount = amount - unclaimed;
}
function rescueTokens(address _token) onlyOwner external {
uint256 amount;
if (_token == vestedToken) {
amount = getUnallocatedAmount();
} else {
amount = IERC20(_token).balanceOf(address(this));
}
safeTransfer(_token, msg.sender, amount);
emit Rescue(_token, amount);
}
/**
* @dev Returns true if `account` is a contract.
*
* This test is non-exhaustive, and there may be false-negatives: during the
* execution of a contract's constructor, its address will be reported as
* not containing a contract.
*
* > It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*/
function isContract(address account) internal view returns (bool) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
function safeTransfer(address token, address to, uint value) internal {
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
}
function safeTransferFrom(address token, address from, address to, uint value) internal {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
}
}
Contract ABI
[{"type":"event","name":"AddAllocation","inputs":[{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"cliffFinish","internalType":"uint256","indexed":false},{"type":"uint256","name":"vestingPercentage","internalType":"uint256","indexed":false},{"type":"uint256","name":"vestingInterval","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Claim","inputs":[{"type":"address","name":"beneficiary","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Rescue","inputs":[{"type":"address","name":"_token","internalType":"address","indexed":false},{"type":"uint256","name":"_amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SetDepositor","inputs":[{"type":"address","name":"depositor","internalType":"address","indexed":false},{"type":"bool","name":"enable","internalType":"bool","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"allocateTokens","inputs":[{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"cliffFinish","internalType":"uint256"},{"type":"uint256","name":"vestingPercentage","internalType":"uint256"},{"type":"uint256","name":"vestingInterval","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"cliffFinish","internalType":"uint256"},{"type":"uint256","name":"vestingPercentage","internalType":"uint256"},{"type":"uint256","name":"vestingInterval","internalType":"uint256"}],"name":"beneficiaries","inputs":[{"type":"address","name":"beneficiary","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claim","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimBehalf","inputs":[{"type":"address","name":"beneficiary","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"claimedAmount","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"depositors","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"","internalType":"struct Vesting.Allocation[]","components":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"cliffFinish","internalType":"uint256"},{"type":"uint256","name":"vestingPercentage","internalType":"uint256"},{"type":"uint256","name":"vestingInterval","internalType":"uint256"}]}],"name":"getBeneficiary","inputs":[{"type":"address","name":"beneficiary","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"amount","internalType":"uint256"}],"name":"getUnallocatedAmount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"unlockedAmount","internalType":"uint256"},{"type":"uint256","name":"lockedAmount","internalType":"uint256"},{"type":"uint256","name":"nextUnlock","internalType":"uint256"}],"name":"getUnlockedAmount","inputs":[{"type":"address","name":"beneficiary","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"owner_","internalType":"address"},{"type":"address","name":"vestedToken_","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"rescueTokens","inputs":[{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setDepositor","inputs":[{"type":"address","name":"depositor","internalType":"address"},{"type":"bool","name":"enable","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalAllocated","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalClaimed","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"vestedToken","inputs":[]}]
Contract Creation Code
0x608060405234801561001057600080fd5b50600080546001600160a01b0319163390811782556040519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a36112b18061005f6000396000f3fe608060405234801561001057600080fd5b50600436106101155760003560e01c8063529849e9116100a2578063889014961161007157806388901496146102685780638da5cb5b1461027b578063d54ad2a11461028c578063eed75f6d14610295578063f2fde38b146102c857600080fd5b8063529849e9146101ef5780635de29741146102225780636f860d741461024d578063715018a61461026057600080fd5b8063431b1acb116100e9578063431b1acb1461019857806345f7f249146101ab578063485cc955146101b45780634e71d92d146101c7578063505a1b31146101cf57600080fd5b8062ae3bf81461011a57806304e869031461012f57806328733bb714610162578063420d4a021461016a575b600080fd5b61012d610128366004610fca565b6102db565b005b61014f61013d366004610fca565b60056020526000908152604090205481565b6040519081526020015b60405180910390f35b61014f610402565b61017d610178366004610fca565b610495565b60408051938452602084019290925290820152606001610159565b61012d6101a6366004610fec565b61065d565b61014f60025481565b61012d6101c236600461102e565b6108da565b61012d6109a9565b6101e26101dd366004610fca565b6109b4565b6040516101599190611061565b6102026101fd3660046110c5565b610a51565b604080519485526020850193909352918301526060820152608001610159565b600154610235906001600160a01b031681565b6040516001600160a01b039091168152602001610159565b61012d61025b366004611100565b610a97565b61012d610b2c565b61012d610276366004610fca565b610baf565b6000546001600160a01b0316610235565b61014f60035481565b6102b86102a3366004610fca565b60066020526000908152604090205460ff1681565b6040519015158152602001610159565b61012d6102d6366004610fca565b610c6a565b336102ee6000546001600160a01b031690565b6001600160a01b03161461031d5760405162461bcd60e51b815260040161031490611137565b60405180910390fd5b6001546000906001600160a01b03908116908316036103455761033e610402565b90506103b0565b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015610389573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ad919061116c565b90505b6103bb823383610d63565b604080516001600160a01b0384168152602081018390527f542fa6bfee3b4746210fbdd1d83f9e49b65adde3639f8d8f165dd18347938af291015b60405180910390a15050565b6001546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa15801561044b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061046f919061116c565b90506000600354600254610483919061119b565b905061048f818361119b565b91505090565b6001600160a01b03811660009081526004602052604081205481906402540be40090825b81811015610622576001600160a01b03861660009081526004602052604081208054839081106104eb576104eb6111b4565b60009182526020918290206040805160808101825260049093029091018054808452600182015494840194909452600281015491830191909152600301546060820152915061053a81876111ca565b95506000428360200151116105e15760008360600151846020015142610560919061119b565b61056a91906111dd565b6105759060016111ca565b90506127108460400151828561058b91906111ff565b61059591906111ff565b61059f91906111dd565b6105a990836111ca565b9150600084602001518560600151836105c291906111ff565b6105cc91906111ca565b9050808811156105da578097505b50506105f5565b82602001518611156105f557826020015195505b818111156106005750805b61060a81896111ca565b9750505050808061061a90611216565b9150506104b9565b5061062d848461119b565b6001600160a01b038616600090815260056020526040902054909350610653908561119b565b9350509193909250565b3360009081526006602052604090205460ff166106bc5760405162461bcd60e51b815260206004820152601760248201527f4f6e6c79206465706f7369746f727320616c6c6f7765640000000000000000006044820152606401610314565b61271082111561070e5760405162461bcd60e51b815260206004820152601b60248201527f496e636f72726563742076657374696e6750657263656e7461676500000000006044820152606401610314565b6001600160a01b03851660009081526004602052604090205460641161079c5760405162461bcd60e51b815260206004820152603960248201527f546f6f206d616e7920616c6c6f636174696f6e7320666f72206f6e652061646460448201527f726573732c2075736520616e6f746865722061646472657373000000000000006064820152608401610314565b6001546107b4906001600160a01b0316333087610e7e565b6107bc610402565b8411156107ff5760405162461bcd60e51b81526020600482015260116024820152704e6f7420656e6f75676820746f6b656e7360781b6044820152606401610314565b6001600160a01b0385166000908152600460208181526040808420815160808101835289815280840189815292810188815260608201888152835460018181018655948952958820925195909602909101938455915190830155516002808301919091559151600390910155805486929061087b9084906111ca565b90915550506040805185815260208101859052908101839052606081018290526001600160a01b038616907fa6022bfbc5b450715d9f9391a93b6a820b7f97fa4bc1f8077492a5641ae23f209060800160405180910390a25050505050565b6000546001600160a01b0316156109225760405162461bcd60e51b815260206004820152600c60248201526b105b1c9958591e481a5b9a5d60a21b6044820152606401610314565b600080546001600160a01b0319166001600160a01b03841690811782556040519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a33360009081526006602052604090208054600160ff19909116811790915580546001600160a01b0319166001600160a01b039290921691909117905550565b6109b233610baf565b565b6001600160a01b0381166000908152600460209081526040808320805482518185028101850190935280835260609492939192909184015b82821015610a465783829060005260206000209060040201604051806080016040529081600082015481526020016001820154815260200160028201548152602001600382015481525050815260200190600101906109ec565b505050509050919050565b60046020528160005260406000208181548110610a6d57600080fd5b60009182526020909120600490910201805460018201546002830154600390930154919450925084565b33610aaa6000546001600160a01b031690565b6001600160a01b031614610ad05760405162461bcd60e51b815260040161031490611137565b6001600160a01b038216600081815260066020908152604091829020805460ff19168515159081179091558251938452908301527fb1252fa67b4c05afbdaadfae34890b205103c0212a9e062b3978e8cd573631a991016103f6565b33610b3f6000546001600160a01b031690565b6001600160a01b031614610b655760405162461bcd60e51b815260040161031490611137565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6000610bba82610495565b5050905080600014610c23576001600160a01b03821660009081526005602052604081208054839290610bee9084906111ca565b925050819055508060036000828254610c0791906111ca565b9091555050600154610c23906001600160a01b03168383610d63565b816001600160a01b03167f47cee97cb7acd717b3c0aa1435d004cd5b3c8c57d70dbceb4e4458bbd60e39d482604051610c5e91815260200190565b60405180910390a25050565b33610c7d6000546001600160a01b031690565b6001600160a01b031614610ca35760405162461bcd60e51b815260040161031490611137565b6001600160a01b038116610d085760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610314565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1790529151600092839290871691610dbf919061122f565b6000604051808303816000865af19150503d8060008114610dfc576040519150601f19603f3d011682016040523d82523d6000602084013e610e01565b606091505b5091509150818015610e2b575080511580610e2b575080806020019051810190610e2b919061125e565b610e775760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c4544006044820152606401610314565b5050505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092839290881691610ee2919061122f565b6000604051808303816000865af19150503d8060008114610f1f576040519150601f19603f3d011682016040523d82523d6000602084013e610f24565b606091505b5091509150818015610f4e575080511580610f4e575080806020019051810190610f4e919061125e565b610fa65760405162461bcd60e51b8152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416044820152631253115160e21b6064820152608401610314565b505050505050565b80356001600160a01b0381168114610fc557600080fd5b919050565b600060208284031215610fdc57600080fd5b610fe582610fae565b9392505050565b600080600080600060a0868803121561100457600080fd5b61100d86610fae565b97602087013597506040870135966060810135965060800135945092505050565b6000806040838503121561104157600080fd5b61104a83610fae565b915061105860208401610fae565b90509250929050565b602080825282518282018190526000919060409081850190868401855b828110156110b85781518051855286810151878601528581015186860152606090810151908501526080909301929085019060010161107e565b5091979650505050505050565b600080604083850312156110d857600080fd5b6110e183610fae565b946020939093013593505050565b80151581146110fd57600080fd5b50565b6000806040838503121561111357600080fd5b61111c83610fae565b9150602083013561112c816110ef565b809150509250929050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60006020828403121561117e57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156111ae576111ae611185565b92915050565b634e487b7160e01b600052603260045260246000fd5b808201808211156111ae576111ae611185565b6000826111fa57634e487b7160e01b600052601260045260246000fd5b500490565b80820281158282048414176111ae576111ae611185565b60006001820161122857611228611185565b5060010190565b6000825160005b818110156112505760208186018101518583015201611236565b506000920191825250919050565b60006020828403121561127057600080fd5b8151610fe5816110ef56fea2646970667358221220e902dc80791974d51f4733fd79a75daa85eac976f766facb2bf2d93d2d9cff8f64736f6c63430008130033
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106101155760003560e01c8063529849e9116100a2578063889014961161007157806388901496146102685780638da5cb5b1461027b578063d54ad2a11461028c578063eed75f6d14610295578063f2fde38b146102c857600080fd5b8063529849e9146101ef5780635de29741146102225780636f860d741461024d578063715018a61461026057600080fd5b8063431b1acb116100e9578063431b1acb1461019857806345f7f249146101ab578063485cc955146101b45780634e71d92d146101c7578063505a1b31146101cf57600080fd5b8062ae3bf81461011a57806304e869031461012f57806328733bb714610162578063420d4a021461016a575b600080fd5b61012d610128366004610fca565b6102db565b005b61014f61013d366004610fca565b60056020526000908152604090205481565b6040519081526020015b60405180910390f35b61014f610402565b61017d610178366004610fca565b610495565b60408051938452602084019290925290820152606001610159565b61012d6101a6366004610fec565b61065d565b61014f60025481565b61012d6101c236600461102e565b6108da565b61012d6109a9565b6101e26101dd366004610fca565b6109b4565b6040516101599190611061565b6102026101fd3660046110c5565b610a51565b604080519485526020850193909352918301526060820152608001610159565b600154610235906001600160a01b031681565b6040516001600160a01b039091168152602001610159565b61012d61025b366004611100565b610a97565b61012d610b2c565b61012d610276366004610fca565b610baf565b6000546001600160a01b0316610235565b61014f60035481565b6102b86102a3366004610fca565b60066020526000908152604090205460ff1681565b6040519015158152602001610159565b61012d6102d6366004610fca565b610c6a565b336102ee6000546001600160a01b031690565b6001600160a01b03161461031d5760405162461bcd60e51b815260040161031490611137565b60405180910390fd5b6001546000906001600160a01b03908116908316036103455761033e610402565b90506103b0565b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015610389573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ad919061116c565b90505b6103bb823383610d63565b604080516001600160a01b0384168152602081018390527f542fa6bfee3b4746210fbdd1d83f9e49b65adde3639f8d8f165dd18347938af291015b60405180910390a15050565b6001546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa15801561044b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061046f919061116c565b90506000600354600254610483919061119b565b905061048f818361119b565b91505090565b6001600160a01b03811660009081526004602052604081205481906402540be40090825b81811015610622576001600160a01b03861660009081526004602052604081208054839081106104eb576104eb6111b4565b60009182526020918290206040805160808101825260049093029091018054808452600182015494840194909452600281015491830191909152600301546060820152915061053a81876111ca565b95506000428360200151116105e15760008360600151846020015142610560919061119b565b61056a91906111dd565b6105759060016111ca565b90506127108460400151828561058b91906111ff565b61059591906111ff565b61059f91906111dd565b6105a990836111ca565b9150600084602001518560600151836105c291906111ff565b6105cc91906111ca565b9050808811156105da578097505b50506105f5565b82602001518611156105f557826020015195505b818111156106005750805b61060a81896111ca565b9750505050808061061a90611216565b9150506104b9565b5061062d848461119b565b6001600160a01b038616600090815260056020526040902054909350610653908561119b565b9350509193909250565b3360009081526006602052604090205460ff166106bc5760405162461bcd60e51b815260206004820152601760248201527f4f6e6c79206465706f7369746f727320616c6c6f7765640000000000000000006044820152606401610314565b61271082111561070e5760405162461bcd60e51b815260206004820152601b60248201527f496e636f72726563742076657374696e6750657263656e7461676500000000006044820152606401610314565b6001600160a01b03851660009081526004602052604090205460641161079c5760405162461bcd60e51b815260206004820152603960248201527f546f6f206d616e7920616c6c6f636174696f6e7320666f72206f6e652061646460448201527f726573732c2075736520616e6f746865722061646472657373000000000000006064820152608401610314565b6001546107b4906001600160a01b0316333087610e7e565b6107bc610402565b8411156107ff5760405162461bcd60e51b81526020600482015260116024820152704e6f7420656e6f75676820746f6b656e7360781b6044820152606401610314565b6001600160a01b0385166000908152600460208181526040808420815160808101835289815280840189815292810188815260608201888152835460018181018655948952958820925195909602909101938455915190830155516002808301919091559151600390910155805486929061087b9084906111ca565b90915550506040805185815260208101859052908101839052606081018290526001600160a01b038616907fa6022bfbc5b450715d9f9391a93b6a820b7f97fa4bc1f8077492a5641ae23f209060800160405180910390a25050505050565b6000546001600160a01b0316156109225760405162461bcd60e51b815260206004820152600c60248201526b105b1c9958591e481a5b9a5d60a21b6044820152606401610314565b600080546001600160a01b0319166001600160a01b03841690811782556040519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a33360009081526006602052604090208054600160ff19909116811790915580546001600160a01b0319166001600160a01b039290921691909117905550565b6109b233610baf565b565b6001600160a01b0381166000908152600460209081526040808320805482518185028101850190935280835260609492939192909184015b82821015610a465783829060005260206000209060040201604051806080016040529081600082015481526020016001820154815260200160028201548152602001600382015481525050815260200190600101906109ec565b505050509050919050565b60046020528160005260406000208181548110610a6d57600080fd5b60009182526020909120600490910201805460018201546002830154600390930154919450925084565b33610aaa6000546001600160a01b031690565b6001600160a01b031614610ad05760405162461bcd60e51b815260040161031490611137565b6001600160a01b038216600081815260066020908152604091829020805460ff19168515159081179091558251938452908301527fb1252fa67b4c05afbdaadfae34890b205103c0212a9e062b3978e8cd573631a991016103f6565b33610b3f6000546001600160a01b031690565b6001600160a01b031614610b655760405162461bcd60e51b815260040161031490611137565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6000610bba82610495565b5050905080600014610c23576001600160a01b03821660009081526005602052604081208054839290610bee9084906111ca565b925050819055508060036000828254610c0791906111ca565b9091555050600154610c23906001600160a01b03168383610d63565b816001600160a01b03167f47cee97cb7acd717b3c0aa1435d004cd5b3c8c57d70dbceb4e4458bbd60e39d482604051610c5e91815260200190565b60405180910390a25050565b33610c7d6000546001600160a01b031690565b6001600160a01b031614610ca35760405162461bcd60e51b815260040161031490611137565b6001600160a01b038116610d085760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610314565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1790529151600092839290871691610dbf919061122f565b6000604051808303816000865af19150503d8060008114610dfc576040519150601f19603f3d011682016040523d82523d6000602084013e610e01565b606091505b5091509150818015610e2b575080511580610e2b575080806020019051810190610e2b919061125e565b610e775760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c4544006044820152606401610314565b5050505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092839290881691610ee2919061122f565b6000604051808303816000865af19150503d8060008114610f1f576040519150601f19603f3d011682016040523d82523d6000602084013e610f24565b606091505b5091509150818015610f4e575080511580610f4e575080806020019051810190610f4e919061125e565b610fa65760405162461bcd60e51b8152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416044820152631253115160e21b6064820152608401610314565b505050505050565b80356001600160a01b0381168114610fc557600080fd5b919050565b600060208284031215610fdc57600080fd5b610fe582610fae565b9392505050565b600080600080600060a0868803121561100457600080fd5b61100d86610fae565b97602087013597506040870135966060810135965060800135945092505050565b6000806040838503121561104157600080fd5b61104a83610fae565b915061105860208401610fae565b90509250929050565b602080825282518282018190526000919060409081850190868401855b828110156110b85781518051855286810151878601528581015186860152606090810151908501526080909301929085019060010161107e565b5091979650505050505050565b600080604083850312156110d857600080fd5b6110e183610fae565b946020939093013593505050565b80151581146110fd57600080fd5b50565b6000806040838503121561111357600080fd5b61111c83610fae565b9150602083013561112c816110ef565b809150509250929050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60006020828403121561117e57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156111ae576111ae611185565b92915050565b634e487b7160e01b600052603260045260246000fd5b808201808211156111ae576111ae611185565b6000826111fa57634e487b7160e01b600052601260045260246000fd5b500490565b80820281158282048414176111ae576111ae611185565b60006001820161122857611228611185565b5060010190565b6000825160005b818110156112505760208186018101518583015201611236565b506000920191825250919050565b60006020828403121561127057600080fd5b8151610fe5816110ef56fea2646970667358221220e902dc80791974d51f4733fd79a75daa85eac976f766facb2bf2d93d2d9cff8f64736f6c63430008130033