Source Code
Latest 25 from a total of 4,749 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Claim Tokens | 23235566 | 178 days ago | IN | 0 ETH | 0.0010048 | ||||
| Claim Tokens | 23235267 | 178 days ago | IN | 0 ETH | 0.00026745 | ||||
| Claim Tokens | 23235259 | 178 days ago | IN | 0 ETH | 0.00143473 | ||||
| Claim Tokens | 23234655 | 178 days ago | IN | 0 ETH | 0.00052561 | ||||
| Claim Tokens | 23234470 | 178 days ago | IN | 0 ETH | 0.00248074 | ||||
| Claim Tokens | 23234240 | 178 days ago | IN | 0 ETH | 0.0007885 | ||||
| Claim Tokens | 23234095 | 178 days ago | IN | 0 ETH | 0.00117637 | ||||
| Claim Tokens | 23233965 | 178 days ago | IN | 0 ETH | 0.00109698 | ||||
| Claim Tokens | 23233300 | 178 days ago | IN | 0 ETH | 0.00337673 | ||||
| Claim Tokens | 23233259 | 178 days ago | IN | 0 ETH | 0.00329864 | ||||
| Claim Tokens | 23232850 | 178 days ago | IN | 0 ETH | 0.00200933 | ||||
| Claim Tokens | 23232675 | 178 days ago | IN | 0 ETH | 0.00112663 | ||||
| Claim Tokens | 23232517 | 178 days ago | IN | 0 ETH | 0.00119235 | ||||
| Claim Tokens | 23232314 | 178 days ago | IN | 0 ETH | 0.00049772 | ||||
| Claim Tokens | 23232244 | 178 days ago | IN | 0 ETH | 0.00059306 | ||||
| Claim Tokens | 23232228 | 178 days ago | IN | 0 ETH | 0.00062144 | ||||
| Claim Tokens | 23232162 | 178 days ago | IN | 0 ETH | 0.00190967 | ||||
| Claim Tokens | 23232135 | 178 days ago | IN | 0 ETH | 0.00104964 | ||||
| Claim Tokens | 23232105 | 178 days ago | IN | 0 ETH | 0.00198057 | ||||
| Claim Tokens | 23231975 | 178 days ago | IN | 0 ETH | 0.00026565 | ||||
| Claim Tokens | 23231942 | 178 days ago | IN | 0 ETH | 0.00049681 | ||||
| Claim Tokens | 23231922 | 178 days ago | IN | 0 ETH | 0.00053258 | ||||
| Claim Tokens | 23231834 | 178 days ago | IN | 0 ETH | 0.00111175 | ||||
| Claim Tokens | 23231739 | 179 days ago | IN | 0 ETH | 0.00011338 | ||||
| Claim Tokens | 23231737 | 179 days ago | IN | 0 ETH | 0.00043904 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
FeeDistributor
Compiler Version
v0.7.6+commit.7338295f
Optimization Enabled:
Yes with 9999 runs
Other Settings:
istanbul EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import "@openzeppelin-solc-0.7/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin-solc-0.7/contracts/math/Math.sol";
import "@openzeppelin-solc-0.7/contracts/math/SafeMath.sol";
import "@openzeppelin-solc-0.7/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin-solc-0.7/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin-solc-0.7/contracts/access/Ownable.sol";
import "./interfaces/IFeeDistributor.sol";
import "./interfaces/IVotingEscrow.sol";
// solhint-disable not-rely-on-time
/**
* @title Fee Distributor
* @author Balancer Labs. Original version https://github.com/balancer/balancer-v2-monorepo/blob/master/pkg/liquidity-mining/contracts/fee-distribution/FeeDistributor.sol
* @notice Distributes any tokens transferred to the contract (e.g. Protocol fees) among veSTG
* holders proportionally based on a snapshot of the week at which the tokens are sent to the FeeDistributor contract.
* @dev Supports distributing arbitrarily many different tokens. In order to start distributing a new token to veSTG holders call `depositToken`.
*/
contract FeeDistributor is IFeeDistributor, Ownable, ReentrancyGuard {
using SafeMath for uint256;
using SafeERC20 for IERC20;
// gas optimization
uint256 private constant WEEK_MINUS_SECOND = 1 weeks - 1;
IVotingEscrow private immutable _votingEscrow;
uint256 private immutable _startTime;
// Global State
uint256 private _timeCursor;
mapping(uint256 => uint256) private _veSupplyCache;
// Token State
// `startTime` and `timeCursor` are both timestamps so comfortably fit in a uint64.
// `cachedBalance` will comfortably fit the total supply of any meaningful token.
// Should more than 2^128 tokens be sent to this contract then checkpointing this token will fail until enough
// tokens have been claimed to bring the total balance back below 2^128.
struct TokenState {
uint64 startTime;
uint64 timeCursor;
uint128 cachedBalance;
}
mapping(IERC20 => TokenState) private _tokenState;
mapping(IERC20 => mapping(uint256 => uint256)) private _tokensPerWeek;
mapping(IERC20 => bool) private _tokenClaimingEnabled;
// User State
// `startTime` and `timeCursor` are timestamps so will comfortably fit in a uint64.
// For `lastEpochCheckpointed` to overflow would need over 2^128 transactions to the VotingEscrow contract.
struct UserState {
uint64 startTime;
uint64 timeCursor;
uint128 lastEpochCheckpointed;
}
mapping(address => UserState) internal _userState;
mapping(address => mapping(uint256 => uint256)) private _userBalanceAtTimestamp;
mapping(address => mapping(IERC20 => uint256)) private _userTokenTimeCursor;
mapping(address => bool) private _onlyVeHolderClaimingEnabled;
/**
* @dev Reverts if only the VotingEscrow holder can claim their rewards and the given address is a third-party caller.
* @param user - The address to validate as the only allowed caller.
*/
modifier userAllowedToClaim(address user) {
if (_onlyVeHolderClaimingEnabled[user]) {
require(msg.sender == user, "Claiming is not allowed");
}
_;
}
/**
* @dev Reverts if the given token cannot be claimed.
* @param token - The token to check.
*/
modifier tokenCanBeClaimed(IERC20 token) {
_checkIfClaimingEnabled(token);
_;
}
/**
* @dev Reverts if the given tokens cannot be claimed.
* @param tokens - The tokens to check.
*/
modifier tokensCanBeClaimed(IERC20[] calldata tokens) {
uint256 tokensLength = tokens.length;
for (uint256 i = 0; i < tokensLength; ++i) {
_checkIfClaimingEnabled(tokens[i]);
}
_;
}
constructor(IVotingEscrow votingEscrow, uint256 startTime) {
_votingEscrow = votingEscrow;
startTime = _roundDownTimestamp(startTime);
uint256 currentWeek = _roundDownTimestamp(block.timestamp);
require(startTime >= currentWeek, "Cannot start before current week");
IVotingEscrow.Point memory pt = votingEscrow.point_history(0);
require(startTime > pt.ts, "Cannot start before VotingEscrow first epoch");
_startTime = startTime;
_timeCursor = startTime;
}
/**
* @notice Returns the VotingEscrow (veSTG) token contract
*/
function getVotingEscrow() external view override returns (IVotingEscrow) {
return _votingEscrow;
}
/**
* @notice Returns the time when fee distribution starts.
*/
function getStartTime() external view override returns (uint256) {
return _startTime;
}
/**
* @notice Returns the global time cursor representing the most earliest uncheckpointed week.
*/
function getTimeCursor() external view override returns (uint256) {
return _timeCursor;
}
/**
* @notice Returns the user-level start time representing the first week they're eligible to claim tokens.
* @param user - The address of the user to query.
*/
function getUserStartTime(address user) external view override returns (uint256) {
return _userState[user].startTime;
}
/**
* @notice Returns the user-level time cursor representing the most earliest uncheckpointed week.
* @param user - The address of the user to query.
*/
function getUserTimeCursor(address user) external view override returns (uint256) {
return _userState[user].timeCursor;
}
/**
* @notice Returns the user-level last checkpointed epoch.
* @param user - The address of the user to query.
*/
function getUserLastEpochCheckpointed(address user) external view override returns (uint256) {
return _userState[user].lastEpochCheckpointed;
}
/**
* @notice True if the given token can be claimed, false otherwise.
* @param token - The ERC20 token address to query.
*/
function canTokenBeClaimed(IERC20 token) external view override returns (bool) {
return _tokenClaimingEnabled[token];
}
/**
* @notice Returns the token-level start time representing the timestamp users could start claiming this token
* @param token - The ERC20 token address to query.
*/
function getTokenStartTime(IERC20 token) external view override returns (uint256) {
return _tokenState[token].startTime;
}
/**
* @notice Returns the token-level time cursor storing the timestamp at up to which tokens have been distributed.
* @param token - The ERC20 token address to query.
*/
function getTokenTimeCursor(IERC20 token) external view override returns (uint256) {
return _tokenState[token].timeCursor;
}
/**
* @notice Returns the token-level cached balance.
* @param token - The ERC20 token address to query.
*/
function getTokenCachedBalance(IERC20 token) external view override returns (uint256) {
return _tokenState[token].cachedBalance;
}
/**
* @notice Returns the user-level time cursor storing the timestamp of the latest token distribution claimed.
* @param user - The address of the user to query.
* @param token - The ERC20 token address to query.
*/
function getUserTokenTimeCursor(address user, IERC20 token) external view override returns (uint256) {
return _getUserTokenTimeCursor(user, token);
}
/**
* @notice Returns the user's cached balance of veSTG as of the provided timestamp.
* @dev Only timestamps which fall on Thursdays 00:00:00 UTC will return correct values.
* This function requires `user` to have been checkpointed past `timestamp` so that their balance is cached.
* @param user - The address of the user of which to read the cached balance of.
* @param timestamp - The timestamp at which to read the `user`'s cached balance at.
*/
function getUserBalanceAtTimestamp(address user, uint256 timestamp) external view override returns (uint256) {
return _userBalanceAtTimestamp[user][timestamp];
}
/**
* @notice Returns the cached total supply of veSTG as of the provided timestamp.
* @dev Only timestamps which fall on Thursdays 00:00:00 UTC will return correct values.
* This function requires the contract to have been checkpointed past `timestamp` so that the supply is cached.
* @param timestamp - The timestamp at which to read the cached total supply at.
*/
function getTotalSupplyAtTimestamp(uint256 timestamp) external view override returns (uint256) {
return _veSupplyCache[timestamp];
}
/**
* @notice Returns the FeeDistributor's cached balance of `token`.
*/
function getTokenLastBalance(IERC20 token) external view override returns (uint256) {
return _tokenState[token].cachedBalance;
}
/**
* @notice Returns the amount of `token` which the FeeDistributor received in the week beginning at `timestamp`.
* @param token - The ERC20 token address to query.
* @param timestamp - The timestamp corresponding to the beginning of the week of interest.
*/
function getTokensDistributedInWeek(IERC20 token, uint256 timestamp) external view override returns (uint256) {
return _tokensPerWeek[token][timestamp];
}
// Preventing third-party claiming
/**
* @notice Enables / disables rewards claiming only by the VotingEscrow holder for the message sender.
* @param enabled - True if only the VotingEscrow holder can claim their rewards, false otherwise.
*/
function enableOnlyVeHolderClaiming(bool enabled) external override {
_onlyVeHolderClaimingEnabled[msg.sender] = enabled;
emit OnlyVeHolderClaimingEnabled(msg.sender, enabled);
}
/**
* @notice Returns true if only the VotingEscrow holder can claim their rewards, false otherwise.
*/
function onlyVeHolderClaimingEnabled(address user) external view override returns (bool) {
return _onlyVeHolderClaimingEnabled[user];
}
// Depositing
/**
* @notice Deposits tokens to be distributed in the current week.
* @dev Sending tokens directly to the FeeDistributor instead of using `depositToken` may result in tokens being
* retroactively distributed to past weeks, or for the distribution to carry over to future weeks.
*
* If for some reason `depositToken` cannot be called, in order to ensure that all tokens are correctly distributed
* manually call `checkpointToken` before and after the token transfer.
* @param token - The ERC20 token address to distribute.
* @param amount - The amount of tokens to deposit.
*/
function depositToken(IERC20 token, uint256 amount) external override nonReentrant tokenCanBeClaimed(token) {
_checkpointToken(token, false);
token.safeTransferFrom(msg.sender, address(this), amount);
_checkpointToken(token, true);
}
/**
* @notice Deposits tokens to be distributed in the current week.
* @dev A version of `depositToken` which supports depositing multiple `tokens` at once.
* See `depositToken` for more details.
* @param tokens - An array of ERC20 token addresses to distribute.
* @param amounts - An array of token amounts to deposit.
*/
function depositTokens(IERC20[] calldata tokens, uint256[] calldata amounts) external override nonReentrant {
require(tokens.length == amounts.length, "Input length mismatch");
uint256 length = tokens.length;
for (uint256 i = 0; i < length; ++i) {
_checkIfClaimingEnabled(tokens[i]);
_checkpointToken(tokens[i], false);
tokens[i].safeTransferFrom(msg.sender, address(this), amounts[i]);
_checkpointToken(tokens[i], true);
}
}
// Checkpointing
/**
* @notice Caches the total supply of veSTG at the beginning of each week.
* This function will be called automatically before claiming tokens to ensure the contract is properly updated.
*/
function checkpoint() external override nonReentrant {
_checkpointTotalSupply();
}
/**
* @notice Caches the user's balance of veSTG at the beginning of each week.
* This function will be called automatically before claiming tokens to ensure the contract is properly updated.
* @param user - The address of the user to be checkpointed.
*/
function checkpointUser(address user) external override nonReentrant {
_checkpointUserBalance(user);
}
/**
* @notice Assigns any newly-received tokens held by the FeeDistributor to weekly distributions.
* @dev Any `token` balance held by the FeeDistributor above that which is returned by `getTokenLastBalance`
* will be distributed evenly across the time period since `token` was last checkpointed.
*
* This function will be called automatically before claiming tokens to ensure the contract is properly updated.
* @param token - The ERC20 token address to be checkpointed.
*/
function checkpointToken(IERC20 token) external override nonReentrant tokenCanBeClaimed(token) {
_checkpointToken(token, true);
}
/**
* @notice Assigns any newly-received tokens held by the FeeDistributor to weekly distributions.
* @dev A version of `checkpointToken` which supports checkpointing multiple tokens.
* See `checkpointToken` for more details.
* @param tokens - An array of ERC20 token addresses to be checkpointed.
*/
function checkpointTokens(IERC20[] calldata tokens) external override nonReentrant {
uint256 tokensLength = tokens.length;
for (uint256 i = 0; i < tokensLength; ++i) {
_checkIfClaimingEnabled(tokens[i]);
_checkpointToken(tokens[i], true);
}
}
// Claiming
/**
* @notice Claims all pending distributions of the provided token for a user.
* @dev It's not necessary to explicitly checkpoint before calling this function, it will ensure the FeeDistributor
* is up to date before calculating the amount of tokens to be claimed.
* @param user - The user on behalf of which to claim.
* @param token - The ERC20 token address to be claimed.
* @return The amount of `token` sent to `user` as a result of claiming.
*/
function claimToken(address user, IERC20 token) external override nonReentrant userAllowedToClaim(user) tokenCanBeClaimed(token) returns (uint256) {
_checkpointTotalSupply();
_checkpointUserBalance(user);
_checkpointToken(token, false);
return _claimToken(user, token);
}
/**
* @notice Claims a number of tokens on behalf of a user.
* @dev A version of `claimToken` which supports claiming multiple `tokens` on behalf of `user`.
* See `claimToken` for more details.
* @param user - The user on behalf of which to claim.
* @param tokens - An array of ERC20 token addresses to be claimed.
* @return An array of the amounts of each token in `tokens` sent to `user` as a result of claiming.
*/
function claimTokens(address user, IERC20[] calldata tokens) external override nonReentrant userAllowedToClaim(user) tokensCanBeClaimed(tokens) returns (uint256[] memory) {
_checkpointTotalSupply();
_checkpointUserBalance(user);
uint256 tokensLength = tokens.length;
uint256[] memory amounts = new uint256[](tokensLength);
for (uint256 i = 0; i < tokensLength; ++i) {
_checkpointToken(tokens[i], false);
amounts[i] = _claimToken(user, tokens[i]);
}
return amounts;
}
// Governance
/**
* @notice Withdraws the specified `amount` of the `token` from the contract to the `recipient`. Can be called only by Stargate DAO.
* @param token - The token to withdraw.
* @param amount - The amount to withdraw.
* @param recipient - The address to transfer the tokens to.
*/
function withdrawToken(IERC20 token, uint256 amount, address recipient) external override onlyOwner {
token.safeTransfer(recipient, amount);
emit TokenWithdrawn(token, amount, recipient);
}
/**
* @notice Enables or disables claiming of the given token. Can be called only by Stargate DAO.
* @param token - The token to enable or disable claiming.
* @param enable - True if the token can be claimed, false otherwise.
*/
function enableTokenClaiming(IERC20 token, bool enable) external override onlyOwner {
_tokenClaimingEnabled[token] = enable;
emit TokenClaimingEnabled(token, enable);
}
// Internal functions
/**
* @dev It is required that both the global, token and user state have been properly checkpointed
* before calling this function.
*/
function _claimToken(address user, IERC20 token) internal returns (uint256) {
TokenState storage tokenState = _tokenState[token];
uint256 nextUserTokenWeekToClaim = _getUserTokenTimeCursor(user, token);
// The first week which cannot be correctly claimed is the earliest of:
// - A) The global or user time cursor (whichever is earliest), rounded up to the end of the week.
// - B) The token time cursor, rounded down to the beginning of the week.
//
// This prevents the two failure modes:
// - A) A user may claim a week for which we have not processed their balance, resulting in tokens being locked.
// - B) A user may claim a week which then receives more tokens to be distributed. However the user has
// already claimed for that week so their share of these new tokens are lost.
uint256 firstUnclaimableWeek = Math.min(_roundUpTimestamp(Math.min(_timeCursor, _userState[user].timeCursor)), _roundDownTimestamp(tokenState.timeCursor));
mapping(uint256 => uint256) storage tokensPerWeek = _tokensPerWeek[token];
mapping(uint256 => uint256) storage userBalanceAtTimestamp = _userBalanceAtTimestamp[user];
uint256 amount;
for (uint256 i = 0; i < 20; ++i) {
// We clearly cannot claim for `firstUnclaimableWeek` and so we break here.
if (nextUserTokenWeekToClaim >= firstUnclaimableWeek) break;
amount += (tokensPerWeek[nextUserTokenWeekToClaim] * userBalanceAtTimestamp[nextUserTokenWeekToClaim]) / _veSupplyCache[nextUserTokenWeekToClaim];
nextUserTokenWeekToClaim += 1 weeks;
}
// Update the stored user-token time cursor to prevent this user claiming this week again.
_userTokenTimeCursor[user][token] = nextUserTokenWeekToClaim;
if (amount > 0) {
// For a token to be claimable it must have been added to the cached balance so this is safe.
tokenState.cachedBalance = uint128(tokenState.cachedBalance - amount);
token.safeTransfer(user, amount);
emit TokensClaimed(user, token, amount, nextUserTokenWeekToClaim);
}
return amount;
}
/**
* @dev Calculate the amount of `token` to be distributed to `_votingEscrow` holders since the last checkpoint.
*/
function _checkpointToken(IERC20 token, bool force) internal {
TokenState storage tokenState = _tokenState[token];
uint256 lastTokenTime = tokenState.timeCursor;
uint256 timeSinceLastCheckpoint;
if (lastTokenTime == 0) {
// Prevent someone from assigning tokens to an inaccessible week.
require(block.timestamp > _startTime, "Fee distribution has not started yet");
// If it's the first time we're checkpointing this token then start distributing from now.
// Also mark at which timestamp users should start attempts to claim this token from.
lastTokenTime = block.timestamp;
tokenState.startTime = uint64(_roundDownTimestamp(block.timestamp));
} else {
timeSinceLastCheckpoint = block.timestamp - lastTokenTime;
if (!force) {
// Checkpointing N times within a single week is completely equivalent to checkpointing once at the end.
// We then want to get as close as possible to a single checkpoint every Wed 23:59 UTC to save gas.
// We then skip checkpointing if we're in the same week as the previous checkpoint.
bool alreadyCheckpointedThisWeek = _roundDownTimestamp(block.timestamp) == _roundDownTimestamp(lastTokenTime);
// However we want to ensure that all of this week's fees are assigned to the current week without
// overspilling into the next week. To mitigate this, we checkpoint if we're near the end of the week.
bool nearingEndOfWeek = _roundUpTimestamp(block.timestamp) - block.timestamp < 1 days;
// This ensures that we checkpoint once at the beginning of the week and again for each user interaction
// towards the end of the week to give an accurate final reading of the balance.
if (alreadyCheckpointedThisWeek && !nearingEndOfWeek) {
return;
}
}
}
tokenState.timeCursor = uint64(block.timestamp);
uint256 tokenBalance = token.balanceOf(address(this));
uint256 newTokensToDistribute = tokenBalance.sub(tokenState.cachedBalance);
if (newTokensToDistribute == 0) return;
require(tokenBalance <= type(uint128).max, "Maximum token balance exceeded");
tokenState.cachedBalance = uint128(tokenBalance);
uint256 firstIncompleteWeek = _roundDownTimestamp(lastTokenTime);
uint256 nextWeek = 0;
// Distribute `newTokensToDistribute` evenly across the time period from `lastTokenTime` to now.
// These tokens are assigned to weeks proportionally to how much of this period falls into each week.
mapping(uint256 => uint256) storage tokensPerWeek = _tokensPerWeek[token];
for (uint256 i = 0; i < 20; ++i) {
// This is safe as we're incrementing a timestamp.
nextWeek = firstIncompleteWeek + 1 weeks;
if (block.timestamp < nextWeek) {
// `firstIncompleteWeek` is now the beginning of the current week, i.e. this is the final iteration.
if (timeSinceLastCheckpoint == 0 && block.timestamp == lastTokenTime) {
tokensPerWeek[firstIncompleteWeek] += newTokensToDistribute;
} else {
// block.timestamp >= lastTokenTime by definition.
tokensPerWeek[firstIncompleteWeek] += (newTokensToDistribute * (block.timestamp - lastTokenTime)) / timeSinceLastCheckpoint;
}
// As we've caught up to the present then we should now break.
break;
} else {
// We've gone a full week or more without checkpointing so need to distribute tokens to previous weeks.
if (timeSinceLastCheckpoint == 0 && nextWeek == lastTokenTime) {
// It shouldn't be possible to enter this block
tokensPerWeek[firstIncompleteWeek] += newTokensToDistribute;
} else {
// nextWeek > lastTokenTime by definition.
tokensPerWeek[firstIncompleteWeek] += (newTokensToDistribute * (nextWeek - lastTokenTime)) / timeSinceLastCheckpoint;
}
}
// We've now "checkpointed" up to the beginning of next week so must update timestamps appropriately.
lastTokenTime = nextWeek;
firstIncompleteWeek = nextWeek;
}
emit TokenCheckpointed(token, newTokensToDistribute, lastTokenTime);
}
/**
* @dev Cache the `user`'s balance of `_votingEscrow` at the beginning of each new week
*/
function _checkpointUserBalance(address user) internal {
uint256 maxUserEpoch = _votingEscrow.user_point_epoch(user);
// If user has no epochs then they have never locked STG.
// They clearly will not then receive fees.
require(maxUserEpoch > 0, "veSTG balance is zero");
UserState storage userState = _userState[user];
// `nextWeekToCheckpoint` represents the timestamp of the beginning of the first week
// which we haven't checkpointed the user's VotingEscrow balance yet.
uint256 nextWeekToCheckpoint = userState.timeCursor;
uint256 userEpoch;
if (nextWeekToCheckpoint == 0) {
// First checkpoint for user so need to do the initial binary search
userEpoch = _findTimestampUserEpoch(user, _startTime, 0, maxUserEpoch);
} else {
if (nextWeekToCheckpoint >= block.timestamp) {
// User has checkpointed the current week already so perform early return.
// This prevents a user from processing epochs created later in this week, however this is not an issue
// as if a significant number of these builds up then the user will skip past them with a binary search.
return;
}
// Otherwise use the value saved from last time
userEpoch = userState.lastEpochCheckpointed;
// This optimizes a scenario common for power users, which have frequent `VotingEscrow` interactions in
// the same week. We assume that any such user is also claiming fees every week, and so we only perform
// a binary search here rather than integrating it into the main search algorithm, effectively skipping
// most of the week's irrelevant checkpoints.
// The slight tradeoff is that users who have multiple infrequent `VotingEscrow` interactions and also don't
// claim frequently will also perform the binary search, despite it not leading to gas savings.
if (maxUserEpoch - userEpoch > 20) {
userEpoch = _findTimestampUserEpoch(user, nextWeekToCheckpoint, userEpoch, maxUserEpoch);
}
}
// Epoch 0 is always empty so bump onto the next one so that we start on a valid epoch.
if (userEpoch == 0) {
userEpoch = 1;
}
IVotingEscrow.Point memory nextUserPoint = _votingEscrow.user_point_history(user, userEpoch);
// If this is the first checkpoint for the user, calculate the first week they're eligible for.
// i.e. the timestamp of the first Thursday after they locked.
// If this is earlier then the first distribution then fast forward to then.
if (nextWeekToCheckpoint == 0) {
// Disallow checkpointing before `startTime`.
require(block.timestamp > _startTime, "Fee distribution has not started yet");
nextWeekToCheckpoint = Math.max(_startTime, _roundUpTimestamp(nextUserPoint.ts));
userState.startTime = uint64(nextWeekToCheckpoint);
}
// It's safe to increment `userEpoch` and `nextWeekToCheckpoint` in this loop as epochs and timestamps
// are always much smaller than 2^256 and are being incremented by small values.
IVotingEscrow.Point memory currentUserPoint;
for (uint256 i = 0; i < 50; ++i) {
if (nextWeekToCheckpoint >= nextUserPoint.ts && userEpoch <= maxUserEpoch) {
// The week being considered is contained in a user epoch after that described by `currentUserPoint`.
// We then shift `nextUserPoint` into `currentUserPoint` and query the Point for the next user epoch.
// We do this in order to step though epochs until we find the first epoch starting after
// `nextWeekToCheckpoint`, making the previous epoch the one that contains `nextWeekToCheckpoint`.
userEpoch += 1;
currentUserPoint = nextUserPoint;
if (userEpoch > maxUserEpoch) {
nextUserPoint = IVotingEscrow.Point(0, 0, 0, 0);
} else {
nextUserPoint = _votingEscrow.user_point_history(user, userEpoch);
}
} else {
// The week being considered lies inside the user epoch described by `oldUserPoint`
// we can then use it to calculate the user's balance at the beginning of the week.
if (nextWeekToCheckpoint >= block.timestamp) {
// Break if we're trying to cache the user's balance at a timestamp in the future.
// We only perform this check here to ensure that we can still process checkpoints created
// in the current week.
break;
}
int128 dt = int128(nextWeekToCheckpoint - currentUserPoint.ts);
uint256 userBalance = currentUserPoint.bias > currentUserPoint.slope * dt ? uint256(currentUserPoint.bias - currentUserPoint.slope * dt) : 0;
// User's lock has expired and they haven't relocked yet.
if (userBalance == 0 && userEpoch > maxUserEpoch) {
nextWeekToCheckpoint = _roundUpTimestamp(block.timestamp);
break;
}
// User had a nonzero lock and so is eligible to collect fees.
_userBalanceAtTimestamp[user][nextWeekToCheckpoint] = userBalance;
nextWeekToCheckpoint += 1 weeks;
}
}
// We subtract off 1 from the userEpoch to step back once so that on the next attempt to checkpoint
// the current `currentUserPoint` will be loaded as `nextUserPoint`. This ensures that we can't skip over the
// user epoch containing `nextWeekToCheckpoint`.
// userEpoch > 0 so this is safe.
userState.lastEpochCheckpointed = uint64(userEpoch - 1);
userState.timeCursor = uint64(nextWeekToCheckpoint);
}
/**
* @dev Cache the totalSupply of VotingEscrow token at the beginning of each new week
*/
function _checkpointTotalSupply() internal {
uint256 nextWeekToCheckpoint = _timeCursor;
uint256 weekStart = _roundDownTimestamp(block.timestamp);
// We expect `timeCursor == weekStart + 1 weeks` when fully up to date.
if (nextWeekToCheckpoint > weekStart || weekStart == block.timestamp) {
// We've already checkpointed up to this week so perform early return
return;
}
_votingEscrow.checkpoint();
// Step through the each week and cache the total supply at beginning of week on this contract
for (uint256 i = 0; i < 20; ++i) {
if (nextWeekToCheckpoint > weekStart) break;
// NOTE: Replaced Balancer's logic with Solidly/Velodrome implementation due to the differences in the VotingEscrow totalSupply function
// See https://github.com/velodrome-finance/v1/blob/master/contracts/RewardsDistributor.sol#L143
uint256 epoch = _findTimestampEpoch(nextWeekToCheckpoint);
IVotingEscrow.Point memory pt = _votingEscrow.point_history(epoch);
int128 dt = nextWeekToCheckpoint > pt.ts ? int128(nextWeekToCheckpoint - pt.ts) : 0;
int128 supply = pt.bias - pt.slope * dt;
_veSupplyCache[nextWeekToCheckpoint] = supply > 0 ? uint256(supply) : 0;
// This is safe as we're incrementing a timestamp
nextWeekToCheckpoint += 1 weeks;
}
// Update state to the end of the current week (`weekStart` + 1 weeks)
_timeCursor = nextWeekToCheckpoint;
}
// Helper functions
/**
* @dev Wrapper around `_userTokenTimeCursor` which returns the start timestamp for `token`
* if `user` has not attempted to interact with it previously.
*/
function _getUserTokenTimeCursor(address user, IERC20 token) internal view returns (uint256) {
uint256 userTimeCursor = _userTokenTimeCursor[user][token];
if (userTimeCursor > 0) return userTimeCursor;
// This is the first time that the user has interacted with this token.
// We then start from the latest out of either when `user` first locked veSTG or `token` was first checkpointed.
return Math.max(_userState[user].startTime, _tokenState[token].startTime);
}
/**
* @dev Return the user epoch number for `user` corresponding to the provided `timestamp`
*/
function _findTimestampUserEpoch(address user, uint256 timestamp, uint256 minUserEpoch, uint256 maxUserEpoch) internal view returns (uint256) {
uint256 min = minUserEpoch;
uint256 max = maxUserEpoch;
// Perform binary search through epochs to find epoch containing `timestamp`
for (uint256 i = 0; i < 128; ++i) {
if (min >= max) break;
// Algorithm assumes that inputs are less than 2^128 so this operation is safe.
// +2 avoids getting stuck in min == mid < max
uint256 mid = (min + max + 2) / 2;
IVotingEscrow.Point memory pt = _votingEscrow.user_point_history(user, mid);
if (pt.ts <= timestamp) {
min = mid;
} else {
// max > min so this is safe.
max = mid - 1;
}
}
return min;
}
/**
* @dev Return the global epoch number corresponding to the provided `timestamp`
*/
function _findTimestampEpoch(uint256 timestamp) internal view returns (uint256) {
uint256 min = 0;
uint256 max = _votingEscrow.epoch();
// Perform binary search through epochs to find epoch containing `timestamp`
for (uint256 i = 0; i < 128; i++) {
if (min >= max) break;
// Algorithm assumes that inputs are less than 2^128 so this operation is safe.
// +2 avoids getting stuck in min == mid < max
uint256 mid = (min + max + 2) / 2;
IVotingEscrow.Point memory pt = _votingEscrow.point_history(mid);
if (pt.ts <= timestamp) {
min = mid;
} else {
max = mid - 1;
}
}
return min;
}
/**
* @dev Rounds the provided timestamp down to the beginning of the previous week (Thurs 00:00 UTC)
*/
function _roundDownTimestamp(uint256 timestamp) private pure returns (uint256) {
// Division by zero or overflows are impossible here.
return (timestamp / 1 weeks) * 1 weeks;
}
/**
* @dev Rounds the provided timestamp up to the beginning of the next week (Thurs 00:00 UTC)
*/
function _roundUpTimestamp(uint256 timestamp) private pure returns (uint256) {
// Overflows are impossible here for all realistic inputs.
return _roundDownTimestamp(timestamp + WEEK_MINUS_SECOND);
}
/**
* @dev Reverts if the provided token cannot be claimed.
*/
function _checkIfClaimingEnabled(IERC20 token) private view {
require(_tokenClaimingEnabled[token], "Token is not allowed");
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
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;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <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, so we distribute
return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when 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.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
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'
// solhint-disable-next-line max-line-length
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).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_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
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(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");
// solhint-disable-next-line avoid-low-level-calls
(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");
// solhint-disable-next-line avoid-low-level-calls
(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");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <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 GSN 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 payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <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 () internal {
_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 make 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;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.7.0 <0.9.0;
pragma experimental ABIEncoderV2;
import "@openzeppelin-solc-0.7/contracts/token/ERC20/IERC20.sol";
import "./IVotingEscrow.sol";
/**
* @title Fee Distributor
* @notice Distributes any tokens transferred to the contract (e.g. Protocol fees) among veSTG
* holders proportionally based on a snapshot of the week at which the tokens are sent to the FeeDistributor contract.
* @dev Supports distributing arbitrarily many different tokens. In order to start distributing a new token to veSTG
* holders simply transfer the tokens to the `FeeDistributor` contract and then call `checkpointToken`.
*/
interface IFeeDistributor {
event TokenCheckpointed(IERC20 token, uint256 amount, uint256 lastCheckpointTimestamp);
event TokensClaimed(address user, IERC20 token, uint256 amount, uint256 userTokenTimeCursor);
event TokenWithdrawn(IERC20 token, uint256 amount, address recipient);
event TokenClaimingEnabled(IERC20 token, bool enabled);
event OnlyVeHolderClaimingEnabled(address user, bool enabled);
/**
* @notice Returns the VotingEscrow (veSTG) token contract
*/
function getVotingEscrow() external view returns (IVotingEscrow);
/**
* @notice Returns the time when fee distribution starts.
*/
function getStartTime() external view returns (uint256);
/**
* @notice Returns the global time cursor representing the most earliest uncheckpointed week.
*/
function getTimeCursor() external view returns (uint256);
/**
* @notice Returns the user-level time cursor representing the most earliest uncheckpointed week.
* @param user - The address of the user to query.
*/
function getUserTimeCursor(address user) external view returns (uint256);
/**
* @notice Returns the user-level start time representing the first week they're eligible to claim tokens.
* @param user - The address of the user to query.
*/
function getUserStartTime(address user) external view returns (uint256);
/**
* @notice True if the given token can be claimed, false otherwise.
* @param token - The ERC20 token address to query.
*/
function canTokenBeClaimed(IERC20 token) external view returns (bool);
/**
* @notice Returns the token-level start time representing the timestamp users could start claiming this token
* @param token - The ERC20 token address to query.
*/
function getTokenStartTime(IERC20 token) external view returns (uint256);
/**
* @notice Returns the token-level time cursor storing the timestamp at up to which tokens have been distributed.
* @param token - The ERC20 token address to query.
*/
function getTokenTimeCursor(IERC20 token) external view returns (uint256);
/**
* @notice Returns the token-level cached balance.
* @param token - The ERC20 token address to query.
*/
function getTokenCachedBalance(IERC20 token) external view returns (uint256);
/**
* @notice Returns the user-level last checkpointed epoch.
* @param user - The address of the user to query.
*/
function getUserLastEpochCheckpointed(address user) external view returns (uint256);
/**
* @notice Returns the user-level time cursor storing the timestamp of the latest token distribution claimed.
* @param user - The address of the user to query.
* @param token - The ERC20 token address to query.
*/
function getUserTokenTimeCursor(address user, IERC20 token) external view returns (uint256);
/**
* @notice Returns the user's cached balance of veSTG as of the provided timestamp.
* @dev Only timestamps which fall on Thursdays 00:00:00 UTC will return correct values.
* This function requires `user` to have been checkpointed past `timestamp` so that their balance is cached.
* @param user - The address of the user of which to read the cached balance of.
* @param timestamp - The timestamp at which to read the `user`'s cached balance at.
*/
function getUserBalanceAtTimestamp(address user, uint256 timestamp) external view returns (uint256);
/**
* @notice Returns the cached total supply of veSTG as of the provided timestamp.
* @dev Only timestamps which fall on Thursdays 00:00:00 UTC will return correct values.
* This function requires the contract to have been checkpointed past `timestamp` so that the supply is cached.
* @param timestamp - The timestamp at which to read the cached total supply at.
*/
function getTotalSupplyAtTimestamp(uint256 timestamp) external view returns (uint256);
/**
* @notice Returns the FeeDistributor's cached balance of `token`.
*/
function getTokenLastBalance(IERC20 token) external view returns (uint256);
/**
* @notice Returns the amount of `token` which the FeeDistributor received in the week beginning at `timestamp`.
* @param token - The ERC20 token address to query.
* @param timestamp - The timestamp corresponding to the beginning of the week of interest.
*/
function getTokensDistributedInWeek(IERC20 token, uint256 timestamp) external view returns (uint256);
// Preventing third-party claiming
/**
* @notice Enables / disables rewards claiming only by the VotingEscrow holder for the message sender.
* @param enabled - True if only the VotingEscrow holder can claim their rewards, false otherwise.
*/
function enableOnlyVeHolderClaiming(bool enabled) external;
/**
* @notice Returns true if only the VotingEscrow holder can claim their rewards, false otherwise.
*/
function onlyVeHolderClaimingEnabled(address user) external view returns (bool);
// Depositing
/**
* @notice Deposits tokens to be distributed in the current week.
* @dev Sending tokens directly to the FeeDistributor instead of using `depositTokens` may result in tokens being
* retroactively distributed to past weeks, or for the distribution to carry over to future weeks.
*
* If for some reason `depositTokens` cannot be called, in order to ensure that all tokens are correctly distributed
* manually call `checkpointToken` before and after the token transfer.
* @param token - The ERC20 token address to distribute.
* @param amount - The amount of tokens to deposit.
*/
function depositToken(IERC20 token, uint256 amount) external;
/**
* @notice Deposits tokens to be distributed in the current week.
* @dev A version of `depositToken` which supports depositing multiple `tokens` at once.
* See `depositToken` for more details.
* @param tokens - An array of ERC20 token addresses to distribute.
* @param amounts - An array of token amounts to deposit.
*/
function depositTokens(IERC20[] calldata tokens, uint256[] calldata amounts) external;
// Checkpointing
/**
* @notice Caches the total supply of veSTG at the beginning of each week.
* This function will be called automatically before claiming tokens to ensure the contract is properly updated.
*/
function checkpoint() external;
/**
* @notice Caches the user's balance of veSTG at the beginning of each week.
* This function will be called automatically before claiming tokens to ensure the contract is properly updated.
* @param user - The address of the user to be checkpointed.
*/
function checkpointUser(address user) external;
/**
* @notice Assigns any newly-received tokens held by the FeeDistributor to weekly distributions.
* @dev Any `token` balance held by the FeeDistributor above that which is returned by `getTokenLastBalance`
* will be distributed evenly across the time period since `token` was last checkpointed.
*
* This function will be called automatically before claiming tokens to ensure the contract is properly updated.
* @param token - The ERC20 token address to be checkpointed.
*/
function checkpointToken(IERC20 token) external;
/**
* @notice Assigns any newly-received tokens held by the FeeDistributor to weekly distributions.
* @dev A version of `checkpointToken` which supports checkpointing multiple tokens.
* See `checkpointToken` for more details.
* @param tokens - An array of ERC20 token addresses to be checkpointed.
*/
function checkpointTokens(IERC20[] calldata tokens) external;
// Claiming
/**
* @notice Claims all pending distributions of the provided token for a user.
* @dev It's not necessary to explicitly checkpoint before calling this function, it will ensure the FeeDistributor
* is up to date before calculating the amount of tokens to be claimed.
* @param user - The user on behalf of which to claim.
* @param token - The ERC20 token address to be claimed.
* @return The amount of `token` sent to `user` as a result of claiming.
*/
function claimToken(address user, IERC20 token) external returns (uint256);
/**
* @notice Claims a number of tokens on behalf of a user.
* @dev A version of `claimToken` which supports claiming multiple `tokens` on behalf of `user`.
* See `claimToken` for more details.
* @param user - The user on behalf of which to claim.
* @param tokens - An array of ERC20 token addresses to be claimed.
* @return An array of the amounts of each token in `tokens` sent to `user` as a result of claiming.
*/
function claimTokens(address user, IERC20[] calldata tokens) external returns (uint256[] memory);
// Governance
/**
* @notice Withdraws the specified `amount` of the `token` from the contract to the `recipient`. Can be called only by Stargate DAO.
* @param token - The token to withdraw.
* @param amount - The amount to withdraw.
* @param recipient - The address to transfer the tokens to.
*/
function withdrawToken(IERC20 token, uint256 amount, address recipient) external;
/**
* @notice Enables or disables claiming of the given token. Can be called only by Stargate DAO.
* @param token - The token to enable or disable claiming.
* @param enable - True if the token can be claimed, false otherwise.
*/
function enableTokenClaiming(IERC20 token, bool enable) external;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.7.0 <0.9.0;
pragma experimental ABIEncoderV2;
// For compatibility, we're keeping the same function names as in the original Curve code, including the mixed-case
// naming convention.
// solhint-disable func-name-mixedcase
interface IVotingEscrow {
struct Point {
int128 bias;
int128 slope; // - dweight / dt
uint256 ts;
uint256 blk; // block
}
function epoch() external view returns (uint256);
function balanceOfAtT(address user, uint256 timestamp) external view returns (uint256);
function totalSupplyAtT(uint256 timestamp) external view returns (uint256);
function user_point_epoch(address user) external view returns (uint256);
function point_history(uint256 timestamp) external view returns (Point memory);
function user_point_history(address user, uint256 timestamp) external view returns (Point memory);
function checkpoint() external;
function locked__end(address user) external view returns (uint256);
}{
"evmVersion": "istanbul",
"optimizer": {
"enabled": true,
"runs": 9999
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract IVotingEscrow","name":"votingEscrow","type":"address"},{"internalType":"uint256","name":"startTime","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"OnlyVeHolderClaimingEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lastCheckpointTimestamp","type":"uint256"}],"name":"TokenCheckpointed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"TokenClaimingEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"}],"name":"TokenWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"userTokenTimeCursor","type":"uint256"}],"name":"TokensClaimed","type":"event"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"canTokenBeClaimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"checkpointToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"}],"name":"checkpointTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"checkpointUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"claimToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"}],"name":"claimTokens","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"depositTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"enableOnlyVeHolderClaiming","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"bool","name":"enable","type":"bool"}],"name":"enableTokenClaiming","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTimeCursor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"getTokenCachedBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"getTokenLastBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"getTokenStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"getTokenTimeCursor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getTokensDistributedInWeek","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getTotalSupplyAtTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getUserBalanceAtTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserLastEpochCheckpointed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserTimeCursor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"getUserTokenTimeCursor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVotingEscrow","outputs":[{"internalType":"contract IVotingEscrow","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"onlyVeHolderClaimingEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"withdrawToken","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60c06040523480156200001157600080fd5b5060405162002fc638038062002fc68339810160408190526200003491620001cb565b600062000040620001a6565b600080546001600160a01b0319166001600160a01b0383169081178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350600180556001600160601b0319606083901b16608052620000ab81620001aa565b90506000620000ba42620001aa565b905080821015620000e85760405162461bcd60e51b8152600401620000df90620002ca565b60405180910390fd5b60405163d1febfb960e01b81526000906001600160a01b0385169063d1febfb9906200011990849060040162000275565b60806040518083038186803b1580156200013257600080fd5b505afa15801562000147573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200016d919062000205565b905080604001518311620001955760405162461bcd60e51b8152600401620000df906200027e565b505060a081905260025550620002ff565b3390565b62093a80808204025b919050565b8051600f81900b8114620001b357600080fd5b60008060408385031215620001de578182fd5b82516001600160a01b0381168114620001f5578283fd5b6020939093015192949293505050565b60006080828403121562000217578081fd5b604051608081016001600160401b03811182821017156200023457fe5b6040526200024283620001b8565b81526200025260208401620001b8565b602082015260408301516040820152606083015160608201528091505092915050565b90815260200190565b6020808252602c908201527f43616e6e6f74207374617274206265666f726520566f74696e67457363726f7760408201526b040ccd2e4e6e840cae0dec6d60a31b606082015260800190565b6020808252818101527f43616e6e6f74207374617274206265666f72652063757272656e74207765656b604082015260600190565b60805160601c60a051612c646200036260003980610ce452806112085280611355528061139752806116df525080610405528061112252806112c5528061149d5280611c345280611ccf528061200b528061221052806122f55250612c646000f3fe608060405234801561001057600080fd5b50600436106101da5760003560e01c806389d11a5911610104578063c8a0f2d5116100a2578063de2283ab11610071578063de2283ab146103b7578063de681faf146103ca578063f14045ac146103dd578063f2fde38b146103f0576101da565b8063c8a0f2d514610232578063ca31879d1461037e578063cc08736414610391578063d3dc4ca1146103a4576101da565b8063a3208f82116100de578063a3208f821461033b578063acbc14281461035b578063c2c4c5c11461036e578063c828371e14610376576101da565b806389d11a591461030d5780638da5cb5b14610320578063905d10ac14610328576101da565b80633ccdbb281161017c5780638050a7ee1161014b5780638050a7ee146102bf57806382aa5ad4146102d2578063876e69a1146102da57806388720467146102ed576101da565b80633ccdbb281461027e5780634f3c509014610291578063715018a6146102a45780637b8d6221146102ac576101da565b80632308805b116101b85780632308805b14610232578063286d5e7f14610245578063338b5dea146102585780633902b9bc1461026b576101da565b806308b0308a146101df5780630c59ef34146101fd57806314866e081461021d575b600080fd5b6101e7610403565b6040516101f49190612932565b60405180910390f35b61021061020b36600461268d565b610427565b6040516101f49190612b9a565b61023061022b36600461268d565b610450565b005b61021061024036600461268d565b6104bd565b61021061025336600461268d565b6104fe565b610230610266366004612734565b61053f565b61023061027936600461268d565b6105da565b61023061028c366004612856565b610654565b61021061029f366004612902565b61071c565b61023061072e565b6102306102ba36600461279f565b610804565b6102106102cd3660046126fc565b610972565b610210610987565b6102106102e836600461268d565b61098d565b6103006102fb3660046126a9565b6109be565b6040516101f491906129a3565b61021061031b36600461268d565b610b5b565b6101e7610b80565b61023061033636600461275f565b610b8f565b61034e61034936600461268d565b610c28565b6040516101f491906129db565b61021061036936600461268d565b610c46565b610230610c77565b610210610ce2565b61021061038c3660046126fc565b610d06565b61023061039f366004612808565b610ded565b6102106103b2366004612734565b610e61565b61034e6103c536600461268d565b610e89565b6102106103d8366004612734565b610ea7565b6102306103eb366004612822565b610ecf565b6102306103fe36600461268d565b610fc3565b7f000000000000000000000000000000000000000000000000000000000000000090565b6001600160a01b03811660009081526007602052604090205467ffffffffffffffff165b919050565b600260015414156104a8576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026001556104b6816110ef565b5060018055565b6001600160a01b031660009081526004602052604090205470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1690565b6001600160a01b031660009081526007602052604090205470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1690565b60026001541415610597576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600155816105a68161166c565b6105b18360006116a4565b6105c66001600160a01b038416333085611a74565b6105d18360016116a4565b50506001805550565b60026001541415610632576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600155806106418161166c565b61064c8260016116a4565b505060018055565b61065c611b02565b6001600160a01b031661066d610b80565b6001600160a01b0316146106c8576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6106dc6001600160a01b0384168284611b06565b7fa0524ee0fd8662d6c046d199da2a6d3dc49445182cec055873a5bb9c2843c8e083838360405161070f939291906129e6565b60405180910390a1505050565b60009081526003602052604090205490565b610736611b02565b6001600160a01b0316610747610b80565b6001600160a01b0316146107a2576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b6002600154141561085c576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026001558281146108895760405162461bcd60e51b815260040161088090612b63565b60405180910390fd5b8260005b81811015610966576108be8686838181106108a457fe5b90506020020160208101906108b9919061268d565b61166c565b6108e98686838181106108cd57fe5b90506020020160208101906108e2919061268d565b60006116a4565b61093333308686858181106108fa57fe5b9050602002013589898681811061090d57fe5b9050602002016020810190610922919061268d565b6001600160a01b0316929190611a74565b61095e86868381811061094257fe5b9050602002016020810190610957919061268d565b60016116a4565b60010161088d565b50506001805550505050565b600061097e8383611b8b565b90505b92915050565b60025490565b6001600160a01b031660009081526007602052604090205468010000000000000000900467ffffffffffffffff1690565b606060026001541415610a18576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026001556001600160a01b0384166000908152600a6020526040902054849060ff1615610a6857336001600160a01b03821614610a685760405162461bcd60e51b815260040161088090612af5565b83838060005b81811015610a8d57610a858484838181106108a457fe5b600101610a6e565b50610a96611c09565b610a9f886110ef565b8560008167ffffffffffffffff81118015610ab957600080fd5b50604051908082528060200260200182016040528015610ae3578160200160208202803683370190505b50905060005b82811015610b4957610b008a8a838181106108cd57fe5b610b2a8b8b8b84818110610b1057fe5b9050602002016020810190610b25919061268d565b611dd8565b828281518110610b3657fe5b6020908102919091010152600101610ae9565b50600180559998505050505050505050565b6001600160a01b031660009081526004602052604090205467ffffffffffffffff1690565b6000546001600160a01b031690565b60026001541415610be7576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026001558060005b81811015610c1e57610c078484838181106108a457fe5b610c1684848381811061094257fe5b600101610bf0565b5050600180555050565b6001600160a01b03166000908152600a602052604090205460ff1690565b6001600160a01b031660009081526004602052604090205468010000000000000000900467ffffffffffffffff1690565b60026001541415610ccf576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600155610cdc611c09565b60018055565b7f000000000000000000000000000000000000000000000000000000000000000090565b600060026001541415610d60576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026001556001600160a01b0383166000908152600a6020526040902054839060ff1615610db057336001600160a01b03821614610db05760405162461bcd60e51b815260040161088090612af5565b82610dba8161166c565b610dc2611c09565b610dcb856110ef565b610dd68460006116a4565b610de08585611dd8565b6001805595945050505050565b336000818152600a60205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016841515179055517f5df4db5082ff794d02e809cecb2c56f2ac683e734a0b4d1e03fd82b3da056c7191610e56918490612946565b60405180910390a150565b6001600160a01b03919091166000908152600560209081526040808320938352929052205490565b6001600160a01b031660009081526006602052604090205460ff1690565b6001600160a01b03919091166000908152600860209081526040808320938352929052205490565b610ed7611b02565b6001600160a01b0316610ee8610b80565b6001600160a01b031614610f43576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b0382166000908152600660205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016831515179055517f359cee3647456cdac70428f1f028ea08d4bafdc5703fc14df9625b49f4398c9d90610fb79084908490612946565b60405180910390a15050565b610fcb611b02565b6001600160a01b0316610fdc610b80565b6001600160a01b031614611037576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b03811661107c5760405162461bcd60e51b8152600401808060200182810382526026815260200180612bb96026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6040517f010ae7570000000000000000000000000000000000000000000000000000000081526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063010ae75790611157908590600401612932565b60206040518083038186803b15801561116f57600080fd5b505afa158015611183573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111a7919061291a565b9050600081116111c95760405162461bcd60e51b815260040161088090612b2c565b6001600160a01b0382166000908152600760205260408120805490916801000000000000000090910467ffffffffffffffff1690816112365761122f857f0000000000000000000000000000000000000000000000000000000000000000600087611fb4565b9050611289565b4282106112465750505050611669565b50815470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16601481850311156112895761128685838387611fb4565b90505b80611292575060015b6040517f28d09d470000000000000000000000000000000000000000000000000000000081526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906328d09d47906112fc908990869060040161298a565b60806040518083038186803b15801561131457600080fd5b505afa158015611328573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061134c9190612897565b9050826113fd577f000000000000000000000000000000000000000000000000000000000000000042116113925760405162461bcd60e51b815260040161088090612a98565b6113c87f00000000000000000000000000000000000000000000000000000000000000006113c383604001516120c4565b6120d4565b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff821617855592505b6114056125f4565b60005b60328110156115d057826040015185101580156114255750868411155b1561152c576001840193508291508684111561146d5760405180608001604052806000600f0b81526020016000600f0b81526020016000815260200160008152509250611527565b6040517f28d09d470000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906328d09d47906114d4908b90889060040161298a565b60806040518083038186803b1580156114ec57600080fd5b505afa158015611500573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115249190612897565b92505b6115c8565b428510611538576115d0565b6000826040015186039050600081846020015102600f0b8460000151600f0b13611563576000611574565b81846020015102846000015103600f0b5b90508015801561158357508886115b1561159a57611591426120c4565b965050506115d0565b6001600160a01b038a1660009081526008602090815260408083208a84529091529020555062093a80909401935b600101611408565b505083546fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9290920167ffffffffffffffff90811670010000000000000000000000000000000002929092177fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff166801000000000000000093909216929092021790915550505b50565b6001600160a01b03811660009081526006602052604090205460ff166116695760405162461bcd60e51b815260040161088090612a2a565b6001600160a01b0382166000908152600460205260408120805490916801000000000000000090910467ffffffffffffffff169081611761577f0000000000000000000000000000000000000000000000000000000000000000421161171c5760405162461bcd60e51b815260040161088090612a98565b429150611728426120eb565b83547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff919091161783556117b3565b8142039050836117b3576000611776836120eb565b61177f426120eb565b14905060006201518042611792426120c4565b031090508180156117a1575080155b156117b0575050505050611a70565b50505b82547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff16680100000000000000004267ffffffffffffffff16021783556040517f70a082310000000000000000000000000000000000000000000000000000000081526000906001600160a01b038716906370a0823190611838903090600401612932565b60206040518083038186803b15801561185057600080fd5b505afa158015611864573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611888919061291a565b84549091506000906118c190839070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff166120f7565b9050806118d2575050505050611a70565b6fffffffffffffffffffffffffffffffff8211156119025760405162461bcd60e51b815260040161088090612a61565b84546fffffffffffffffffffffffffffffffff808416700100000000000000000000000000000000029116178555600061193b856120eb565b6001600160a01b038916600090815260056020526040812091925090815b6014811015611a2b578362093a80019250824210156119c9578615801561197f57508742145b1561199d5760008481526020839052604090208054860190556119c4565b868842038602816119aa57fe5b600086815260208590526040902080549290910490910190555b611a2b565b861580156119d657508783145b156119f4576000848152602083905260409020805486019055611a1b565b86888403860281611a0157fe5b600086815260208590526040902080549290910490910190555b9196508692508291600101611959565b507f9b7f1a85a4c9b4e59e1b6527d9969c50cdfb3a1a467d0c4a51fb0ed8bf07f1308a8589604051611a5f93929190612a09565b60405180910390a150505050505050505b5050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd00000000000000000000000000000000000000000000000000000000179052611afc908590612154565b50505050565b3390565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052611b86908490612154565b505050565b6001600160a01b0380831660009081526009602090815260408083209385168352929052908120548015611bc0579050610981565b6001600160a01b0380851660009081526007602090815260408083205493871683526004909152902054611c019167ffffffffffffffff90811691166120d4565b949350505050565b6002546000611c17426120eb565b905080821180611c2657504281145b15611c32575050611dd6565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c2c4c5c16040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611c8d57600080fd5b505af1158015611ca1573d6000803e3d6000fd5b5050505060005b6014811015611dd05781831115611cbe57611dd0565b6000611cc984612205565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d1febfb9836040518263ffffffff1660e01b8152600401611d199190612b9a565b60806040518083038186803b158015611d3157600080fd5b505afa158015611d45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d699190612897565b9050600081604001518611611d7f576000611d87565b816040015186035b60208301518351919250820290036000600f82900b13611da8576000611dad565b80600f0b5b600088815260036020526040902055505062093a80909401935050600101611ca8565b50506002555b565b6001600160a01b038116600090815260046020526040812081611dfb8585611b8b565b6002546001600160a01b03871660009081526007602052604081205492935091611e6e91611e4891611e43919068010000000000000000900467ffffffffffffffff166123a9565b6120c4565b8454611e699068010000000000000000900467ffffffffffffffff166120eb565b6123a9565b6001600160a01b038087166000908152600560209081526040808320938b16835260089091528120929350909190805b6014811015611ef257848610611eb357611ef2565b60008681526003602090815260408083205486835281842054928890529220540281611edb57fe5b62093a809790970196049190910190600101611e9e565b506001600160a01b03808a166000908152600960209081526040808320938c168352929052208590558015611fa85785546fffffffffffffffffffffffffffffffff70010000000000000000000000000000000080830482168490038216029116178655611f6a6001600160a01b0389168a83611b06565b7fff097c7d8b1957a4ff09ef1361b5fb54dcede3941ba836d0beb9d10bec725de689898388604051611f9f9493929190612961565b60405180910390a15b98975050505050505050565b60008282825b60808110156120b857818310611fcf576120b8565b6040517f28d09d470000000000000000000000000000000000000000000000000000000081526002838501810104906000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906328d09d4790612042908d90869060040161298a565b60806040518083038186803b15801561205a57600080fd5b505afa15801561206e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120929190612897565b9050888160400151116120a7578194506120ae565b6001820393505b5050600101611fba565b50909695505050505050565b600061098162093a7f83016120eb565b6000818310156120e4578161097e565b5090919050565b62093a80908190040290565b60008282111561214e576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60006121a9826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166123b89092919063ffffffff16565b805190915015611b86578080602001905160208110156121c857600080fd5b5051611b865760405162461bcd60e51b815260040180806020018281038252602a815260200180612c05602a913960400191505060405180910390fd5b6000806000905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663900cf0cf6040518163ffffffff1660e01b815260040160206040518083038186803b15801561226757600080fd5b505afa15801561227b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061229f919061291a565b905060005b60808110156123a0578183106122b9576123a0565b6040517fd1febfb90000000000000000000000000000000000000000000000000000000081526002838501810104906000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d1febfb99061232a908590600401612b9a565b60806040518083038186803b15801561234257600080fd5b505afa158015612356573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061237a9190612897565b90508681604001511161238f57819450612396565b6001820393505b50506001016122a4565b50909392505050565b60008183106120e4578161097e565b60606123c784846000856123d1565b90505b9392505050565b6060824710156124125760405162461bcd60e51b8152600401808060200182810382526026815260200180612bdf6026913960400191505060405180910390fd5b61241b8561254a565b61246c576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b602083106124c857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161248b565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d806000811461252a576040519150601f19603f3d011682016040523d82523d6000602084013e61252f565b606091505b509150915061253f828286612550565b979650505050505050565b3b151590565b6060831561255f5750816123ca565b82511561256f5782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156125b95781810151838201526020016125a1565b50505050905090810190601f1680156125e65780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b60405180608001604052806000600f0b81526020016000600f0b815260200160008152602001600081525090565b60008083601f840112612633578182fd5b50813567ffffffffffffffff81111561264a578182fd5b602083019150836020808302850101111561266457600080fd5b9250929050565b8035801515811461044b57600080fd5b8051600f81900b811461044b57600080fd5b60006020828403121561269e578081fd5b81356123ca81612ba3565b6000806000604084860312156126bd578182fd5b83356126c881612ba3565b9250602084013567ffffffffffffffff8111156126e3578283fd5b6126ef86828701612622565b9497909650939450505050565b6000806040838503121561270e578182fd5b823561271981612ba3565b9150602083013561272981612ba3565b809150509250929050565b60008060408385031215612746578182fd5b823561275181612ba3565b946020939093013593505050565b60008060208385031215612771578182fd5b823567ffffffffffffffff811115612787578283fd5b61279385828601612622565b90969095509350505050565b600080600080604085870312156127b4578081fd5b843567ffffffffffffffff808211156127cb578283fd5b6127d788838901612622565b909650945060208701359150808211156127ef578283fd5b506127fc87828801612622565b95989497509550505050565b600060208284031215612819578081fd5b61097e8261266b565b60008060408385031215612834578182fd5b823561283f81612ba3565b915061284d6020840161266b565b90509250929050565b60008060006060848603121561286a578283fd5b833561287581612ba3565b925060208401359150604084013561288c81612ba3565b809150509250925092565b6000608082840312156128a8578081fd5b6040516080810181811067ffffffffffffffff821117156128c557fe5b6040526128d18361267b565b81526128df6020840161267b565b602082015260408301516040820152606083015160608201528091505092915050565b600060208284031215612913578081fd5b5035919050565b60006020828403121561292b578081fd5b5051919050565b6001600160a01b0391909116815260200190565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6001600160a01b03929092168252602082015260400190565b6020808252825182820181905260009190848201906040850190845b818110156120b8578351835292840192918401916001016129bf565b901515815260200190565b6001600160a01b0393841681526020810192909252909116604082015260600190565b6001600160a01b039390931683526020830191909152604082015260600190565b60208082526014908201527f546f6b656e206973206e6f7420616c6c6f776564000000000000000000000000604082015260600190565b6020808252601e908201527f4d6178696d756d20746f6b656e2062616c616e63652065786365656465640000604082015260600190565b60208082526024908201527f46656520646973747269627574696f6e20686173206e6f74207374617274656460408201527f2079657400000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526017908201527f436c61696d696e67206973206e6f7420616c6c6f776564000000000000000000604082015260600190565b60208082526015908201527f76655354472062616c616e6365206973207a65726f0000000000000000000000604082015260600190565b60208082526015908201527f496e707574206c656e677468206d69736d617463680000000000000000000000604082015260600190565b90815260200190565b6001600160a01b038116811461166957600080fdfe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c5361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a2646970667358221220ed016ceb092c9550649e4c44527b0d19d8e48c83a3744d0b7901b08f3419abb364736f6c634300070600330000000000000000000000000e42acbd23faee03249daff896b78d7e79fbd58e0000000000000000000000000000000000000000000000000000000064f91280
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101da5760003560e01c806389d11a5911610104578063c8a0f2d5116100a2578063de2283ab11610071578063de2283ab146103b7578063de681faf146103ca578063f14045ac146103dd578063f2fde38b146103f0576101da565b8063c8a0f2d514610232578063ca31879d1461037e578063cc08736414610391578063d3dc4ca1146103a4576101da565b8063a3208f82116100de578063a3208f821461033b578063acbc14281461035b578063c2c4c5c11461036e578063c828371e14610376576101da565b806389d11a591461030d5780638da5cb5b14610320578063905d10ac14610328576101da565b80633ccdbb281161017c5780638050a7ee1161014b5780638050a7ee146102bf57806382aa5ad4146102d2578063876e69a1146102da57806388720467146102ed576101da565b80633ccdbb281461027e5780634f3c509014610291578063715018a6146102a45780637b8d6221146102ac576101da565b80632308805b116101b85780632308805b14610232578063286d5e7f14610245578063338b5dea146102585780633902b9bc1461026b576101da565b806308b0308a146101df5780630c59ef34146101fd57806314866e081461021d575b600080fd5b6101e7610403565b6040516101f49190612932565b60405180910390f35b61021061020b36600461268d565b610427565b6040516101f49190612b9a565b61023061022b36600461268d565b610450565b005b61021061024036600461268d565b6104bd565b61021061025336600461268d565b6104fe565b610230610266366004612734565b61053f565b61023061027936600461268d565b6105da565b61023061028c366004612856565b610654565b61021061029f366004612902565b61071c565b61023061072e565b6102306102ba36600461279f565b610804565b6102106102cd3660046126fc565b610972565b610210610987565b6102106102e836600461268d565b61098d565b6103006102fb3660046126a9565b6109be565b6040516101f491906129a3565b61021061031b36600461268d565b610b5b565b6101e7610b80565b61023061033636600461275f565b610b8f565b61034e61034936600461268d565b610c28565b6040516101f491906129db565b61021061036936600461268d565b610c46565b610230610c77565b610210610ce2565b61021061038c3660046126fc565b610d06565b61023061039f366004612808565b610ded565b6102106103b2366004612734565b610e61565b61034e6103c536600461268d565b610e89565b6102106103d8366004612734565b610ea7565b6102306103eb366004612822565b610ecf565b6102306103fe36600461268d565b610fc3565b7f0000000000000000000000000e42acbd23faee03249daff896b78d7e79fbd58e90565b6001600160a01b03811660009081526007602052604090205467ffffffffffffffff165b919050565b600260015414156104a8576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026001556104b6816110ef565b5060018055565b6001600160a01b031660009081526004602052604090205470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1690565b6001600160a01b031660009081526007602052604090205470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1690565b60026001541415610597576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600155816105a68161166c565b6105b18360006116a4565b6105c66001600160a01b038416333085611a74565b6105d18360016116a4565b50506001805550565b60026001541415610632576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600155806106418161166c565b61064c8260016116a4565b505060018055565b61065c611b02565b6001600160a01b031661066d610b80565b6001600160a01b0316146106c8576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6106dc6001600160a01b0384168284611b06565b7fa0524ee0fd8662d6c046d199da2a6d3dc49445182cec055873a5bb9c2843c8e083838360405161070f939291906129e6565b60405180910390a1505050565b60009081526003602052604090205490565b610736611b02565b6001600160a01b0316610747610b80565b6001600160a01b0316146107a2576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b6002600154141561085c576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026001558281146108895760405162461bcd60e51b815260040161088090612b63565b60405180910390fd5b8260005b81811015610966576108be8686838181106108a457fe5b90506020020160208101906108b9919061268d565b61166c565b6108e98686838181106108cd57fe5b90506020020160208101906108e2919061268d565b60006116a4565b61093333308686858181106108fa57fe5b9050602002013589898681811061090d57fe5b9050602002016020810190610922919061268d565b6001600160a01b0316929190611a74565b61095e86868381811061094257fe5b9050602002016020810190610957919061268d565b60016116a4565b60010161088d565b50506001805550505050565b600061097e8383611b8b565b90505b92915050565b60025490565b6001600160a01b031660009081526007602052604090205468010000000000000000900467ffffffffffffffff1690565b606060026001541415610a18576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026001556001600160a01b0384166000908152600a6020526040902054849060ff1615610a6857336001600160a01b03821614610a685760405162461bcd60e51b815260040161088090612af5565b83838060005b81811015610a8d57610a858484838181106108a457fe5b600101610a6e565b50610a96611c09565b610a9f886110ef565b8560008167ffffffffffffffff81118015610ab957600080fd5b50604051908082528060200260200182016040528015610ae3578160200160208202803683370190505b50905060005b82811015610b4957610b008a8a838181106108cd57fe5b610b2a8b8b8b84818110610b1057fe5b9050602002016020810190610b25919061268d565b611dd8565b828281518110610b3657fe5b6020908102919091010152600101610ae9565b50600180559998505050505050505050565b6001600160a01b031660009081526004602052604090205467ffffffffffffffff1690565b6000546001600160a01b031690565b60026001541415610be7576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026001558060005b81811015610c1e57610c078484838181106108a457fe5b610c1684848381811061094257fe5b600101610bf0565b5050600180555050565b6001600160a01b03166000908152600a602052604090205460ff1690565b6001600160a01b031660009081526004602052604090205468010000000000000000900467ffffffffffffffff1690565b60026001541415610ccf576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600155610cdc611c09565b60018055565b7f0000000000000000000000000000000000000000000000000000000064f9128090565b600060026001541415610d60576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026001556001600160a01b0383166000908152600a6020526040902054839060ff1615610db057336001600160a01b03821614610db05760405162461bcd60e51b815260040161088090612af5565b82610dba8161166c565b610dc2611c09565b610dcb856110ef565b610dd68460006116a4565b610de08585611dd8565b6001805595945050505050565b336000818152600a60205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016841515179055517f5df4db5082ff794d02e809cecb2c56f2ac683e734a0b4d1e03fd82b3da056c7191610e56918490612946565b60405180910390a150565b6001600160a01b03919091166000908152600560209081526040808320938352929052205490565b6001600160a01b031660009081526006602052604090205460ff1690565b6001600160a01b03919091166000908152600860209081526040808320938352929052205490565b610ed7611b02565b6001600160a01b0316610ee8610b80565b6001600160a01b031614610f43576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b0382166000908152600660205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016831515179055517f359cee3647456cdac70428f1f028ea08d4bafdc5703fc14df9625b49f4398c9d90610fb79084908490612946565b60405180910390a15050565b610fcb611b02565b6001600160a01b0316610fdc610b80565b6001600160a01b031614611037576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b03811661107c5760405162461bcd60e51b8152600401808060200182810382526026815260200180612bb96026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6040517f010ae7570000000000000000000000000000000000000000000000000000000081526000906001600160a01b037f0000000000000000000000000e42acbd23faee03249daff896b78d7e79fbd58e169063010ae75790611157908590600401612932565b60206040518083038186803b15801561116f57600080fd5b505afa158015611183573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111a7919061291a565b9050600081116111c95760405162461bcd60e51b815260040161088090612b2c565b6001600160a01b0382166000908152600760205260408120805490916801000000000000000090910467ffffffffffffffff1690816112365761122f857f0000000000000000000000000000000000000000000000000000000064f91280600087611fb4565b9050611289565b4282106112465750505050611669565b50815470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16601481850311156112895761128685838387611fb4565b90505b80611292575060015b6040517f28d09d470000000000000000000000000000000000000000000000000000000081526000906001600160a01b037f0000000000000000000000000e42acbd23faee03249daff896b78d7e79fbd58e16906328d09d47906112fc908990869060040161298a565b60806040518083038186803b15801561131457600080fd5b505afa158015611328573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061134c9190612897565b9050826113fd577f0000000000000000000000000000000000000000000000000000000064f9128042116113925760405162461bcd60e51b815260040161088090612a98565b6113c87f0000000000000000000000000000000000000000000000000000000064f912806113c383604001516120c4565b6120d4565b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff821617855592505b6114056125f4565b60005b60328110156115d057826040015185101580156114255750868411155b1561152c576001840193508291508684111561146d5760405180608001604052806000600f0b81526020016000600f0b81526020016000815260200160008152509250611527565b6040517f28d09d470000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000e42acbd23faee03249daff896b78d7e79fbd58e16906328d09d47906114d4908b90889060040161298a565b60806040518083038186803b1580156114ec57600080fd5b505afa158015611500573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115249190612897565b92505b6115c8565b428510611538576115d0565b6000826040015186039050600081846020015102600f0b8460000151600f0b13611563576000611574565b81846020015102846000015103600f0b5b90508015801561158357508886115b1561159a57611591426120c4565b965050506115d0565b6001600160a01b038a1660009081526008602090815260408083208a84529091529020555062093a80909401935b600101611408565b505083546fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9290920167ffffffffffffffff90811670010000000000000000000000000000000002929092177fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff166801000000000000000093909216929092021790915550505b50565b6001600160a01b03811660009081526006602052604090205460ff166116695760405162461bcd60e51b815260040161088090612a2a565b6001600160a01b0382166000908152600460205260408120805490916801000000000000000090910467ffffffffffffffff169081611761577f0000000000000000000000000000000000000000000000000000000064f91280421161171c5760405162461bcd60e51b815260040161088090612a98565b429150611728426120eb565b83547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff919091161783556117b3565b8142039050836117b3576000611776836120eb565b61177f426120eb565b14905060006201518042611792426120c4565b031090508180156117a1575080155b156117b0575050505050611a70565b50505b82547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff16680100000000000000004267ffffffffffffffff16021783556040517f70a082310000000000000000000000000000000000000000000000000000000081526000906001600160a01b038716906370a0823190611838903090600401612932565b60206040518083038186803b15801561185057600080fd5b505afa158015611864573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611888919061291a565b84549091506000906118c190839070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff166120f7565b9050806118d2575050505050611a70565b6fffffffffffffffffffffffffffffffff8211156119025760405162461bcd60e51b815260040161088090612a61565b84546fffffffffffffffffffffffffffffffff808416700100000000000000000000000000000000029116178555600061193b856120eb565b6001600160a01b038916600090815260056020526040812091925090815b6014811015611a2b578362093a80019250824210156119c9578615801561197f57508742145b1561199d5760008481526020839052604090208054860190556119c4565b868842038602816119aa57fe5b600086815260208590526040902080549290910490910190555b611a2b565b861580156119d657508783145b156119f4576000848152602083905260409020805486019055611a1b565b86888403860281611a0157fe5b600086815260208590526040902080549290910490910190555b9196508692508291600101611959565b507f9b7f1a85a4c9b4e59e1b6527d9969c50cdfb3a1a467d0c4a51fb0ed8bf07f1308a8589604051611a5f93929190612a09565b60405180910390a150505050505050505b5050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd00000000000000000000000000000000000000000000000000000000179052611afc908590612154565b50505050565b3390565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052611b86908490612154565b505050565b6001600160a01b0380831660009081526009602090815260408083209385168352929052908120548015611bc0579050610981565b6001600160a01b0380851660009081526007602090815260408083205493871683526004909152902054611c019167ffffffffffffffff90811691166120d4565b949350505050565b6002546000611c17426120eb565b905080821180611c2657504281145b15611c32575050611dd6565b7f0000000000000000000000000e42acbd23faee03249daff896b78d7e79fbd58e6001600160a01b031663c2c4c5c16040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611c8d57600080fd5b505af1158015611ca1573d6000803e3d6000fd5b5050505060005b6014811015611dd05781831115611cbe57611dd0565b6000611cc984612205565b905060007f0000000000000000000000000e42acbd23faee03249daff896b78d7e79fbd58e6001600160a01b031663d1febfb9836040518263ffffffff1660e01b8152600401611d199190612b9a565b60806040518083038186803b158015611d3157600080fd5b505afa158015611d45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d699190612897565b9050600081604001518611611d7f576000611d87565b816040015186035b60208301518351919250820290036000600f82900b13611da8576000611dad565b80600f0b5b600088815260036020526040902055505062093a80909401935050600101611ca8565b50506002555b565b6001600160a01b038116600090815260046020526040812081611dfb8585611b8b565b6002546001600160a01b03871660009081526007602052604081205492935091611e6e91611e4891611e43919068010000000000000000900467ffffffffffffffff166123a9565b6120c4565b8454611e699068010000000000000000900467ffffffffffffffff166120eb565b6123a9565b6001600160a01b038087166000908152600560209081526040808320938b16835260089091528120929350909190805b6014811015611ef257848610611eb357611ef2565b60008681526003602090815260408083205486835281842054928890529220540281611edb57fe5b62093a809790970196049190910190600101611e9e565b506001600160a01b03808a166000908152600960209081526040808320938c168352929052208590558015611fa85785546fffffffffffffffffffffffffffffffff70010000000000000000000000000000000080830482168490038216029116178655611f6a6001600160a01b0389168a83611b06565b7fff097c7d8b1957a4ff09ef1361b5fb54dcede3941ba836d0beb9d10bec725de689898388604051611f9f9493929190612961565b60405180910390a15b98975050505050505050565b60008282825b60808110156120b857818310611fcf576120b8565b6040517f28d09d470000000000000000000000000000000000000000000000000000000081526002838501810104906000906001600160a01b037f0000000000000000000000000e42acbd23faee03249daff896b78d7e79fbd58e16906328d09d4790612042908d90869060040161298a565b60806040518083038186803b15801561205a57600080fd5b505afa15801561206e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120929190612897565b9050888160400151116120a7578194506120ae565b6001820393505b5050600101611fba565b50909695505050505050565b600061098162093a7f83016120eb565b6000818310156120e4578161097e565b5090919050565b62093a80908190040290565b60008282111561214e576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60006121a9826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166123b89092919063ffffffff16565b805190915015611b86578080602001905160208110156121c857600080fd5b5051611b865760405162461bcd60e51b815260040180806020018281038252602a815260200180612c05602a913960400191505060405180910390fd5b6000806000905060007f0000000000000000000000000e42acbd23faee03249daff896b78d7e79fbd58e6001600160a01b031663900cf0cf6040518163ffffffff1660e01b815260040160206040518083038186803b15801561226757600080fd5b505afa15801561227b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061229f919061291a565b905060005b60808110156123a0578183106122b9576123a0565b6040517fd1febfb90000000000000000000000000000000000000000000000000000000081526002838501810104906000906001600160a01b037f0000000000000000000000000e42acbd23faee03249daff896b78d7e79fbd58e169063d1febfb99061232a908590600401612b9a565b60806040518083038186803b15801561234257600080fd5b505afa158015612356573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061237a9190612897565b90508681604001511161238f57819450612396565b6001820393505b50506001016122a4565b50909392505050565b60008183106120e4578161097e565b60606123c784846000856123d1565b90505b9392505050565b6060824710156124125760405162461bcd60e51b8152600401808060200182810382526026815260200180612bdf6026913960400191505060405180910390fd5b61241b8561254a565b61246c576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b602083106124c857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161248b565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d806000811461252a576040519150601f19603f3d011682016040523d82523d6000602084013e61252f565b606091505b509150915061253f828286612550565b979650505050505050565b3b151590565b6060831561255f5750816123ca565b82511561256f5782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156125b95781810151838201526020016125a1565b50505050905090810190601f1680156125e65780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b60405180608001604052806000600f0b81526020016000600f0b815260200160008152602001600081525090565b60008083601f840112612633578182fd5b50813567ffffffffffffffff81111561264a578182fd5b602083019150836020808302850101111561266457600080fd5b9250929050565b8035801515811461044b57600080fd5b8051600f81900b811461044b57600080fd5b60006020828403121561269e578081fd5b81356123ca81612ba3565b6000806000604084860312156126bd578182fd5b83356126c881612ba3565b9250602084013567ffffffffffffffff8111156126e3578283fd5b6126ef86828701612622565b9497909650939450505050565b6000806040838503121561270e578182fd5b823561271981612ba3565b9150602083013561272981612ba3565b809150509250929050565b60008060408385031215612746578182fd5b823561275181612ba3565b946020939093013593505050565b60008060208385031215612771578182fd5b823567ffffffffffffffff811115612787578283fd5b61279385828601612622565b90969095509350505050565b600080600080604085870312156127b4578081fd5b843567ffffffffffffffff808211156127cb578283fd5b6127d788838901612622565b909650945060208701359150808211156127ef578283fd5b506127fc87828801612622565b95989497509550505050565b600060208284031215612819578081fd5b61097e8261266b565b60008060408385031215612834578182fd5b823561283f81612ba3565b915061284d6020840161266b565b90509250929050565b60008060006060848603121561286a578283fd5b833561287581612ba3565b925060208401359150604084013561288c81612ba3565b809150509250925092565b6000608082840312156128a8578081fd5b6040516080810181811067ffffffffffffffff821117156128c557fe5b6040526128d18361267b565b81526128df6020840161267b565b602082015260408301516040820152606083015160608201528091505092915050565b600060208284031215612913578081fd5b5035919050565b60006020828403121561292b578081fd5b5051919050565b6001600160a01b0391909116815260200190565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6001600160a01b03929092168252602082015260400190565b6020808252825182820181905260009190848201906040850190845b818110156120b8578351835292840192918401916001016129bf565b901515815260200190565b6001600160a01b0393841681526020810192909252909116604082015260600190565b6001600160a01b039390931683526020830191909152604082015260600190565b60208082526014908201527f546f6b656e206973206e6f7420616c6c6f776564000000000000000000000000604082015260600190565b6020808252601e908201527f4d6178696d756d20746f6b656e2062616c616e63652065786365656465640000604082015260600190565b60208082526024908201527f46656520646973747269627574696f6e20686173206e6f74207374617274656460408201527f2079657400000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526017908201527f436c61696d696e67206973206e6f7420616c6c6f776564000000000000000000604082015260600190565b60208082526015908201527f76655354472062616c616e6365206973207a65726f0000000000000000000000604082015260600190565b60208082526015908201527f496e707574206c656e677468206d69736d617463680000000000000000000000604082015260600190565b90815260200190565b6001600160a01b038116811461166957600080fdfe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c5361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a2646970667358221220ed016ceb092c9550649e4c44527b0d19d8e48c83a3744d0b7901b08f3419abb364736f6c63430007060033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000e42acbd23faee03249daff896b78d7e79fbd58e0000000000000000000000000000000000000000000000000000000064f91280
-----Decoded View---------------
Arg [0] : votingEscrow (address): 0x0e42acBD23FAee03249DAFF896b78d7e79fBD58E
Arg [1] : startTime (uint256): 1694044800
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000000e42acbd23faee03249daff896b78d7e79fbd58e
Arg [1] : 0000000000000000000000000000000000000000000000000000000064f91280
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
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.