More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 559 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Claim Tokens | 19290490 | 359 days ago | IN | 0 ETH | 0.02854543 | ||||
Claim Token | 19234706 | 367 days ago | IN | 0 ETH | 0.03662923 | ||||
Claim Tokens | 19227850 | 368 days ago | IN | 0 ETH | 0.03910657 | ||||
Claim Tokens | 19157315 | 378 days ago | IN | 0 ETH | 0.01421774 | ||||
Claim Tokens | 19137360 | 381 days ago | IN | 0 ETH | 0.01247897 | ||||
Claim Tokens | 19136481 | 381 days ago | IN | 0 ETH | 0.02879842 | ||||
Claim Tokens | 19096965 | 386 days ago | IN | 0 ETH | 0.00083644 | ||||
Claim Tokens | 19096965 | 386 days ago | IN | 0 ETH | 0.01112873 | ||||
Claim Tokens | 19095621 | 387 days ago | IN | 0 ETH | 0.00818807 | ||||
Claim Tokens | 19068830 | 390 days ago | IN | 0 ETH | 0.01223157 | ||||
Claim Tokens | 19038589 | 395 days ago | IN | 0 ETH | 0.01269076 | ||||
Claim Tokens | 19033008 | 395 days ago | IN | 0 ETH | 0.02558061 | ||||
Claim Tokens | 19012857 | 398 days ago | IN | 0 ETH | 0.01530943 | ||||
Claim Tokens | 19012101 | 398 days ago | IN | 0 ETH | 0.01519221 | ||||
Claim Tokens | 19000325 | 400 days ago | IN | 0 ETH | 0.00944755 | ||||
Claim Tokens | 18995516 | 401 days ago | IN | 0 ETH | 0.00977408 | ||||
Claim Tokens | 18994608 | 401 days ago | IN | 0 ETH | 0.00204739 | ||||
Claim Tokens | 18994604 | 401 days ago | IN | 0 ETH | 0.01088133 | ||||
Claim Tokens | 18989844 | 401 days ago | IN | 0 ETH | 0.01086764 | ||||
Claim Tokens | 18988775 | 402 days ago | IN | 0 ETH | 0.01665966 | ||||
Claim Tokens | 18979522 | 403 days ago | IN | 0 ETH | 0.03874476 | ||||
Claim Tokens | 18973238 | 404 days ago | IN | 0 ETH | 0.00909786 | ||||
Claim Tokens | 18972585 | 404 days ago | IN | 0 ETH | 0.0101656 | ||||
Claim Tokens | 18971308 | 404 days ago | IN | 0 ETH | 0.00086044 | ||||
Claim Tokens | 18971308 | 404 days ago | IN | 0 ETH | 0.00764318 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
FeeDistributor
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 200 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 "../lib/openzeppelin-contracts/contracts/utils/math/Math.sol"; import {SafeCast} from "../lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol"; import {ERC20} from "../lib/solmate/src/tokens/ERC20.sol"; import {SafeTransferLib} from "../lib/solmate/src/utils/SafeTransferLib.sol"; import {ReentrancyGuard} from "../lib/solmate/src/utils/ReentrancyGuard.sol"; import "./lib/OptionalOnlyCaller.sol"; import "./interfaces/IVotingEscrow.sol"; import "./interfaces/IFeeDistributor.sol"; // solhint-disable not-rely-on-time /** * @title FeeDistributor * @author opnxj - Modified from ZeframLou/fee-distributor: https://github.com/ZeframLou/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 (_veTotalSupply(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); } /** * @notice Calculate total voting power at some point in the past * @param point The point (bias/slope) to start search from * @param t Time to calculate the total voting power at * @return Total voting power at that time */ function _supplyAt( IVotingEscrow.Point memory point, uint256 t ) internal view returns (uint256) { IVotingEscrow.Point memory lastPoint = point; uint tI = _roundDownTimestamp(lastPoint.ts); for (uint i = 0; i < 255; ++i) { tI += 1 weeks; int128 dSlope = 0; if (tI > t) { tI = t; } else { dSlope = IVotingEscrow(_votingEscrow).slope_changes(tI); } lastPoint.bias -= lastPoint.slope * int128(int(tI) - int(lastPoint.ts)); if (tI == t) { break; } lastPoint.slope += dSlope; lastPoint.ts = tI; } if (lastPoint.bias < 0) { lastPoint.bias = 0; } return uint(uint128(lastPoint.bias)); } /** * @dev Provides a better implementation of VE.totalSupplyAtT */ function _veTotalSupply(uint256 timestamp) internal view returns (uint256) { uint256 _epoch = IVotingEscrow(_votingEscrow).epoch(); IVotingEscrow.Point memory lastPoint = IVotingEscrow(_votingEscrow) .point_history(_epoch); return _supplyAt(lastPoint, timestamp); } /** * @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] = _veTotalSupply( 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.9.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) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 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 256, 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.8.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; 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; /// @solidity memory-safe-assembly 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; /// @solidity memory-safe-assembly 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; /// @solidity memory-safe-assembly 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; /// @solidity memory-safe-assembly 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: 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: 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(); } } } }
// 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 user_point_epoch(address user) external view returns (uint256); function slope_changes(uint256 i) external view returns (int128); 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; }
// 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 "../../lib/solmate/src/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 // 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 "../../lib/openzeppelin-contracts/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-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: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol) pragma solidity ^0.8.8; import "./ECDSA.sol"; import "../ShortStrings.sol"; import "../../interfaces/IERC5267.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]. * * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the * separator from the immutable values, which is cheaper than accessing a cached version in cold storage. * * _Available since v3.4._ * * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment */ abstract contract EIP712 is IERC5267 { using ShortStrings for *; bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); // 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 _cachedDomainSeparator; uint256 private immutable _cachedChainId; address private immutable _cachedThis; bytes32 private immutable _hashedName; bytes32 private immutable _hashedVersion; ShortString private immutable _name; ShortString private immutable _version; string private _nameFallback; string private _versionFallback; /** * @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) { _name = name.toShortStringWithFallback(_nameFallback); _version = version.toShortStringWithFallback(_versionFallback); _hashedName = keccak256(bytes(name)); _hashedVersion = keccak256(bytes(version)); _cachedChainId = block.chainid; _cachedDomainSeparator = _buildDomainSeparator(); _cachedThis = address(this); } /** * @dev Returns the domain separator for the current chain. */ function _domainSeparatorV4() internal view returns (bytes32) { if (address(this) == _cachedThis && block.chainid == _cachedChainId) { return _cachedDomainSeparator; } else { return _buildDomainSeparator(); } } function _buildDomainSeparator() private view returns (bytes32) { return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, 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); } /** * @dev See {EIP-5267}. * * _Available since v4.9._ */ function eip712Domain() public view virtual override returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ) { return ( hex"0f", // 01111 _name.toStringWithFallback(_nameFallback), _version.toStringWithFallback(_versionFallback), block.chainid, address(this), bytes32(0), new uint256[](0) ); } }
// 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: MIT // OpenZeppelin Contracts (last updated v4.9.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 message) { // 32 is the length in bytes of hash, // enforced by the type signature above /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") mstore(0x1c, hash) message := keccak256(0x00, 0x3c) } } /** * @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 data) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, "\x19\x01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) data := keccak256(ptr, 0x42) } } /** * @dev Returns an Ethereum Signed Data with intended validator, created from a * `validator` and `data` according to the version 0 of EIP-191. * * See {recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x00", validator, data)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol) pragma solidity ^0.8.8; import "./StorageSlot.sol"; // | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | // | length | 0x BB | type ShortString is bytes32; /** * @dev This library provides functions to convert short memory strings * into a `ShortString` type that can be used as an immutable variable. * * Strings of arbitrary length can be optimized using this library if * they are short enough (up to 31 bytes) by packing them with their * length (1 byte) in a single EVM word (32 bytes). Additionally, a * fallback mechanism can be used for every other case. * * Usage example: * * ```solidity * contract Named { * using ShortStrings for *; * * ShortString private immutable _name; * string private _nameFallback; * * constructor(string memory contractName) { * _name = contractName.toShortStringWithFallback(_nameFallback); * } * * function name() external view returns (string memory) { * return _name.toStringWithFallback(_nameFallback); * } * } * ``` */ library ShortStrings { // Used as an identifier for strings longer than 31 bytes. bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF; error StringTooLong(string str); error InvalidShortString(); /** * @dev Encode a string of at most 31 chars into a `ShortString`. * * This will trigger a `StringTooLong` error is the input string is too long. */ function toShortString(string memory str) internal pure returns (ShortString) { bytes memory bstr = bytes(str); if (bstr.length > 31) { revert StringTooLong(str); } return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length)); } /** * @dev Decode a `ShortString` back to a "normal" string. */ function toString(ShortString sstr) internal pure returns (string memory) { uint256 len = byteLength(sstr); // using `new string(len)` would work locally but is not memory safe. string memory str = new string(32); /// @solidity memory-safe-assembly assembly { mstore(str, len) mstore(add(str, 0x20), sstr) } return str; } /** * @dev Return the length of a `ShortString`. */ function byteLength(ShortString sstr) internal pure returns (uint256) { uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF; if (result > 31) { revert InvalidShortString(); } return result; } /** * @dev Encode a string into a `ShortString`, or write it to storage if it is too long. */ function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) { if (bytes(value).length < 32) { return toShortString(value); } else { StorageSlot.getStringSlot(store).value = value; return ShortString.wrap(_FALLBACK_SENTINEL); } } /** * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}. */ function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) { if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) { return toString(value); } else { return store; } } /** * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}. * * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of * actual characters as the UTF-8 encoding of a single character can span over multiple bytes. */ function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) { if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) { return byteLength(value); } else { return bytes(store).length; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol) pragma solidity ^0.8.0; interface IERC5267 { /** * @dev MAY be emitted to signal that the domain could have changed. */ event EIP712DomainChanged(); /** * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712 * signature. */ function eip712Domain() external view returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SignedMath.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 `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); } /** * @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); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol) // This file was procedurally generated from scripts/generate/templates/StorageSlot.js. pragma solidity ^0.8.0; /** * @dev Library for reading and writing primitive types to specific storage slots. * * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. * This library helps with reading and writing to such slots without the need for inline assembly. * * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. * * Example usage to set ERC1967 implementation slot: * ```solidity * contract ERC1967 { * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; * * function _getImplementation() internal view returns (address) { * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; * } * * function _setImplementation(address newImplementation) internal { * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` * * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._ * _Available since v4.9 for `string`, `bytes`._ */ library StorageSlot { struct AddressSlot { address value; } struct BooleanSlot { bool value; } struct Bytes32Slot { bytes32 value; } struct Uint256Slot { uint256 value; } struct StringSlot { string value; } struct BytesSlot { bytes value; } /** * @dev Returns an `AddressSlot` with member `value` located at `slot`. */ function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `BooleanSlot` with member `value` located at `slot`. */ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. */ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `Uint256Slot` with member `value` located at `slot`. */ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `StringSlot` with member `value` located at `slot`. */ function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `StringSlot` representation of the string storage pointer `store`. */ function getStringSlot(string storage store) internal pure returns (StringSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := store.slot } } /** * @dev Returns an `BytesSlot` with member `value` located at `slot`. */ function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`. */ function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := store.slot } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
{ "remappings": [ "@openzeppelin/=lib/openzeppelin-contracts/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "openzeppelin/=lib/openzeppelin-contracts/contracts/", "solidity-examples/=lib/solidity-examples/contracts/", "solmate/=lib/solmate/src/", "superallowlist/=lib/superallowlist/", "v2-core/=lib/v2-core/contracts/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"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":"InvalidShortString","type":"error"},{"inputs":[],"name":"OptionalOnlyCaller__SenderNotAllowed","type":"error"},{"inputs":[{"internalType":"string","name":"str","type":"string"}],"name":"StringTooLong","type":"error"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"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":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","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
6101a060405260016004553480156200001757600080fd5b5060405162002f3c38038062002f3c8339810160408190526200003a9162000490565b604080518082018252600e81526d2332b2a234b9ba3934b13aba37b960911b602080830191909152825180840190935260018352603160f81b908301529062000085826000620001b2565b6101205262000096816001620001b2565b61014052815160208084019190912060e052815190820120610100524660a0526200012460e05161010051604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b60805250503060c0526001600160a01b0382166101805262093a80908190048102904281900402808210156200016d576040516344473c4960e01b815260040160405180910390fd5b808203620001a1576200018081620001eb565b600003620001a157604051636e5832e160e01b815260040160405180910390fd5b506101608190526005555062000877565b6000602083511015620001d257620001ca83620002e6565b9050620001e5565b81620001df848262000571565b5060ff90505b92915050565b600080610180516001600160a01b031663900cf0cf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000230573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200025691906200063d565b90506000610180516001600160a01b031663d1febfb9836040518263ffffffff1660e01b81526004016200028c91815260200190565b608060405180830381865afa158015620002aa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002d091906200066f565b9050620002de818562000332565b949350505050565b600080829050601f815111156200031d578260405163305a27a960e01b8152600401620003149190620006e8565b60405180910390fd5b80516200032a8262000738565b179392505050565b6040820151600090839062093a809081900402825b60ff81101562000467576200036062093a808362000773565b91506000858311156200037657859250620003ea565b61018051604051631c465d2160e21b8152600481018590526001600160a01b0390911690637119748490602401602060405180830381865afa158015620003c1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003e7919062000789565b90505b6040840151620003fb9084620007ae565b84602001516200040c9190620007d8565b845185906200041d908390620007fb565b600f0b90525085830362000432575062000467565b80846020018181516200044691906200082b565b600f0b90525050604083018290526200045f816200085b565b905062000347565b5060008260000151600f0b12156200047e57600082525b50516001600160801b03169392505050565b60008060408385031215620004a457600080fd5b82516001600160a01b0381168114620004bc57600080fd5b6020939093015192949293505050565b634e487b7160e01b600052604160045260246000fd5b600181811c90821680620004f757607f821691505b6020821081036200051857634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200056c57600081815260208120601f850160051c81016020861015620005475750805b601f850160051c820191505b81811015620005685782815560010162000553565b5050505b505050565b81516001600160401b038111156200058d576200058d620004cc565b620005a5816200059e8454620004e2565b846200051e565b602080601f831160018114620005dd5760008415620005c45750858301515b600019600386901b1c1916600185901b17855562000568565b600085815260208120601f198616915b828110156200060e57888601518255948401946001909101908401620005ed565b50858210156200062d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6000602082840312156200065057600080fd5b5051919050565b8051600f81900b81146200066a57600080fd5b919050565b6000608082840312156200068257600080fd5b604051608081016001600160401b0381118282101715620006a757620006a7620004cc565b604052620006b58362000657565b8152620006c56020840162000657565b602082015260408301516040820152606083015160608201528091505092915050565b600060208083528351808285015260005b818110156200071757858101830151858201604001528201620006f9565b506000604082860101526040601f19601f8301168501019250505092915050565b80516020808301519190811015620005185760001960209190910360031b1b16919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115620001e557620001e56200075d565b6000602082840312156200079c57600080fd5b620007a78262000657565b9392505050565b8181036000831280158383131683831282161715620007d157620007d16200075d565b5092915050565b600082600f0b82600f0b0280600f0b9150808214620007d157620007d16200075d565b600f82810b9082900b0360016001607f1b0319811260016001607f1b0382131715620001e557620001e56200075d565b600f81810b9083900b0160016001607f1b03811360016001607f1b031982121715620001e557620001e56200075d565b6000600182016200087057620008706200075d565b5060010190565b60805160a05160c05160e0516101005161012051610140516101605161018051612607620009356000396000818161015f015281816109b101528181610b0501528181610cbe015281816113d30152818161184401528181611a7601528181611b1a0152611d24015260008181610a6701528181610b7e01528181610bc00152610e8b01526000610673015260006106490152600061179b01526000611773015260006116ce015260006116f80152600061172201526126076000f3fe608060405234801561001057600080fd5b506004361061014d5760003560e01c806388720467116100c3578063ca31879d1161007c578063ca31879d1461038b578063d3dc4ca11461039e578063de681faf146103d4578063e811f44b1461040a578063ed24911d1461041d578063fcaa54ee1461042557600080fd5b806388720467146102b257806390193b7c146102d2578063905d10ac146102fb578063a1648aa51461030e578063acbc14281461034a578063c2c4c5c11461038357600080fd5b80634f3c5090116101155780634f3c5090146102105780637b8d6221146102305780638050a7ee1461024357806382aa5ad41461025657806384b0196e1461025e578063876e69a11461027957600080fd5b806308b0308a1461015257806314866e081461018e5780632308805b146101a3578063338b5dea146101ea5780633902b9bc146101fd575b600080fd5b6040516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681526020015b60405180910390f35b6101a161019c366004611f3d565b610438565b005b6101dc6101b1366004611f3d565b6001600160a01b0316600090815260076020526040902054600160801b90046001600160801b031690565b604051908152602001610185565b6101a16101f8366004611f61565b610479565b6101a161020b366004611f3d565b6104d4565b6101dc61021e366004611f8d565b60009081526006602052604090205490565b6101a161023e366004611ff1565b610506565b6101dc61025136600461205c565b610626565b6005546101dc565b61026661063b565b6040516101859796959493929190612116565b6101dc610287366004611f3d565b6001600160a01b0316600090815260096020526040902054600160401b90046001600160401b031690565b6102c56102c0366004612186565b6106c3565b60405161018591906121da565b6101dc6102e0366004611f3d565b6001600160a01b031660009081526002602052604090205490565b6101a16103093660046121ed565b6107d3565b61033a61031c366004611f3d565b6001600160a01b031660009081526003602052604090205460ff1690565b6040519015158152602001610185565b6101dc610358366004611f3d565b6001600160a01b0316600090815260076020526040902054600160401b90046001600160401b031690565b6101a161082e565b6101dc61039936600461205c565b610864565b6101dc6103ac366004611f61565b6001600160a01b03919091166000908152600860209081526040808320938352929052205490565b6101dc6103e2366004611f61565b6001600160a01b03919091166000908152600a60209081526040808320938352929052205490565b6101a161041836600461223e565b6108cd565b6101dc6108da565b6101a161043336600461229f565b6108e9565b6004546001146104635760405162461bcd60e51b815260040161045a90612356565b60405180910390fd5b60026004556104718161098f565b506001600455565b60045460011461049b5760405162461bcd60e51b815260040161045a90612356565b60026004556104ab826000610e2d565b6104c06001600160a01b0383163330846111b3565b6104cb826001610e2d565b50506001600455565b6004546001146104f65760405162461bcd60e51b815260040161045a90612356565b6002600455610471816001610e2d565b6004546001146105285760405162461bcd60e51b815260040161045a90612356565b600260045582811461054d5760405163b0208d4960e01b815260040160405180910390fd5b8260005b818110156106195761058a86868381811061056e5761056e61237a565b90506020020160208101906105839190611f3d565b6000610e2d565b6105e033308686858181106105a1576105a161237a565b905060200201358989868181106105ba576105ba61237a565b90506020020160208101906105cf9190611f3d565b6001600160a01b03169291906111b3565b6106118686838181106105f5576105f561237a565b905060200201602081019061060a9190611f3d565b6001610e2d565b600101610551565b5050600160045550505050565b6000610632838361123d565b90505b92915050565b60006060808280808361066e7f0000000000000000000000000000000000000000000000000000000000000000836112ba565b6106997f000000000000000000000000000000000000000000000000000000000000000060016112ba565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b60606004546001146106e75760405162461bcd60e51b815260040161045a90612356565b6002600455836106f681611365565b6106fe6113af565b6107078561098f565b826000816001600160401b0381111561072257610722612259565b60405190808252806020026020018201604052801561074b578160200160208202803683370190505b50905060005b828110156107c35761076e87878381811061056e5761056e61237a565b61079e888888848181106107845761078461237a565b90506020020160208101906107999190611f3d565b611486565b8282815181106107b0576107b061237a565b6020908102919091010152600101610751565b5060016004559695505050505050565b6004546001146107f55760405162461bcd60e51b815260040161045a90612356565b60026004558060005b818110156108235761081b8484838181106105f5576105f561237a565b6001016107fe565b505060016004555050565b6004546001146108505760405162461bcd60e51b815260040161045a90612356565b600260045561085d6113af565b6001600455565b60006004546001146108885760405162461bcd60e51b815260040161045a90612356565b60026004558261089781611365565b61089f6113af565b6108a88461098f565b6108b3836000610e2d565b60006108bf8585611486565b600160045595945050505050565b6108d7338261165e565b50565b60006108e46116c1565b905090565b60007fbd291ffccec065968fe20c5f8debdad73ab50837733f357eeae8814178015a90848461092d876001600160a01b031660009081526002602052604090205490565b6040805160208101959095526001600160a01b039093169284019290925215156060830152608082015260a00160405160208183030381529060405280519060200120905061097f84828460016117ec565b610989848461165e565b50505050565b60405163010ae75760e01b81526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063010ae75790602401602060405180830381865afa1580156109fa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1e9190612390565b905080600003610a2c575050565b6001600160a01b038216600090815260096020526040812080549091600160401b9091046001600160401b031690818103610a9557610a8e857f00000000000000000000000000000000000000000000000000000000000000006000876117fb565b9050610ad0565b428210610aa3575050505050565b508154600160801b90046001600160801b031660148185031115610ad057610acd858383876117fb565b90505b80600003610adc575060015b6040516328d09d4760e01b81526001600160a01b038681166004830152602482018390526000917f0000000000000000000000000000000000000000000000000000000000000000909116906328d09d4790604401608060405180830381865afa158015610b4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7291906123bb565b905082600003610c0e577f00000000000000000000000000000000000000000000000000000000000000004211610bbb5760405162461bcd60e51b815260040161045a9061242c565b610bf17f0000000000000000000000000000000000000000000000000000000000000000610bec83604001516118de565b6118f3565b845467ffffffffffffffff19166001600160401b03821617855592505b6040805160808101825260008082526020820181905291810182905260608101829052905b6032811015610de75782604001518510158015610c505750868411155b15610d305760018401935082915086841115610c985760405180608001604052806000600f0b81526020016000600f0b81526020016000815260200160008152509250610ddf565b6040516328d09d4760e01b81526001600160a01b038981166004830152602482018690527f000000000000000000000000000000000000000000000000000000000000000016906328d09d4790604401608060405180830381865afa158015610d05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2991906123bb565b9250610ddf565b42851015610de7576000610d51610d4c84604001518803611909565b611977565b9050600081846020015102600f0b8460000151600f0b13610d73576000610d8b565b610d8b82856020015102856000015103600f0b6119e0565b905080158015610d9a57508886115b15610db157610da8426118de565b96505050610de7565b6001600160a01b038a166000908152600a602090815260408083208a84529091529020555062093a80909401935b600101610c33565b505083546001600160401b03938416600160401b0267ffffffffffffffff60401b19600160801b6000199095018616949094029390931693169290921717909155505050565b6001600160a01b038216600090815260076020526040812080549091600160401b9091046001600160401b031690818103610ecd5742915062093a8080830402835467ffffffffffffffff19166001600160401b03919091161783557f00000000000000000000000000000000000000000000000000000000000000004211610ec85760405162461bcd60e51b815260040161045a9061242c565b610f1d565b814203905083610f1d57600062093a808084040262093a80428190040214905060006201518042610efd426118de565b03109050818015610f0c575080155b15610f1a5750505050505050565b50505b825467ffffffffffffffff60401b1916600160401b426001600160401b0316021783556040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015610f87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fab9190612390565b8454909150600090610fcd90600160801b90046001600160801b031683612486565b905080600003610fe05750505050505050565b6001600160801b038211156110375760405162461bcd60e51b815260206004820152601e60248201527f4d6178696d756d20746f6b656e2062616c616e63652065786365656465640000604482015260640161045a565b84546001600160801b03808416600160801b02911617855560006110618562093a80908190040290565b6001600160a01b038916600090815260086020526040812091925090815b601481101561115c578362093a80019250824210156110f457861580156110a557508742145b156110c357600084815260208390526040902080548601905561115c565b868842038602816110d6576110d6612499565b6000868152602085905260409020805492909104909101905561115c565b8615801561110157508783145b1561111f57600084815260208390526040902080548601905561114c565b8688840386028161113257611132612499565b600086815260208590526040902080549290910490910190555b919650869250829160010161107f565b50604080516001600160a01b038c168152602081018690529081018890527f9b7f1a85a4c9b4e59e1b6527d9969c50cdfb3a1a467d0c4a51fb0ed8bf07f1309060600160405180910390a150505050505050505050565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806112365760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b604482015260640161045a565b5050505050565b6001600160a01b038083166000908152600b602090815260408083209385168352929052908120548015611272579050610635565b6001600160a01b03808516600090815260096020908152604080832054938716835260079091529020546112b2916001600160401b0390811691166118f3565b949350505050565b606060ff83146112d4576112cd83611a32565b9050610635565b8180546112e0906124af565b80601f016020809104026020016040519081016040528092919081815260200182805461130c906124af565b80156113595780601f1061132e57610100808354040283529160200191611359565b820191906000526020600020905b81548152906001019060200180831161133c57829003601f168201915b50505050509050610635565b6001600160a01b03811660009081526003602052604090205460ff16156108d757336001600160a01b038216146108d75760405163f93014bb60e01b815260040160405180910390fd5b60055462093a804281900402808211806113c857504281145b156113d1575050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c2c4c5c16040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561142c57600080fd5b505af1158015611440573d6000803e3d6000fd5b5050505060005b601481101561147f5781831161147f5761146083611a71565b60008481526006602052604090205562093a8090920191600101611447565b5050600555565b6001600160a01b0381166000908152600760205260408120816114a9858561123d565b6005546001600160a01b03871660009081526009602052604081205492935091611517916114f0916114eb9190600160401b90046001600160401b0316611b91565b6118de565b845461151290600160401b90046001600160401b031662093a80908190040290565b611b91565b6001600160a01b038087166000908152600860209081526040808320938b168352600a9091528120929350909190805b601481101561159d578486101561159d576000868152600660209081526040808320548683528184205492889052922054028161158657611586612499565b62093a809790970196049190910190600101611547565b506001600160a01b03808a166000908152600b60209081526040808320938c1683529290522085905580156116525785546001600160801b03600160801b808304821684900382160291161786556115ff6001600160a01b0389168a83611ba0565b604080516001600160a01b03808c1682528a166020820152908101829052606081018690527fff097c7d8b1957a4ff09ef1361b5fb54dcede3941ba836d0beb9d10bec725de69060800160405180910390a15b98975050505050505050565b6001600160a01b038216600081815260036020908152604091829020805460ff19168515159081179091558251938452908301527fac9874a7a931a3f5c9f202c6d9cf40de5d21506993c9f9c38ca8265add89584c910160405180910390a15050565b6000306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614801561171a57507f000000000000000000000000000000000000000000000000000000000000000046145b1561174457507f000000000000000000000000000000000000000000000000000000000000000090565b6108e4604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b61098984848460001985611c18565b60008282825b60808110156118d257818310156118d2576040516328d09d4760e01b81526001600160a01b038981166004830152600284860181010460248301819052916000917f000000000000000000000000000000000000000000000000000000000000000016906328d09d4790604401608060405180830381865afa15801561188b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118af91906123bb565b9050888160400151116118c4578194506118cb565b6001820393505b5050611801565b50909695505050505050565b600062093a8062093a7f830181900402610635565b60008183116119025781610632565b5090919050565b60006001600160ff1b038211156119735760405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e2061604482015267371034b73a191a9b60c11b606482015260840161045a565b5090565b80600f81900b81146119db5760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b606482015260840161045a565b919050565b6000808212156119735760405162461bcd60e51b815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f736974697665604482015260640161045a565b60606000611a3f83611ca7565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663900cf0cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ad2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af69190612390565b60405163d1febfb960e01b8152600481018290529091506000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d1febfb990602401608060405180830381865afa158015611b61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b8591906123bb565b90506112b28185611ccf565b60008183106119025781610632565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806109895760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b604482015260640161045a565b6000611c2385611e32565b9050611c30868286611e5f565b611c505760405163e9a06b8b60e01b81526004810183905260240161045a565b42831015611c7157604051634917986360e11b815260040160405180910390fd5b6001600160a01b0386166000908152600260205260408120805460019290611c9a9084906124e9565b9091555050505050505050565b600060ff8216601f81111561063557604051632cd44ac360e21b815260040160405180910390fd5b6040820151600090839062093a809081900402825b60ff811015611e0a57611cfa62093a80836124e9565b9150600085831115611d0e57859250611d9a565b604051631c465d2160e21b8152600481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690637119748490602401602060405180830381865afa158015611d73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d9791906124fc565b90505b6040840151611da99084612517565b8460200151611db8919061253e565b84518590611dc790839061255e565b600f0b905250858303611dda5750611e0a565b8084602001818151611dec919061258b565b600f0b9052505060408301829052611e03816125b8565b9050611ce4565b5060008260000151600f0b1215611e2057600082525b50516001600160801b03169392505050565b6000610635611e3f6116c1565b8360405161190160f01b8152600281019290925260228201526042902090565b60008151604114611e8357604051634c57c87b60e11b815260040160405180910390fd5b602082810151604080850151606080870151835160008082529681018086528a905290861a938101849052908101849052608081018290529293909260019060a0016020604051602081039080840390855afa158015611ee7573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906116525750876001600160a01b0316816001600160a01b03161498975050505050505050565b6001600160a01b03811681146108d757600080fd5b600060208284031215611f4f57600080fd5b8135611f5a81611f28565b9392505050565b60008060408385031215611f7457600080fd5b8235611f7f81611f28565b946020939093013593505050565b600060208284031215611f9f57600080fd5b5035919050565b60008083601f840112611fb857600080fd5b5081356001600160401b03811115611fcf57600080fd5b6020830191508360208260051b8501011115611fea57600080fd5b9250929050565b6000806000806040858703121561200757600080fd5b84356001600160401b038082111561201e57600080fd5b61202a88838901611fa6565b9096509450602087013591508082111561204357600080fd5b5061205087828801611fa6565b95989497509550505050565b6000806040838503121561206f57600080fd5b823561207a81611f28565b9150602083013561208a81611f28565b809150509250929050565b6000815180845260005b818110156120bb5760208185018101518683018201520161209f565b506000602082860101526020601f19601f83011685010191505092915050565b600081518084526020808501945080840160005b8381101561210b578151875295820195908201906001016120ef565b509495945050505050565b60ff60f81b8816815260e06020820152600061213560e0830189612095565b82810360408401526121478189612095565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152905061217881856120db565b9a9950505050505050505050565b60008060006040848603121561219b57600080fd5b83356121a681611f28565b925060208401356001600160401b038111156121c157600080fd5b6121cd86828701611fa6565b9497909650939450505050565b60208152600061063260208301846120db565b6000806020838503121561220057600080fd5b82356001600160401b0381111561221657600080fd5b61222285828601611fa6565b90969095509350505050565b803580151581146119db57600080fd5b60006020828403121561225057600080fd5b6106328261222e565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561229757612297612259565b604052919050565b6000806000606084860312156122b457600080fd5b83356122bf81611f28565b925060206122ce85820161222e565b925060408501356001600160401b03808211156122ea57600080fd5b818701915087601f8301126122fe57600080fd5b81358181111561231057612310612259565b612322601f8201601f1916850161226f565b9150808252888482850101111561233857600080fd5b80848401858401376000848284010152508093505050509250925092565b6020808252600a90820152695245454e5452414e435960b01b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156123a257600080fd5b5051919050565b8051600f81900b81146119db57600080fd5b6000608082840312156123cd57600080fd5b604051608081018181106001600160401b03821117156123ef576123ef612259565b6040526123fb836123a9565b8152612409602084016123a9565b602082015260408301516040820152606083015160608201528091505092915050565b60208082526024908201527f46656520646973747269627574696f6e20686173206e6f742073746172746564604082015263081e595d60e21b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8181038181111561063557610635612470565b634e487b7160e01b600052601260045260246000fd5b600181811c908216806124c357607f821691505b6020821081036124e357634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561063557610635612470565b60006020828403121561250e57600080fd5b610632826123a9565b818103600083128015838313168383128216171561253757612537612470565b5092915050565b600082600f0b82600f0b0280600f0b915080821461253757612537612470565b600f82810b9082900b0360016001607f1b0319811260016001607f1b038213171561063557610635612470565b600f81810b9083900b0160016001607f1b03811360016001607f1b03198212171561063557610635612470565b6000600182016125ca576125ca612470565b506001019056fea26469706673582212209e53aef3029d82e8f564f18db73ffe53732896b692bbae51844017b611945d9864736f6c6343000813003300000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd87000000000000000000000000000000000000000000000000000000006486e6b7
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061014d5760003560e01c806388720467116100c3578063ca31879d1161007c578063ca31879d1461038b578063d3dc4ca11461039e578063de681faf146103d4578063e811f44b1461040a578063ed24911d1461041d578063fcaa54ee1461042557600080fd5b806388720467146102b257806390193b7c146102d2578063905d10ac146102fb578063a1648aa51461030e578063acbc14281461034a578063c2c4c5c11461038357600080fd5b80634f3c5090116101155780634f3c5090146102105780637b8d6221146102305780638050a7ee1461024357806382aa5ad41461025657806384b0196e1461025e578063876e69a11461027957600080fd5b806308b0308a1461015257806314866e081461018e5780632308805b146101a3578063338b5dea146101ea5780633902b9bc146101fd575b600080fd5b6040516001600160a01b037f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd871681526020015b60405180910390f35b6101a161019c366004611f3d565b610438565b005b6101dc6101b1366004611f3d565b6001600160a01b0316600090815260076020526040902054600160801b90046001600160801b031690565b604051908152602001610185565b6101a16101f8366004611f61565b610479565b6101a161020b366004611f3d565b6104d4565b6101dc61021e366004611f8d565b60009081526006602052604090205490565b6101a161023e366004611ff1565b610506565b6101dc61025136600461205c565b610626565b6005546101dc565b61026661063b565b6040516101859796959493929190612116565b6101dc610287366004611f3d565b6001600160a01b0316600090815260096020526040902054600160401b90046001600160401b031690565b6102c56102c0366004612186565b6106c3565b60405161018591906121da565b6101dc6102e0366004611f3d565b6001600160a01b031660009081526002602052604090205490565b6101a16103093660046121ed565b6107d3565b61033a61031c366004611f3d565b6001600160a01b031660009081526003602052604090205460ff1690565b6040519015158152602001610185565b6101dc610358366004611f3d565b6001600160a01b0316600090815260076020526040902054600160401b90046001600160401b031690565b6101a161082e565b6101dc61039936600461205c565b610864565b6101dc6103ac366004611f61565b6001600160a01b03919091166000908152600860209081526040808320938352929052205490565b6101dc6103e2366004611f61565b6001600160a01b03919091166000908152600a60209081526040808320938352929052205490565b6101a161041836600461223e565b6108cd565b6101dc6108da565b6101a161043336600461229f565b6108e9565b6004546001146104635760405162461bcd60e51b815260040161045a90612356565b60405180910390fd5b60026004556104718161098f565b506001600455565b60045460011461049b5760405162461bcd60e51b815260040161045a90612356565b60026004556104ab826000610e2d565b6104c06001600160a01b0383163330846111b3565b6104cb826001610e2d565b50506001600455565b6004546001146104f65760405162461bcd60e51b815260040161045a90612356565b6002600455610471816001610e2d565b6004546001146105285760405162461bcd60e51b815260040161045a90612356565b600260045582811461054d5760405163b0208d4960e01b815260040160405180910390fd5b8260005b818110156106195761058a86868381811061056e5761056e61237a565b90506020020160208101906105839190611f3d565b6000610e2d565b6105e033308686858181106105a1576105a161237a565b905060200201358989868181106105ba576105ba61237a565b90506020020160208101906105cf9190611f3d565b6001600160a01b03169291906111b3565b6106118686838181106105f5576105f561237a565b905060200201602081019061060a9190611f3d565b6001610e2d565b600101610551565b5050600160045550505050565b6000610632838361123d565b90505b92915050565b60006060808280808361066e7f4665654469737472696275746f7200000000000000000000000000000000000e836112ba565b6106997f310000000000000000000000000000000000000000000000000000000000000160016112ba565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b60606004546001146106e75760405162461bcd60e51b815260040161045a90612356565b6002600455836106f681611365565b6106fe6113af565b6107078561098f565b826000816001600160401b0381111561072257610722612259565b60405190808252806020026020018201604052801561074b578160200160208202803683370190505b50905060005b828110156107c35761076e87878381811061056e5761056e61237a565b61079e888888848181106107845761078461237a565b90506020020160208101906107999190611f3d565b611486565b8282815181106107b0576107b061237a565b6020908102919091010152600101610751565b5060016004559695505050505050565b6004546001146107f55760405162461bcd60e51b815260040161045a90612356565b60026004558060005b818110156108235761081b8484838181106105f5576105f561237a565b6001016107fe565b505060016004555050565b6004546001146108505760405162461bcd60e51b815260040161045a90612356565b600260045561085d6113af565b6001600455565b60006004546001146108885760405162461bcd60e51b815260040161045a90612356565b60026004558261089781611365565b61089f6113af565b6108a88461098f565b6108b3836000610e2d565b60006108bf8585611486565b600160045595945050505050565b6108d7338261165e565b50565b60006108e46116c1565b905090565b60007fbd291ffccec065968fe20c5f8debdad73ab50837733f357eeae8814178015a90848461092d876001600160a01b031660009081526002602052604090205490565b6040805160208101959095526001600160a01b039093169284019290925215156060830152608082015260a00160405160208183030381529060405280519060200120905061097f84828460016117ec565b610989848461165e565b50505050565b60405163010ae75760e01b81526001600160a01b0382811660048301526000917f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd879091169063010ae75790602401602060405180830381865afa1580156109fa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1e9190612390565b905080600003610a2c575050565b6001600160a01b038216600090815260096020526040812080549091600160401b9091046001600160401b031690818103610a9557610a8e857f0000000000000000000000000000000000000000000000000000000064811a006000876117fb565b9050610ad0565b428210610aa3575050505050565b508154600160801b90046001600160801b031660148185031115610ad057610acd858383876117fb565b90505b80600003610adc575060015b6040516328d09d4760e01b81526001600160a01b038681166004830152602482018390526000917f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd87909116906328d09d4790604401608060405180830381865afa158015610b4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7291906123bb565b905082600003610c0e577f0000000000000000000000000000000000000000000000000000000064811a004211610bbb5760405162461bcd60e51b815260040161045a9061242c565b610bf17f0000000000000000000000000000000000000000000000000000000064811a00610bec83604001516118de565b6118f3565b845467ffffffffffffffff19166001600160401b03821617855592505b6040805160808101825260008082526020820181905291810182905260608101829052905b6032811015610de75782604001518510158015610c505750868411155b15610d305760018401935082915086841115610c985760405180608001604052806000600f0b81526020016000600f0b81526020016000815260200160008152509250610ddf565b6040516328d09d4760e01b81526001600160a01b038981166004830152602482018690527f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd8716906328d09d4790604401608060405180830381865afa158015610d05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2991906123bb565b9250610ddf565b42851015610de7576000610d51610d4c84604001518803611909565b611977565b9050600081846020015102600f0b8460000151600f0b13610d73576000610d8b565b610d8b82856020015102856000015103600f0b6119e0565b905080158015610d9a57508886115b15610db157610da8426118de565b96505050610de7565b6001600160a01b038a166000908152600a602090815260408083208a84529091529020555062093a80909401935b600101610c33565b505083546001600160401b03938416600160401b0267ffffffffffffffff60401b19600160801b6000199095018616949094029390931693169290921717909155505050565b6001600160a01b038216600090815260076020526040812080549091600160401b9091046001600160401b031690818103610ecd5742915062093a8080830402835467ffffffffffffffff19166001600160401b03919091161783557f0000000000000000000000000000000000000000000000000000000064811a004211610ec85760405162461bcd60e51b815260040161045a9061242c565b610f1d565b814203905083610f1d57600062093a808084040262093a80428190040214905060006201518042610efd426118de565b03109050818015610f0c575080155b15610f1a5750505050505050565b50505b825467ffffffffffffffff60401b1916600160401b426001600160401b0316021783556040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015610f87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fab9190612390565b8454909150600090610fcd90600160801b90046001600160801b031683612486565b905080600003610fe05750505050505050565b6001600160801b038211156110375760405162461bcd60e51b815260206004820152601e60248201527f4d6178696d756d20746f6b656e2062616c616e63652065786365656465640000604482015260640161045a565b84546001600160801b03808416600160801b02911617855560006110618562093a80908190040290565b6001600160a01b038916600090815260086020526040812091925090815b601481101561115c578362093a80019250824210156110f457861580156110a557508742145b156110c357600084815260208390526040902080548601905561115c565b868842038602816110d6576110d6612499565b6000868152602085905260409020805492909104909101905561115c565b8615801561110157508783145b1561111f57600084815260208390526040902080548601905561114c565b8688840386028161113257611132612499565b600086815260208590526040902080549290910490910190555b919650869250829160010161107f565b50604080516001600160a01b038c168152602081018690529081018890527f9b7f1a85a4c9b4e59e1b6527d9969c50cdfb3a1a467d0c4a51fb0ed8bf07f1309060600160405180910390a150505050505050505050565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806112365760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b604482015260640161045a565b5050505050565b6001600160a01b038083166000908152600b602090815260408083209385168352929052908120548015611272579050610635565b6001600160a01b03808516600090815260096020908152604080832054938716835260079091529020546112b2916001600160401b0390811691166118f3565b949350505050565b606060ff83146112d4576112cd83611a32565b9050610635565b8180546112e0906124af565b80601f016020809104026020016040519081016040528092919081815260200182805461130c906124af565b80156113595780601f1061132e57610100808354040283529160200191611359565b820191906000526020600020905b81548152906001019060200180831161133c57829003601f168201915b50505050509050610635565b6001600160a01b03811660009081526003602052604090205460ff16156108d757336001600160a01b038216146108d75760405163f93014bb60e01b815260040160405180910390fd5b60055462093a804281900402808211806113c857504281145b156113d1575050565b7f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd876001600160a01b031663c2c4c5c16040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561142c57600080fd5b505af1158015611440573d6000803e3d6000fd5b5050505060005b601481101561147f5781831161147f5761146083611a71565b60008481526006602052604090205562093a8090920191600101611447565b5050600555565b6001600160a01b0381166000908152600760205260408120816114a9858561123d565b6005546001600160a01b03871660009081526009602052604081205492935091611517916114f0916114eb9190600160401b90046001600160401b0316611b91565b6118de565b845461151290600160401b90046001600160401b031662093a80908190040290565b611b91565b6001600160a01b038087166000908152600860209081526040808320938b168352600a9091528120929350909190805b601481101561159d578486101561159d576000868152600660209081526040808320548683528184205492889052922054028161158657611586612499565b62093a809790970196049190910190600101611547565b506001600160a01b03808a166000908152600b60209081526040808320938c1683529290522085905580156116525785546001600160801b03600160801b808304821684900382160291161786556115ff6001600160a01b0389168a83611ba0565b604080516001600160a01b03808c1682528a166020820152908101829052606081018690527fff097c7d8b1957a4ff09ef1361b5fb54dcede3941ba836d0beb9d10bec725de69060800160405180910390a15b98975050505050505050565b6001600160a01b038216600081815260036020908152604091829020805460ff19168515159081179091558251938452908301527fac9874a7a931a3f5c9f202c6d9cf40de5d21506993c9f9c38ca8265add89584c910160405180910390a15050565b6000306001600160a01b037f0000000000000000000000008787ca6a0afffd8f634d09d7c2ee8d15b326e3f61614801561171a57507f000000000000000000000000000000000000000000000000000000000000000146145b1561174457507f6619b75de76f7331a7d6a541214e0324c0b12c551ab82224c197ae2c1d28b20c90565b6108e4604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0618c188edbe06a8ffa15e11b4f74493cfd6f23aba7fab610364d908072aac99918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b61098984848460001985611c18565b60008282825b60808110156118d257818310156118d2576040516328d09d4760e01b81526001600160a01b038981166004830152600284860181010460248301819052916000917f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd8716906328d09d4790604401608060405180830381865afa15801561188b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118af91906123bb565b9050888160400151116118c4578194506118cb565b6001820393505b5050611801565b50909695505050505050565b600062093a8062093a7f830181900402610635565b60008183116119025781610632565b5090919050565b60006001600160ff1b038211156119735760405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e2061604482015267371034b73a191a9b60c11b606482015260840161045a565b5090565b80600f81900b81146119db5760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b606482015260840161045a565b919050565b6000808212156119735760405162461bcd60e51b815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f736974697665604482015260640161045a565b60606000611a3f83611ca7565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b6000807f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd876001600160a01b031663900cf0cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ad2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af69190612390565b60405163d1febfb960e01b8152600481018290529091506000906001600160a01b037f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd87169063d1febfb990602401608060405180830381865afa158015611b61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b8591906123bb565b90506112b28185611ccf565b60008183106119025781610632565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806109895760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b604482015260640161045a565b6000611c2385611e32565b9050611c30868286611e5f565b611c505760405163e9a06b8b60e01b81526004810183905260240161045a565b42831015611c7157604051634917986360e11b815260040160405180910390fd5b6001600160a01b0386166000908152600260205260408120805460019290611c9a9084906124e9565b9091555050505050505050565b600060ff8216601f81111561063557604051632cd44ac360e21b815260040160405180910390fd5b6040820151600090839062093a809081900402825b60ff811015611e0a57611cfa62093a80836124e9565b9150600085831115611d0e57859250611d9a565b604051631c465d2160e21b8152600481018490527f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd876001600160a01b031690637119748490602401602060405180830381865afa158015611d73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d9791906124fc565b90505b6040840151611da99084612517565b8460200151611db8919061253e565b84518590611dc790839061255e565b600f0b905250858303611dda5750611e0a565b8084602001818151611dec919061258b565b600f0b9052505060408301829052611e03816125b8565b9050611ce4565b5060008260000151600f0b1215611e2057600082525b50516001600160801b03169392505050565b6000610635611e3f6116c1565b8360405161190160f01b8152600281019290925260228201526042902090565b60008151604114611e8357604051634c57c87b60e11b815260040160405180910390fd5b602082810151604080850151606080870151835160008082529681018086528a905290861a938101849052908101849052608081018290529293909260019060a0016020604051602081039080840390855afa158015611ee7573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906116525750876001600160a01b0316816001600160a01b03161498975050505050505050565b6001600160a01b03811681146108d757600080fd5b600060208284031215611f4f57600080fd5b8135611f5a81611f28565b9392505050565b60008060408385031215611f7457600080fd5b8235611f7f81611f28565b946020939093013593505050565b600060208284031215611f9f57600080fd5b5035919050565b60008083601f840112611fb857600080fd5b5081356001600160401b03811115611fcf57600080fd5b6020830191508360208260051b8501011115611fea57600080fd5b9250929050565b6000806000806040858703121561200757600080fd5b84356001600160401b038082111561201e57600080fd5b61202a88838901611fa6565b9096509450602087013591508082111561204357600080fd5b5061205087828801611fa6565b95989497509550505050565b6000806040838503121561206f57600080fd5b823561207a81611f28565b9150602083013561208a81611f28565b809150509250929050565b6000815180845260005b818110156120bb5760208185018101518683018201520161209f565b506000602082860101526020601f19601f83011685010191505092915050565b600081518084526020808501945080840160005b8381101561210b578151875295820195908201906001016120ef565b509495945050505050565b60ff60f81b8816815260e06020820152600061213560e0830189612095565b82810360408401526121478189612095565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152905061217881856120db565b9a9950505050505050505050565b60008060006040848603121561219b57600080fd5b83356121a681611f28565b925060208401356001600160401b038111156121c157600080fd5b6121cd86828701611fa6565b9497909650939450505050565b60208152600061063260208301846120db565b6000806020838503121561220057600080fd5b82356001600160401b0381111561221657600080fd5b61222285828601611fa6565b90969095509350505050565b803580151581146119db57600080fd5b60006020828403121561225057600080fd5b6106328261222e565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561229757612297612259565b604052919050565b6000806000606084860312156122b457600080fd5b83356122bf81611f28565b925060206122ce85820161222e565b925060408501356001600160401b03808211156122ea57600080fd5b818701915087601f8301126122fe57600080fd5b81358181111561231057612310612259565b612322601f8201601f1916850161226f565b9150808252888482850101111561233857600080fd5b80848401858401376000848284010152508093505050509250925092565b6020808252600a90820152695245454e5452414e435960b01b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156123a257600080fd5b5051919050565b8051600f81900b81146119db57600080fd5b6000608082840312156123cd57600080fd5b604051608081018181106001600160401b03821117156123ef576123ef612259565b6040526123fb836123a9565b8152612409602084016123a9565b602082015260408301516040820152606083015160608201528091505092915050565b60208082526024908201527f46656520646973747269627574696f6e20686173206e6f742073746172746564604082015263081e595d60e21b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8181038181111561063557610635612470565b634e487b7160e01b600052601260045260246000fd5b600181811c908216806124c357607f821691505b6020821081036124e357634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561063557610635612470565b60006020828403121561250e57600080fd5b610632826123a9565b818103600083128015838313168383128216171561253757612537612470565b5092915050565b600082600f0b82600f0b0280600f0b915080821461253757612537612470565b600f82810b9082900b0360016001607f1b0319811260016001607f1b038213171561063557610635612470565b600f81810b9083900b0160016001607f1b03811360016001607f1b03198212171561063557610635612470565b6000600182016125ca576125ca612470565b506001019056fea26469706673582212209e53aef3029d82e8f564f18db73ffe53732896b692bbae51844017b611945d9864736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd87000000000000000000000000000000000000000000000000000000006486e6b7
-----Decoded View---------------
Arg [0] : votingEscrow (address): 0x28901CF869D94C9d892fBd86c8e57b801E8FDd87
Arg [1] : startTime (uint256): 1686562487
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd87
Arg [1] : 000000000000000000000000000000000000000000000000000000006486e6b7
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.