Feature Tip: Add private address tag to any address under My Name Tag !
Source Code
Latest 25 from a total of 954 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Claim Tokens | 23998002 | 4 days ago | IN | 0 ETH | 0.00173493 | ||||
| Claim Tokens | 23950020 | 11 days ago | IN | 0 ETH | 0.00000512 | ||||
| Claim Tokens | 23950013 | 11 days ago | IN | 0 ETH | 0.00002884 | ||||
| Claim Tokens | 23946816 | 11 days ago | IN | 0 ETH | 0.00001286 | ||||
| Claim Token | 23940647 | 12 days ago | IN | 0 ETH | 0.0000125 | ||||
| Claim Token | 23940640 | 12 days ago | IN | 0 ETH | 0.00001544 | ||||
| Claim Tokens | 23940589 | 12 days ago | IN | 0 ETH | 0.00002391 | ||||
| Claim Tokens | 23938744 | 12 days ago | IN | 0 ETH | 0.00003673 | ||||
| Claim Tokens | 23917366 | 15 days ago | IN | 0 ETH | 0.00052027 | ||||
| Claim Tokens | 23859424 | 24 days ago | IN | 0 ETH | 0.00165377 | ||||
| Claim Token | 23821869 | 29 days ago | IN | 0 ETH | 0.00047684 | ||||
| Claim Token | 23801546 | 32 days ago | IN | 0 ETH | 0.00011424 | ||||
| Claim Token | 23666389 | 51 days ago | IN | 0 ETH | 0.0001511 | ||||
| Claim Tokens | 23657620 | 52 days ago | IN | 0 ETH | 0.00012018 | ||||
| Claim Tokens | 23644884 | 54 days ago | IN | 0 ETH | 0.00013356 | ||||
| Claim Token | 23637137 | 55 days ago | IN | 0 ETH | 0.00015922 | ||||
| Claim Tokens | 23452634 | 81 days ago | IN | 0 ETH | 0.00193452 | ||||
| Claim Token | 23332873 | 97 days ago | IN | 0 ETH | 0.00234097 | ||||
| Claim Token | 23319296 | 99 days ago | IN | 0 ETH | 0.00109935 | ||||
| Claim Tokens | 23312305 | 100 days ago | IN | 0 ETH | 0.00005826 | ||||
| Claim Tokens | 23312301 | 100 days ago | IN | 0 ETH | 0.00007514 | ||||
| Claim Tokens | 23307484 | 101 days ago | IN | 0 ETH | 0.0003403 | ||||
| Claim Token | 23274022 | 106 days ago | IN | 0 ETH | 0.00037109 | ||||
| Claim Tokens | 23230431 | 112 days ago | IN | 0 ETH | 0.00019825 | ||||
| Claim Token | 23226886 | 112 days ago | IN | 0 ETH | 0.00116897 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x61018060 | 16567953 | 1045 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
FeeDistributor
Compiler Version
v0.8.16+commit.07a7930e
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.8.0;
import {Math} from "openzeppelin-contracts/utils/math/Math.sol";
import {SafeCast} from "openzeppelin-contracts/utils/math/SafeCast.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {ReentrancyGuard} from "solmate/utils/ReentrancyGuard.sol";
import "./lib/OptionalOnlyCaller.sol";
import "./interfaces/IVotingEscrow.sol";
import "./interfaces/IFeeDistributor.sol";
// solhint-disable not-rely-on-time
/**
* @title Fee Distributor
* @notice Distributes any tokens transferred to the contract (e.g. Protocol fees and any token emissions) among vetoken
* 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 vetoken
* holders simply transfer the tokens to the `FeeDistributor` contract and then call `checkpointToken`.
*/
contract FeeDistributor is IFeeDistributor, OptionalOnlyCaller, ReentrancyGuard {
/// -----------------------------------------------------------------------
/// Library usage
/// -----------------------------------------------------------------------
using SafeTransferLib for ERC20;
/// -----------------------------------------------------------------------
/// Errors
/// -----------------------------------------------------------------------
error FeeDistributor__InputLengthMismatch();
error FeeDistributor__VotingEscrowZeroTotalSupply();
error FeeDistributor__CannotStartBeforeCurrentWeek();
/// -----------------------------------------------------------------------
/// Immutable params
/// -----------------------------------------------------------------------
uint256 private immutable _startTime;
IVotingEscrow private immutable _votingEscrow;
/// -----------------------------------------------------------------------
/// Storage variables
/// -----------------------------------------------------------------------
// 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(ERC20 => TokenState) private _tokenState;
mapping(ERC20 => mapping(uint256 => uint256)) private _tokensPerWeek;
// 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(ERC20 => uint256)) private _userTokenTimeCursor;
/// -----------------------------------------------------------------------
/// Constructor
/// -----------------------------------------------------------------------
constructor(IVotingEscrow votingEscrow, uint256 startTime) EIP712("FeeDistributor", "1") {
_votingEscrow = votingEscrow;
startTime = _roundDownTimestamp(startTime);
uint256 currentWeek = _roundDownTimestamp(block.timestamp);
if (startTime < currentWeek) {
revert FeeDistributor__CannotStartBeforeCurrentWeek();
}
if (startTime == currentWeek) {
// We assume that `votingEscrow` has been deployed in a week previous to this one.
// If `votingEscrow` did not have a non-zero supply at the beginning of the current week
// then any tokens which are distributed this week will be lost permanently.
if (votingEscrow.totalSupply(currentWeek) == 0) {
revert FeeDistributor__VotingEscrowZeroTotalSupply();
}
}
_startTime = startTime;
_timeCursor = startTime;
}
/// -----------------------------------------------------------------------
/// View functions
/// -----------------------------------------------------------------------
/**
* @notice Returns the VotingEscrow (vetoken) token contract
*/
function getVotingEscrow() external view override returns (IVotingEscrow) {
return _votingEscrow;
}
/**
* @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 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 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(ERC20 token) external view override returns (uint256) {
return _tokenState[token].timeCursor;
}
/**
* @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, ERC20 token) external view override returns (uint256) {
return _getUserTokenTimeCursor(user, token);
}
/**
* @notice Returns the user's cached balance of vetoken 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 vetoken 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(ERC20 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(ERC20 token, uint256 timestamp) external view override returns (uint256) {
return _tokensPerWeek[token][timestamp];
}
/// -----------------------------------------------------------------------
/// User actions
/// -----------------------------------------------------------------------
// 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(ERC20 token, uint256 amount) external override nonReentrant {
_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(ERC20[] calldata tokens, uint256[] calldata amounts) external override nonReentrant {
if (tokens.length != amounts.length) {
revert FeeDistributor__InputLengthMismatch();
}
uint256 length = tokens.length;
for (uint256 i = 0; i < length;) {
_checkpointToken(tokens[i], false);
tokens[i].safeTransferFrom(msg.sender, address(this), amounts[i]);
_checkpointToken(tokens[i], true);
unchecked {
++i;
}
}
}
// Checkpointing
/**
* @notice Caches the total supply of vetoken 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 vetoken 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(ERC20 token) external override nonReentrant {
_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(ERC20[] calldata tokens) external override nonReentrant {
uint256 tokensLength = tokens.length;
for (uint256 i = 0; i < tokensLength;) {
_checkpointToken(tokens[i], true);
unchecked {
++i;
}
}
}
// 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, ERC20 token)
external
override
nonReentrant
optionalOnlyCaller(user)
returns (uint256)
{
_checkpointTotalSupply();
_checkpointUserBalance(user);
_checkpointToken(token, false);
uint256 amount = _claimToken(user, token);
return amount;
}
/**
* @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, ERC20[] calldata tokens)
external
override
nonReentrant
optionalOnlyCaller(user)
returns (uint256[] memory)
{
_checkpointTotalSupply();
_checkpointUserBalance(user);
uint256 tokensLength = tokens.length;
uint256[] memory amounts = new uint256[](tokensLength);
for (uint256 i = 0; i < tokensLength;) {
_checkpointToken(tokens[i], false);
amounts[i] = _claimToken(user, tokens[i]);
unchecked {
++i;
}
}
return amounts;
}
/// -----------------------------------------------------------------------
/// 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, ERC20 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;) {
// We clearly cannot claim for `firstUnclaimableWeek` and so we break here.
if (nextUserTokenWeekToClaim >= firstUnclaimableWeek) break;
unchecked {
amount += (tokensPerWeek[nextUserTokenWeekToClaim] * userBalanceAtTimestamp[nextUserTokenWeekToClaim])
/ _veSupplyCache[nextUserTokenWeekToClaim];
nextUserTokenWeekToClaim += 1 weeks;
++i;
}
}
// Update the stored user-token time cursor to prevent this user claiming this week again.
_userTokenTimeCursor[user][token] = nextUserTokenWeekToClaim;
if (amount > 0) {
unchecked {
// 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(ERC20 token, bool force) internal {
TokenState storage tokenState = _tokenState[token];
uint256 lastTokenTime = tokenState.timeCursor;
uint256 timeSinceLastCheckpoint;
if (lastTokenTime == 0) {
// 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));
// Prevent someone from assigning tokens to an inaccessible week.
require(block.timestamp > _startTime, "Fee distribution has not started yet");
} else {
unchecked {
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 - 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;) {
unchecked {
// 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;
++i;
}
}
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 vetoken.
// They clearly will not then receive fees.
if (maxUserEpoch == 0) return;
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;
unchecked {
// 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;) {
unchecked {
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 = SafeCast.toInt128(SafeCast.toInt256(nextWeekToCheckpoint - currentUserPoint.ts));
uint256 userBalance = currentUserPoint.bias > currentUserPoint.slope * dt
? uint256(SafeCast.toUint256(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;
}
++i;
}
}
// 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`.
unchecked {
// 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;) {
unchecked {
if (nextWeekToCheckpoint > weekStart) break;
_veSupplyCache[nextWeekToCheckpoint] = _votingEscrow.totalSupply(nextWeekToCheckpoint);
// This is safe as we're incrementing a timestamp
nextWeekToCheckpoint += 1 weeks;
++i;
}
}
// 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, ERC20 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 vetoken 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;) {
unchecked {
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 Rounds the provided timestamp down to the beginning of the previous week (Thurs 00:00 UTC)
*/
function _roundDownTimestamp(uint256 timestamp) private pure returns (uint256) {
unchecked {
// 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) {
unchecked {
// Overflows are impossible here for all realistic inputs.
return _roundDownTimestamp(timestamp + 1 weeks - 1);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./ECDSA.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* _Available since v3.4._
*/
abstract contract EIP712 {
/* solhint-disable var-name-mixedcase */
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
uint256 private immutable _CACHED_CHAIN_ID;
address private immutable _CACHED_THIS;
bytes32 private immutable _HASHED_NAME;
bytes32 private immutable _HASHED_VERSION;
bytes32 private immutable _TYPE_HASH;
/* solhint-enable var-name-mixedcase */
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
bytes32 hashedName = keccak256(bytes(name));
bytes32 hashedVersion = keccak256(bytes(version));
bytes32 typeHash = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
_HASHED_NAME = hashedName;
_HASHED_VERSION = hashedVersion;
_CACHED_CHAIN_ID = block.chainid;
_CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
_CACHED_THIS = address(this);
_TYPE_HASH = typeHash;
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
return _CACHED_DOMAIN_SEPARATOR;
} else {
return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
}
}
function _buildDomainSeparator(
bytes32 typeHash,
bytes32 nameHash,
bytes32 versionHash
) private view returns (bytes32) {
return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10**64) {
value /= 10**64;
result += 64;
}
if (value >= 10**32) {
value /= 10**32;
result += 32;
}
if (value >= 10**16) {
value /= 10**16;
result += 16;
}
if (value >= 10**8) {
value /= 10**8;
result += 8;
}
if (value >= 10**4) {
value /= 10**4;
result += 4;
}
if (value >= 10**2) {
value /= 10**2;
result += 2;
}
if (value >= 10**1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toUint248(uint256 value) internal pure returns (uint248) {
require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toUint240(uint256 value) internal pure returns (uint240) {
require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toUint232(uint256 value) internal pure returns (uint232) {
require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.2._
*/
function toUint224(uint256 value) internal pure returns (uint224) {
require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toUint216(uint256 value) internal pure returns (uint216) {
require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toUint208(uint256 value) internal pure returns (uint208) {
require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toUint200(uint256 value) internal pure returns (uint200) {
require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toUint192(uint256 value) internal pure returns (uint192) {
require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toUint184(uint256 value) internal pure returns (uint184) {
require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toUint176(uint256 value) internal pure returns (uint176) {
require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toUint168(uint256 value) internal pure returns (uint168) {
require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toUint160(uint256 value) internal pure returns (uint160) {
require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toUint152(uint256 value) internal pure returns (uint152) {
require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toUint144(uint256 value) internal pure returns (uint144) {
require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toUint136(uint256 value) internal pure returns (uint136) {
require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v2.5._
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toUint120(uint256 value) internal pure returns (uint120) {
require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toUint112(uint256 value) internal pure returns (uint112) {
require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toUint104(uint256 value) internal pure returns (uint104) {
require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.2._
*/
function toUint96(uint256 value) internal pure returns (uint96) {
require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toUint88(uint256 value) internal pure returns (uint88) {
require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toUint80(uint256 value) internal pure returns (uint80) {
require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toUint72(uint256 value) internal pure returns (uint72) {
require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v2.5._
*/
function toUint64(uint256 value) internal pure returns (uint64) {
require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toUint56(uint256 value) internal pure returns (uint56) {
require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toUint48(uint256 value) internal pure returns (uint48) {
require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toUint40(uint256 value) internal pure returns (uint40) {
require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v2.5._
*/
function toUint32(uint256 value) internal pure returns (uint32) {
require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toUint24(uint256 value) internal pure returns (uint24) {
require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v2.5._
*/
function toUint16(uint256 value) internal pure returns (uint16) {
require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v2.5._
*/
function toUint8(uint256 value) internal pure returns (uint8) {
require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*
* _Available since v3.0._
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: value must be positive");
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.7._
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v3.1._
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.7._
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v3.1._
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v3.1._
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v3.1._
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v3.1._
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*
* _Available since v3.0._
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
return int256(value);
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
uint256 private locked = 1;
modifier nonReentrant() virtual {
require(locked == 1, "REENTRANCY");
locked = 2;
_;
locked = 1;
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.8.0;
import {ERC20} from "solmate/tokens/ERC20.sol";
import "./IVotingEscrow.sol";
/**
* @title Fee Distributor
* @notice Distributes any tokens transferred to the contract (e.g. Protocol fees and any BAL emissions) among veBAL
* 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 veBAL
* holders simply transfer the tokens to the `FeeDistributor` contract and then call `checkpointToken`.
*/
interface IFeeDistributor {
event TokenCheckpointed(ERC20 token, uint256 amount, uint256 lastCheckpointTimestamp);
event TokensClaimed(address user, ERC20 token, uint256 amount, uint256 userTokenTimeCursor);
/**
* @notice Returns the VotingEscrow (veBAL) token contract
*/
function getVotingEscrow() external view returns (IVotingEscrow);
/**
* @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 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(ERC20 token) 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, ERC20 token) external view returns (uint256);
/**
* @notice Returns the user's cached balance of veBAL 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 veBAL 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(ERC20 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(ERC20 token, uint256 timestamp) external view returns (uint256);
// 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(ERC20 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(ERC20[] calldata tokens, uint256[] calldata amounts) external;
// Checkpointing
/**
* @notice Caches the total supply of veBAL 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 veBAL 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(ERC20 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(ERC20[] 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, ERC20 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, ERC20[] calldata tokens) external returns (uint256[] memory);
}// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity >=0.6.11;
/**
* @dev Interface for the OptionalOnlyCaller helper, used to opt in to a caller
* verification for a given address to methods that are otherwise callable by any address.
*/
interface IOptionalOnlyCaller {
/**
* @dev Emitted every time setOnlyCallerCheck is called.
*/
event OnlyCallerOptIn(address user, bool enabled);
/**
* @dev Enables / disables verification mechanism for caller.
* @param enabled - True if caller verification shall be enabled, false otherwise.
*/
function setOnlyCallerCheck(bool enabled) external;
function setOnlyCallerCheckWithSignature(address user, bool enabled, bytes memory signature) external;
/**
* @dev Returns true if caller verification is enabled for the given user, false otherwise.
*/
function isOnlyCallerEnabled(address user) external view returns (bool);
}// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity >=0.6.11;
/**
* @dev Interface for the SignatureValidator helper, used to support meta-transactions.
*/
interface ISignaturesValidator {
/**
* @dev Returns the EIP712 domain separator.
*/
function getDomainSeparator() external view returns (bytes32);
/**
* @dev Returns the next nonce used by an address to sign messages.
*/
function getNextNonce(address user) external view returns (uint256);
}// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity >=0.6.11;
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 totalSupply(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 admin() external view returns (address);
function smart_wallet_checker() external view returns (address);
function commit_smart_wallet_checker(address newSmartWalletChecker) external;
function apply_smart_wallet_checker() external;
}// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.8.0;
import "openzeppelin-contracts/utils/cryptography/EIP712.sol";
import "../interfaces/ISignaturesValidator.sol";
/**
* @dev Utility for signing Solidity function calls.
*/
abstract contract EOASignaturesValidator is ISignaturesValidator, EIP712 {
error EOASignaturesValidator__ExpiredSignature();
error EOASignaturesValidator__MalformedSignature();
error EOASignaturesValidator__RevertWithErrorCode(uint256 errorCode);
// Replay attack prevention for each account.
mapping(address => uint256) internal _nextNonce;
function getDomainSeparator() public view override returns (bytes32) {
return _domainSeparatorV4();
}
function getNextNonce(address account) public view override returns (uint256) {
return _nextNonce[account];
}
function _ensureValidSignature(address account, bytes32 structHash, bytes memory signature, uint256 errorCode)
internal
{
return _ensureValidSignature(account, structHash, signature, type(uint256).max, errorCode);
}
function _ensureValidSignature(
address account,
bytes32 structHash,
bytes memory signature,
uint256 deadline,
uint256 errorCode
) internal {
bytes32 digest = _hashTypedDataV4(structHash);
if (!_isValidSignature(account, digest, signature)) {
revert EOASignaturesValidator__RevertWithErrorCode(errorCode);
}
// We could check for the deadline before validating the signature, but this leads to saner error processing (as
// we only care about expired deadlines if the signature is correct) and only affects the gas cost of the revert
// scenario, which will only occur infrequently, if ever.
// The deadline is timestamp-based: it should not be relied upon for sub-minute accuracy.
// solhint-disable-next-line not-rely-on-time
if (deadline < block.timestamp) {
revert EOASignaturesValidator__ExpiredSignature();
}
// We only advance the nonce after validating the signature. This is irrelevant for this module, but it can be
// important in derived contracts that override _isValidSignature (e.g. SignaturesValidator), as we want for
// the observable state to still have the current nonce as the next valid one.
_nextNonce[account] += 1;
}
function _isValidSignature(address account, bytes32 digest, bytes memory signature)
internal
view
virtual
returns (bool)
{
if (signature.length != 65) {
revert EOASignaturesValidator__MalformedSignature();
}
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the r, s and v signature parameters, and the only way to get them is to use assembly.
// solhint-disable-next-line no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
address recoveredAddress = ecrecover(digest, v, r, s);
// ecrecover returns the zero address on recover failure, so we need to handle that explicitly.
return (recoveredAddress != address(0) && recoveredAddress == account);
}
}// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.8.0;
import "./EOASignaturesValidator.sol";
import "../interfaces/IOptionalOnlyCaller.sol";
abstract contract OptionalOnlyCaller is IOptionalOnlyCaller, EOASignaturesValidator {
error OptionalOnlyCaller__SenderNotAllowed();
mapping(address => bool) private _isOnlyCallerEnabled;
uint256 private constant INVALID_SIGNATURE_ERROR = 1;
bytes32 private constant _SET_ONLY_CALLER_CHECK_TYPEHASH =
keccak256("SetOnlyCallerCheck(address user,bool enabled,uint256 nonce)");
/**
* @dev Reverts if the verification mechanism is enabled and the given address is not the caller.
* @param user - Address to validate as the only allowed caller, if the verification is enabled.
*/
modifier optionalOnlyCaller(address user) {
_verifyCaller(user);
_;
}
function setOnlyCallerCheck(bool enabled) external override {
_setOnlyCallerCheck(msg.sender, enabled);
}
function setOnlyCallerCheckWithSignature(address user, bool enabled, bytes memory signature) external override {
bytes32 structHash = keccak256(abi.encode(_SET_ONLY_CALLER_CHECK_TYPEHASH, user, enabled, getNextNonce(user)));
_ensureValidSignature(user, structHash, signature, INVALID_SIGNATURE_ERROR);
_setOnlyCallerCheck(user, enabled);
}
function _setOnlyCallerCheck(address user, bool enabled) private {
_isOnlyCallerEnabled[user] = enabled;
emit OnlyCallerOptIn(user, enabled);
}
function isOnlyCallerEnabled(address user) external view override returns (bool) {
return _isOnlyCallerEnabled[user];
}
function _verifyCaller(address user) private view {
if (_isOnlyCallerEnabled[user]) {
if (msg.sender != user) {
revert OptionalOnlyCaller__SenderNotAllowed();
}
}
}
}{
"remappings": [
"create3-factory/=lib/create3-factory/src/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
"solmate/=lib/solmate/src/"
],
"optimizer": {
"enabled": true,
"runs": 1000000
},
"metadata": {
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"libraries": {}
}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"},{"inputs":[],"name":"EOASignaturesValidator__ExpiredSignature","type":"error"},{"inputs":[],"name":"EOASignaturesValidator__MalformedSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"EOASignaturesValidator__RevertWithErrorCode","type":"error"},{"inputs":[],"name":"FeeDistributor__CannotStartBeforeCurrentWeek","type":"error"},{"inputs":[],"name":"FeeDistributor__InputLengthMismatch","type":"error"},{"inputs":[],"name":"FeeDistributor__VotingEscrowZeroTotalSupply","type":"error"},{"inputs":[],"name":"OptionalOnlyCaller__SenderNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"OnlyCallerOptIn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract ERC20","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":"address","name":"user","type":"address"},{"indexed":false,"internalType":"contract ERC20","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":[],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"}],"name":"checkpointToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20[]","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 ERC20","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 ERC20[]","name":"tokens","type":"address[]"}],"name":"claimTokens","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"depositTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getDomainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getNextNonce","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 ERC20","name":"token","type":"address"}],"name":"getTokenLastBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"}],"name":"getTokenTimeCursor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","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":"getUserTimeCursor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract ERC20","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":"isOnlyCallerEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setOnlyCallerCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bool","name":"enabled","type":"bool"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"setOnlyCallerCheckWithSignature","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
61018060405260016002553480156200001757600080fd5b5060405162002d2738038062002d278339810160408190526200003a9162000213565b604080518082018252600e81526d2332b2a234b9ba3934b13aba37b960911b6020808301918252835180850190945260018452603160f81b908401528151902060e08190527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc66101008190524660a0529192917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6200011e8184846040805160208101859052908101839052606081018290524660808201523060a082015260009060c0016040516020818303038152906040528051906020012090509392505050565b6080523060c05261012052505050506001600160a01b0382166101605262093a80908190048102904281900402808210156200016d576040516344473c4960e01b815260040160405180910390fd5b808203620002025760405163bd85b03960e01b8152600481018290526001600160a01b0384169063bd85b03990602401602060405180830381865afa158015620001bb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001e191906200024f565b6000036200020257604051636e5832e160e01b815260040160405180910390fd5b506101408190526003555062000269565b600080604083850312156200022757600080fd5b82516001600160a01b03811681146200023f57600080fd5b6020939093015192949293505050565b6000602082840312156200026257600080fd5b5051919050565b60805160a05160c05160e05161010051610120516101405161016051612a196200030e600039600081816101a101528181610c8501528181610e280152818161108e01528181611917015281816119d70152611f23015260008181610d4e01528181610ea101528181610f5201526112d801526000611dea01526000611e3901526000611e1401526000611d6d01526000611d9701526000611dc10152612a196000f3fe608060405234801561001057600080fd5b50600436106101825760003560e01c806390193b7c116100d8578063ca31879d1161008c578063e811f44b11610066578063e811f44b146104ae578063ed24911d146104c1578063fcaa54ee146104c957600080fd5b8063ca31879d14610415578063d3dc4ca114610428578063de681faf1461046b57600080fd5b8063a1648aa5116100bd578063a1648aa514610378578063acbc1428146103c1578063c2c4c5c11461040d57600080fd5b806390193b7c1461032f578063905d10ac1461036557600080fd5b80634f3c50901161013a57806382aa5ad41161011457806382aa5ad4146102bb578063876e69a1146102c3578063887204671461030f57600080fd5b80634f3c5090146102755780637b8d6221146102955780638050a7ee146102a857600080fd5b80632308805b1161016b5780632308805b146101e5578063338b5dea1461024f5780633902b9bc1461026257600080fd5b806308b0308a1461018757806314866e08146101d0575b600080fd5b60405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001681526020015b60405180910390f35b6101e36101de3660046124eb565b6104dc565b005b6102416101f33660046124eb565b73ffffffffffffffffffffffffffffffffffffffff1660009081526005602052604090205470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1690565b6040519081526020016101c7565b6101e361025d36600461250f565b610562565b6101e36102703660046124eb565b610613565b61024161028336600461253b565b60009081526004602052604090205490565b6101e36102a33660046125a0565b61068e565b6102416102b636600461260c565b61081d565b600354610241565b6102416102d13660046124eb565b73ffffffffffffffffffffffffffffffffffffffff1660009081526007602052604090205468010000000000000000900467ffffffffffffffff1690565b61032261031d366004612645565b610832565b6040516101c7919061269a565b61024161033d3660046124eb565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b6101e36103733660046126d2565b61098c565b6103b16103863660046124eb565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604090205460ff1690565b60405190151581526020016101c7565b6102416103cf3660046124eb565b73ffffffffffffffffffffffffffffffffffffffff1660009081526005602052604090205468010000000000000000900467ffffffffffffffff1690565b6101e3610a30565b61024161042336600461260c565b610aaf565b61024161043636600461250f565b73ffffffffffffffffffffffffffffffffffffffff919091166000908152600660209081526040808320938352929052205490565b61024161047936600461250f565b73ffffffffffffffffffffffffffffffffffffffff919091166000908152600860209081526040808320938352929052205490565b6101e36104bc366004612724565b610b61565b610241610b6e565b6101e36104d73660046127bd565b610b7d565b60025460011461054d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b6002805561055a81610c3d565b506001600255565b6002546001146105ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610544565b600280556105dd82600061124f565b6105ff73ffffffffffffffffffffffffffffffffffffffff8316333084611718565b61060a82600161124f565b50506001600255565b60025460011461067f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610544565b6002805561055a81600161124f565b6002546001146106fa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610544565b60028055828114610737576040517fb0208d4900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260005b818110156108105761077486868381811061075857610758612893565b905060200201602081019061076d91906124eb565b600061124f565b6107d7333086868581811061078b5761078b612893565b905060200201358989868181106107a4576107a4612893565b90506020020160208101906107b991906124eb565b73ffffffffffffffffffffffffffffffffffffffff16929190611718565b6108088686838181106107ec576107ec612893565b905060200201602081019061080191906124eb565b600161124f565b60010161073b565b5050600160025550505050565b600061082983836117de565b90505b92915050565b60606002546001146108a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610544565b60028055836108ae81611876565b6108b66118f3565b6108bf85610c3d565b8260008167ffffffffffffffff8111156108db576108db61273f565b604051908082528060200260200182016040528015610904578160200160208202803683370190505b50905060005b8281101561097c5761092787878381811061075857610758612893565b6109578888888481811061093d5761093d612893565b905060200201602081019061095291906124eb565b611a7d565b82828151811061096957610969612893565b602090810291909101015260010161090a565b5060016002559695505050505050565b6002546001146109f8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610544565b600280558060005b81811015610a2557610a1d8484838181106107ec576107ec612893565b600101610a00565b505060016002555050565b600254600114610a9c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610544565b60028055610aa86118f3565b6001600255565b6000600254600114610b1d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610544565b6002805582610b2b81611876565b610b336118f3565b610b3c84610c3d565b610b4783600061124f565b6000610b538585611a7d565b600160025595945050505050565b610b6b3382611cc5565b50565b6000610b78611d53565b905090565b60007fbd291ffccec065968fe20c5f8debdad73ab50837733f357eeae8814178015a908484610bce8773ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b60408051602081019590955273ffffffffffffffffffffffffffffffffffffffff9093169284019290925215156060830152608082015260a001604051602081830303815290604052805190602001209050610c2d8482846001611e87565b610c378484611cc5565b50505050565b6040517f010ae75700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063010ae75790602401602060405180830381865afa158015610cce573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cf291906128c2565b905080600003610d00575050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600760205260408120805490916801000000000000000090910467ffffffffffffffff1690818103610d7c57610d75857f0000000000000000000000000000000000000000000000000000000000000000600087611eb4565b9050610dcd565b428210610d8a575050505050565b50815470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1660148185031115610dcd57610dca85838387611eb4565b90505b80600003610dd9575060015b6040517f28d09d4700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152602482018390526000917f0000000000000000000000000000000000000000000000000000000000000000909116906328d09d4790604401608060405180830381865afa158015610e71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9591906128ed565b905082600003610fb8577f00000000000000000000000000000000000000000000000000000000000000004211610f4d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f46656520646973747269627574696f6e20686173206e6f74207374617274656460448201527f20796574000000000000000000000000000000000000000000000000000000006064820152608401610544565b610f837f0000000000000000000000000000000000000000000000000000000000000000610f7e8360400151611fbd565b611fd2565b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff821617855592505b6040805160808101825260008082526020820181905291810182905260608101829052905b60328110156111c45782604001518510158015610ffa5750868411155b1561110057600184019350829150868411156110425760405180608001604052806000600f0b81526020016000600f0b815260200160008152602001600081525092506111bc565b6040517f28d09d4700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8981166004830152602482018690527f000000000000000000000000000000000000000000000000000000000000000016906328d09d4790604401608060405180830381865afa1580156110d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f991906128ed565b92506111bc565b428510156111c457600061112161111c84604001518803611fe8565b61209e565b9050600081846020015102600f0b8460000151600f0b1361114357600061115b565b61115b82856020015102856000015103600f0b612137565b90508015801561116a57508886115b156111815761117842611fbd565b965050506111c4565b73ffffffffffffffffffffffffffffffffffffffff8a1660009081526008602090815260408083208a84529091529020555062093a80909401935b600101610fdd565b5050835467ffffffffffffffff93841668010000000000000000027fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff7001000000000000000000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9095018616949094029390931693169290921717909155505050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600560205260408120805490916801000000000000000090910467ffffffffffffffff16908181036113895742915062093a808083040283547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff919091161783557f00000000000000000000000000000000000000000000000000000000000000004211611384576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f46656520646973747269627574696f6e20686173206e6f74207374617274656460448201527f20796574000000000000000000000000000000000000000000000000000000006064820152608401610544565b6113d9565b8142039050836113d957600062093a808084040262093a804281900402149050600062015180426113b942611fbd565b031090508180156113c8575080155b156113d65750505050505050565b50505b82547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff16680100000000000000004267ffffffffffffffff16021783556040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8716906370a0823190602401602060405180830381865afa158015611483573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114a791906128c2565b84549091506000906114df9070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff168361298e565b9050806000036114f25750505050505050565b6fffffffffffffffffffffffffffffffff82111561156c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4d6178696d756d20746f6b656e2062616c616e636520657863656564656400006044820152606401610544565b84546fffffffffffffffffffffffffffffffff80841670010000000000000000000000000000000002911617855560006115ac8562093a80908190040290565b73ffffffffffffffffffffffffffffffffffffffff8916600090815260066020526040812091925090815b60148110156116b4578362093a800192508242101561164c57861580156115fd57508742145b1561161b5760008481526020839052604090208054860190556116b4565b8688420386028161162e5761162e6129a1565b600086815260208590526040902080549290910490910190556116b4565b8615801561165957508783145b156116775760008481526020839052604090208054860190556116a4565b8688840386028161168a5761168a6129a1565b600086815260208590526040902080549290910490910190555b91965086925082916001016115d7565b506040805173ffffffffffffffffffffffffffffffffffffffff8c168152602081018690529081018890527f9b7f1a85a4c9b4e59e1b6527d9969c50cdfb3a1a467d0c4a51fb0ed8bf07f1309060600160405180910390a150505050505050505050565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806117d7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610544565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152600960209081526040808320938516835292905290812054801561182057905061082c565b73ffffffffffffffffffffffffffffffffffffffff8085166000908152600760209081526040808320549387168352600590915290205461186e9167ffffffffffffffff9081169116611fd2565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001602052604090205460ff1615610b6b573373ffffffffffffffffffffffffffffffffffffffff821614610b6b576040517ff93014bb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60035462093a8042819004028082118061190c57504281145b15611915575050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663c2c4c5c16040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561197d57600080fd5b505af1158015611991573d6000803e3d6000fd5b5050505060005b6014811015611a7657818311611a76576040517fbd85b039000000000000000000000000000000000000000000000000000000008152600481018490527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063bd85b03990602401602060405180830381865afa158015611a33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a5791906128c2565b60008481526004602052604090205562093a8090920191600101611998565b5050600355565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260056020526040812081611aad85856117de565b60035473ffffffffffffffffffffffffffffffffffffffff871660009081526007602052604081205492935091611b3491611b0791611b02919068010000000000000000900467ffffffffffffffff166121a3565b611fbd565b8454611b2f9068010000000000000000900467ffffffffffffffff1662093a80908190040290565b6121a3565b73ffffffffffffffffffffffffffffffffffffffff8087166000908152600660209081526040808320938b16835260089091528120929350909190805b6014811015611bc75784861015611bc75760008681526004602090815260408083205486835281842054928890529220540281611bb057611bb06129a1565b62093a809790970196049190910190600101611b71565b5073ffffffffffffffffffffffffffffffffffffffff808a166000908152600960209081526040808320938c168352929052208590558015611cb95785546fffffffffffffffffffffffffffffffff70010000000000000000000000000000000080830482168490038216029116178655611c5973ffffffffffffffffffffffffffffffffffffffff89168a836121b2565b6040805173ffffffffffffffffffffffffffffffffffffffff808c1682528a166020820152908101829052606081018690527fff097c7d8b1957a4ff09ef1361b5fb54dcede3941ba836d0beb9d10bec725de69060800160405180910390a15b98975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff821660008181526001602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168515159081179091558251938452908301527fac9874a7a931a3f5c9f202c6d9cf40de5d21506993c9f9c38ca8265add89584c910160405180910390a15050565b60003073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016148015611db957507f000000000000000000000000000000000000000000000000000000000000000046145b15611de357507f000000000000000000000000000000000000000000000000000000000000000090565b50604080517f00000000000000000000000000000000000000000000000000000000000000006020808301919091527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b610c378484847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8561226b565b60008282825b6080811015611fb15781831015611fb1576040517f28d09d4700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8981166004830152600284860181010460248301819052916000917f000000000000000000000000000000000000000000000000000000000000000016906328d09d4790604401608060405180830381865afa158015611f6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f8e91906128ed565b905088816040015111611fa357819450611faa565b6001820393505b5050611eba565b50909695505050505050565b600062093a8062093a7f83018190040261082c565b6000818311611fe15781610829565b5090919050565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82111561209a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e206160448201527f6e20696e743235360000000000000000000000000000000000000000000000006064820152608401610544565b5090565b80600f81900b8114612132576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f32382062697473000000000000000000000000000000000000000000000000006064820152608401610544565b919050565b60008082121561209a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f7369746976656044820152606401610544565b6000818310611fe15781610829565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610c37576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610544565b600061227685612339565b90506122838682866123a2565b6122bc576040517fe9a06b8b00000000000000000000000000000000000000000000000000000000815260048101839052602401610544565b428310156122f6576040517f922f30c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8616600090815260208190526040812080546001929061232c9084906129d0565b9091555050505050505050565b600061082c612346611d53565b836040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b600081516041146123df576040517f98af90f600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602082810151604080850151606080870151835160008082529681018086528a905290861a938101849052908101849052608081018290529293909260019060a0016020604051602081039080840390855afa158015612443573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590611cb957508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161498975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610b6b57600080fd5b6000602082840312156124fd57600080fd5b8135612508816124c9565b9392505050565b6000806040838503121561252257600080fd5b823561252d816124c9565b946020939093013593505050565b60006020828403121561254d57600080fd5b5035919050565b60008083601f84011261256657600080fd5b50813567ffffffffffffffff81111561257e57600080fd5b6020830191508360208260051b850101111561259957600080fd5b9250929050565b600080600080604085870312156125b657600080fd5b843567ffffffffffffffff808211156125ce57600080fd5b6125da88838901612554565b909650945060208701359150808211156125f357600080fd5b5061260087828801612554565b95989497509550505050565b6000806040838503121561261f57600080fd5b823561262a816124c9565b9150602083013561263a816124c9565b809150509250929050565b60008060006040848603121561265a57600080fd5b8335612665816124c9565b9250602084013567ffffffffffffffff81111561268157600080fd5b61268d86828701612554565b9497909650939450505050565b6020808252825182820181905260009190848201906040850190845b81811015611fb1578351835292840192918401916001016126b6565b600080602083850312156126e557600080fd5b823567ffffffffffffffff8111156126fc57600080fd5b61270885828601612554565b90969095509350505050565b8035801515811461213257600080fd5b60006020828403121561273657600080fd5b61082982612714565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156127b5576127b561273f565b604052919050565b6000806000606084860312156127d257600080fd5b83356127dd816124c9565b925060206127ec858201612714565b9250604085013567ffffffffffffffff8082111561280957600080fd5b818701915087601f83011261281d57600080fd5b81358181111561282f5761282f61273f565b61285f847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161276e565b9150808252888482850101111561287557600080fd5b80848401858401376000848284010152508093505050509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156128d457600080fd5b5051919050565b8051600f81900b811461213257600080fd5b6000608082840312156128ff57600080fd5b6040516080810181811067ffffffffffffffff821117156129225761292261273f565b60405261292e836128db565b815261293c602084016128db565b602082015260408301516040820152606083015160608201528091505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561082c5761082c61295f565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8082018082111561082c5761082c61295f56fea2646970667358221220a428039de899a501f531932298382fcf2232ddb76930843b9efd1786d24c6c2e64736f6c63430008100033000000000000000000000000f17d23136b4fead139f54fb766c8795faae096600000000000000000000000000000000000000000000000000000000063e15c40
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101825760003560e01c806390193b7c116100d8578063ca31879d1161008c578063e811f44b11610066578063e811f44b146104ae578063ed24911d146104c1578063fcaa54ee146104c957600080fd5b8063ca31879d14610415578063d3dc4ca114610428578063de681faf1461046b57600080fd5b8063a1648aa5116100bd578063a1648aa514610378578063acbc1428146103c1578063c2c4c5c11461040d57600080fd5b806390193b7c1461032f578063905d10ac1461036557600080fd5b80634f3c50901161013a57806382aa5ad41161011457806382aa5ad4146102bb578063876e69a1146102c3578063887204671461030f57600080fd5b80634f3c5090146102755780637b8d6221146102955780638050a7ee146102a857600080fd5b80632308805b1161016b5780632308805b146101e5578063338b5dea1461024f5780633902b9bc1461026257600080fd5b806308b0308a1461018757806314866e08146101d0575b600080fd5b60405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f17d23136b4fead139f54fb766c8795faae096601681526020015b60405180910390f35b6101e36101de3660046124eb565b6104dc565b005b6102416101f33660046124eb565b73ffffffffffffffffffffffffffffffffffffffff1660009081526005602052604090205470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1690565b6040519081526020016101c7565b6101e361025d36600461250f565b610562565b6101e36102703660046124eb565b610613565b61024161028336600461253b565b60009081526004602052604090205490565b6101e36102a33660046125a0565b61068e565b6102416102b636600461260c565b61081d565b600354610241565b6102416102d13660046124eb565b73ffffffffffffffffffffffffffffffffffffffff1660009081526007602052604090205468010000000000000000900467ffffffffffffffff1690565b61032261031d366004612645565b610832565b6040516101c7919061269a565b61024161033d3660046124eb565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b6101e36103733660046126d2565b61098c565b6103b16103863660046124eb565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604090205460ff1690565b60405190151581526020016101c7565b6102416103cf3660046124eb565b73ffffffffffffffffffffffffffffffffffffffff1660009081526005602052604090205468010000000000000000900467ffffffffffffffff1690565b6101e3610a30565b61024161042336600461260c565b610aaf565b61024161043636600461250f565b73ffffffffffffffffffffffffffffffffffffffff919091166000908152600660209081526040808320938352929052205490565b61024161047936600461250f565b73ffffffffffffffffffffffffffffffffffffffff919091166000908152600860209081526040808320938352929052205490565b6101e36104bc366004612724565b610b61565b610241610b6e565b6101e36104d73660046127bd565b610b7d565b60025460011461054d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b6002805561055a81610c3d565b506001600255565b6002546001146105ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610544565b600280556105dd82600061124f565b6105ff73ffffffffffffffffffffffffffffffffffffffff8316333084611718565b61060a82600161124f565b50506001600255565b60025460011461067f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610544565b6002805561055a81600161124f565b6002546001146106fa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610544565b60028055828114610737576040517fb0208d4900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260005b818110156108105761077486868381811061075857610758612893565b905060200201602081019061076d91906124eb565b600061124f565b6107d7333086868581811061078b5761078b612893565b905060200201358989868181106107a4576107a4612893565b90506020020160208101906107b991906124eb565b73ffffffffffffffffffffffffffffffffffffffff16929190611718565b6108088686838181106107ec576107ec612893565b905060200201602081019061080191906124eb565b600161124f565b60010161073b565b5050600160025550505050565b600061082983836117de565b90505b92915050565b60606002546001146108a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610544565b60028055836108ae81611876565b6108b66118f3565b6108bf85610c3d565b8260008167ffffffffffffffff8111156108db576108db61273f565b604051908082528060200260200182016040528015610904578160200160208202803683370190505b50905060005b8281101561097c5761092787878381811061075857610758612893565b6109578888888481811061093d5761093d612893565b905060200201602081019061095291906124eb565b611a7d565b82828151811061096957610969612893565b602090810291909101015260010161090a565b5060016002559695505050505050565b6002546001146109f8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610544565b600280558060005b81811015610a2557610a1d8484838181106107ec576107ec612893565b600101610a00565b505060016002555050565b600254600114610a9c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610544565b60028055610aa86118f3565b6001600255565b6000600254600114610b1d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401610544565b6002805582610b2b81611876565b610b336118f3565b610b3c84610c3d565b610b4783600061124f565b6000610b538585611a7d565b600160025595945050505050565b610b6b3382611cc5565b50565b6000610b78611d53565b905090565b60007fbd291ffccec065968fe20c5f8debdad73ab50837733f357eeae8814178015a908484610bce8773ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b60408051602081019590955273ffffffffffffffffffffffffffffffffffffffff9093169284019290925215156060830152608082015260a001604051602081830303815290604052805190602001209050610c2d8482846001611e87565b610c378484611cc5565b50505050565b6040517f010ae75700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526000917f000000000000000000000000f17d23136b4fead139f54fb766c8795faae096609091169063010ae75790602401602060405180830381865afa158015610cce573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cf291906128c2565b905080600003610d00575050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600760205260408120805490916801000000000000000090910467ffffffffffffffff1690818103610d7c57610d75857f0000000000000000000000000000000000000000000000000000000063dafd00600087611eb4565b9050610dcd565b428210610d8a575050505050565b50815470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1660148185031115610dcd57610dca85838387611eb4565b90505b80600003610dd9575060015b6040517f28d09d4700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152602482018390526000917f000000000000000000000000f17d23136b4fead139f54fb766c8795faae09660909116906328d09d4790604401608060405180830381865afa158015610e71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9591906128ed565b905082600003610fb8577f0000000000000000000000000000000000000000000000000000000063dafd004211610f4d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f46656520646973747269627574696f6e20686173206e6f74207374617274656460448201527f20796574000000000000000000000000000000000000000000000000000000006064820152608401610544565b610f837f0000000000000000000000000000000000000000000000000000000063dafd00610f7e8360400151611fbd565b611fd2565b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff821617855592505b6040805160808101825260008082526020820181905291810182905260608101829052905b60328110156111c45782604001518510158015610ffa5750868411155b1561110057600184019350829150868411156110425760405180608001604052806000600f0b81526020016000600f0b815260200160008152602001600081525092506111bc565b6040517f28d09d4700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8981166004830152602482018690527f000000000000000000000000f17d23136b4fead139f54fb766c8795faae0966016906328d09d4790604401608060405180830381865afa1580156110d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f991906128ed565b92506111bc565b428510156111c457600061112161111c84604001518803611fe8565b61209e565b9050600081846020015102600f0b8460000151600f0b1361114357600061115b565b61115b82856020015102856000015103600f0b612137565b90508015801561116a57508886115b156111815761117842611fbd565b965050506111c4565b73ffffffffffffffffffffffffffffffffffffffff8a1660009081526008602090815260408083208a84529091529020555062093a80909401935b600101610fdd565b5050835467ffffffffffffffff93841668010000000000000000027fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff7001000000000000000000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9095018616949094029390931693169290921717909155505050565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600560205260408120805490916801000000000000000090910467ffffffffffffffff16908181036113895742915062093a808083040283547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff919091161783557f0000000000000000000000000000000000000000000000000000000063dafd004211611384576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f46656520646973747269627574696f6e20686173206e6f74207374617274656460448201527f20796574000000000000000000000000000000000000000000000000000000006064820152608401610544565b6113d9565b8142039050836113d957600062093a808084040262093a804281900402149050600062015180426113b942611fbd565b031090508180156113c8575080155b156113d65750505050505050565b50505b82547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff16680100000000000000004267ffffffffffffffff16021783556040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8716906370a0823190602401602060405180830381865afa158015611483573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114a791906128c2565b84549091506000906114df9070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff168361298e565b9050806000036114f25750505050505050565b6fffffffffffffffffffffffffffffffff82111561156c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4d6178696d756d20746f6b656e2062616c616e636520657863656564656400006044820152606401610544565b84546fffffffffffffffffffffffffffffffff80841670010000000000000000000000000000000002911617855560006115ac8562093a80908190040290565b73ffffffffffffffffffffffffffffffffffffffff8916600090815260066020526040812091925090815b60148110156116b4578362093a800192508242101561164c57861580156115fd57508742145b1561161b5760008481526020839052604090208054860190556116b4565b8688420386028161162e5761162e6129a1565b600086815260208590526040902080549290910490910190556116b4565b8615801561165957508783145b156116775760008481526020839052604090208054860190556116a4565b8688840386028161168a5761168a6129a1565b600086815260208590526040902080549290910490910190555b91965086925082916001016115d7565b506040805173ffffffffffffffffffffffffffffffffffffffff8c168152602081018690529081018890527f9b7f1a85a4c9b4e59e1b6527d9969c50cdfb3a1a467d0c4a51fb0ed8bf07f1309060600160405180910390a150505050505050505050565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806117d7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610544565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152600960209081526040808320938516835292905290812054801561182057905061082c565b73ffffffffffffffffffffffffffffffffffffffff8085166000908152600760209081526040808320549387168352600590915290205461186e9167ffffffffffffffff9081169116611fd2565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001602052604090205460ff1615610b6b573373ffffffffffffffffffffffffffffffffffffffff821614610b6b576040517ff93014bb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60035462093a8042819004028082118061190c57504281145b15611915575050565b7f000000000000000000000000f17d23136b4fead139f54fb766c8795faae0966073ffffffffffffffffffffffffffffffffffffffff1663c2c4c5c16040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561197d57600080fd5b505af1158015611991573d6000803e3d6000fd5b5050505060005b6014811015611a7657818311611a76576040517fbd85b039000000000000000000000000000000000000000000000000000000008152600481018490527f000000000000000000000000f17d23136b4fead139f54fb766c8795faae0966073ffffffffffffffffffffffffffffffffffffffff169063bd85b03990602401602060405180830381865afa158015611a33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a5791906128c2565b60008481526004602052604090205562093a8090920191600101611998565b5050600355565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260056020526040812081611aad85856117de565b60035473ffffffffffffffffffffffffffffffffffffffff871660009081526007602052604081205492935091611b3491611b0791611b02919068010000000000000000900467ffffffffffffffff166121a3565b611fbd565b8454611b2f9068010000000000000000900467ffffffffffffffff1662093a80908190040290565b6121a3565b73ffffffffffffffffffffffffffffffffffffffff8087166000908152600660209081526040808320938b16835260089091528120929350909190805b6014811015611bc75784861015611bc75760008681526004602090815260408083205486835281842054928890529220540281611bb057611bb06129a1565b62093a809790970196049190910190600101611b71565b5073ffffffffffffffffffffffffffffffffffffffff808a166000908152600960209081526040808320938c168352929052208590558015611cb95785546fffffffffffffffffffffffffffffffff70010000000000000000000000000000000080830482168490038216029116178655611c5973ffffffffffffffffffffffffffffffffffffffff89168a836121b2565b6040805173ffffffffffffffffffffffffffffffffffffffff808c1682528a166020820152908101829052606081018690527fff097c7d8b1957a4ff09ef1361b5fb54dcede3941ba836d0beb9d10bec725de69060800160405180910390a15b98975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff821660008181526001602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168515159081179091558251938452908301527fac9874a7a931a3f5c9f202c6d9cf40de5d21506993c9f9c38ca8265add89584c910160405180910390a15050565b60003073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000951f99350d816c0e160a2c71defe828bdfc17f1216148015611db957507f000000000000000000000000000000000000000000000000000000000000000146145b15611de357507f8bddfbe7a6a37f0262112d2cb0eefbcfe5248abd98001dd69e1c4bf08bffc3b290565b50604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020808301919091527f0618c188edbe06a8ffa15e11b4f74493cfd6f23aba7fab610364d908072aac99828401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b610c378484847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8561226b565b60008282825b6080811015611fb15781831015611fb1576040517f28d09d4700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8981166004830152600284860181010460248301819052916000917f000000000000000000000000f17d23136b4fead139f54fb766c8795faae0966016906328d09d4790604401608060405180830381865afa158015611f6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f8e91906128ed565b905088816040015111611fa357819450611faa565b6001820393505b5050611eba565b50909695505050505050565b600062093a8062093a7f83018190040261082c565b6000818311611fe15781610829565b5090919050565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82111561209a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e206160448201527f6e20696e743235360000000000000000000000000000000000000000000000006064820152608401610544565b5090565b80600f81900b8114612132576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f32382062697473000000000000000000000000000000000000000000000000006064820152608401610544565b919050565b60008082121561209a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f7369746976656044820152606401610544565b6000818310611fe15781610829565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610c37576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610544565b600061227685612339565b90506122838682866123a2565b6122bc576040517fe9a06b8b00000000000000000000000000000000000000000000000000000000815260048101839052602401610544565b428310156122f6576040517f922f30c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8616600090815260208190526040812080546001929061232c9084906129d0565b9091555050505050505050565b600061082c612346611d53565b836040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b600081516041146123df576040517f98af90f600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602082810151604080850151606080870151835160008082529681018086528a905290861a938101849052908101849052608081018290529293909260019060a0016020604051602081039080840390855afa158015612443573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590611cb957508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161498975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610b6b57600080fd5b6000602082840312156124fd57600080fd5b8135612508816124c9565b9392505050565b6000806040838503121561252257600080fd5b823561252d816124c9565b946020939093013593505050565b60006020828403121561254d57600080fd5b5035919050565b60008083601f84011261256657600080fd5b50813567ffffffffffffffff81111561257e57600080fd5b6020830191508360208260051b850101111561259957600080fd5b9250929050565b600080600080604085870312156125b657600080fd5b843567ffffffffffffffff808211156125ce57600080fd5b6125da88838901612554565b909650945060208701359150808211156125f357600080fd5b5061260087828801612554565b95989497509550505050565b6000806040838503121561261f57600080fd5b823561262a816124c9565b9150602083013561263a816124c9565b809150509250929050565b60008060006040848603121561265a57600080fd5b8335612665816124c9565b9250602084013567ffffffffffffffff81111561268157600080fd5b61268d86828701612554565b9497909650939450505050565b6020808252825182820181905260009190848201906040850190845b81811015611fb1578351835292840192918401916001016126b6565b600080602083850312156126e557600080fd5b823567ffffffffffffffff8111156126fc57600080fd5b61270885828601612554565b90969095509350505050565b8035801515811461213257600080fd5b60006020828403121561273657600080fd5b61082982612714565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156127b5576127b561273f565b604052919050565b6000806000606084860312156127d257600080fd5b83356127dd816124c9565b925060206127ec858201612714565b9250604085013567ffffffffffffffff8082111561280957600080fd5b818701915087601f83011261281d57600080fd5b81358181111561282f5761282f61273f565b61285f847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161276e565b9150808252888482850101111561287557600080fd5b80848401858401376000848284010152508093505050509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156128d457600080fd5b5051919050565b8051600f81900b811461213257600080fd5b6000608082840312156128ff57600080fd5b6040516080810181811067ffffffffffffffff821117156129225761292261273f565b60405261292e836128db565b815261293c602084016128db565b602082015260408301516040820152606083015160608201528091505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561082c5761082c61295f565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8082018082111561082c5761082c61295f56fea2646970667358221220a428039de899a501f531932298382fcf2232ddb76930843b9efd1786d24c6c2e64736f6c63430008100033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000f17d23136b4fead139f54fb766c8795faae096600000000000000000000000000000000000000000000000000000000063e15c40
-----Decoded View---------------
Arg [0] : votingEscrow (address): 0xf17d23136B4FeAd139f54fB766c8795faae09660
Arg [1] : startTime (uint256): 1675713600
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000f17d23136b4fead139f54fb766c8795faae09660
Arg [1] : 0000000000000000000000000000000000000000000000000000000063e15c40
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.