Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
RewardsDistributorV2
Compiler Version
v0.8.13+commit.abaa5c0e
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.13;
import "../libraries/Math.sol";
import "../interfaces/IERC20.sol";
import "../interfaces/IRewardsDistributor.sol";
import "../interfaces/IVersionable.sol";
import {IVotingEscrowV2} from "./VotingEscrow/IVotingEscrowV2.sol";
import {Checkpoints} from "./VotingEscrow/libraries/Checkpoints.sol";
import "../Constants.sol";
/**
@title RewardsDistributor for ve(3,3) emissions
@author RewardsDistributor V2 ApeGuru
@author RewardsDistributor V1 Curve Finance, andrecronje
@dev
- WARN If a user burns an expired VotingEscrowV2 NFT before claiming rewards, they will lose the unclaimed rewards.
*/
contract RewardsDistributorV2 is IRewardsDistributor, IVersionable {
event CheckpointToken(uint time, uint tokens);
event Claimed(uint tokenId, uint amount, uint claim_epoch, uint max_epoch);
string public constant override VERSION = "2.1.0";
uint immutable WEEK = Constants.EPOCH;
uint public start_time;
uint public time_cursor;
mapping(uint => uint) public time_cursor_of;
uint public last_token_time;
uint[1000000000000000] public tokens_per_week;
uint public token_last_balance;
address public owner;
address public immutable voting_escrow;
address public immutable token;
address public depositor;
constructor(address _voting_escrow) {
uint _t = (block.timestamp / WEEK) * WEEK;
start_time = _t;
last_token_time = _t;
time_cursor = _t;
address _token = address(IVotingEscrowV2(_voting_escrow).token());
token = _token;
voting_escrow = _voting_escrow;
depositor = msg.sender;
owner = msg.sender;
require(IERC20(_token).approve(_voting_escrow, type(uint).max));
}
function timestamp() public view returns (uint) {
return (block.timestamp / WEEK) * WEEK;
}
function _checkpoint_token() internal {
uint token_balance = IERC20(token).balanceOf(address(this));
uint to_distribute = token_balance - token_last_balance;
token_last_balance = token_balance;
uint t = last_token_time;
uint since_last = block.timestamp - t;
last_token_time = block.timestamp;
uint this_week = (t / WEEK) * WEEK;
uint next_week = 0;
for (uint i = 0; i < 20; i++) {
next_week = this_week + WEEK;
if (block.timestamp < next_week) {
if (since_last == 0 && block.timestamp == t) {
tokens_per_week[this_week] += to_distribute;
} else {
tokens_per_week[this_week] += (to_distribute * (block.timestamp - t)) / since_last;
}
break;
} else {
if (since_last == 0 && next_week == t) {
tokens_per_week[this_week] += to_distribute;
} else {
tokens_per_week[this_week] += (to_distribute * (next_week - t)) / since_last;
}
}
t = next_week;
this_week = next_week;
}
emit CheckpointToken(block.timestamp, to_distribute);
}
function checkpoint_token() external {
assert(msg.sender == depositor);
_checkpoint_token();
}
function _checkpoint_total_supply() internal {
IVotingEscrowV2(voting_escrow).checkpoint();
if (block.timestamp >= time_cursor) time_cursor = timestamp() + WEEK;
}
function checkpoint_total_supply() external {
_checkpoint_total_supply();
}
function _claim(uint _tokenId, address ve, uint _last_token_time) internal returns (uint) {
uint to_distribute = 0;
(, uint48 maxTs) = IVotingEscrowV2(ve).getPastEscrowPoint(_tokenId, block.timestamp);
uint _start_time = start_time;
if (maxTs == 0) return 0;
uint week_cursor = time_cursor_of[_tokenId];
if (week_cursor == 0) {
(, uint48 ts) = IVotingEscrowV2(ve).getFirstEscrowPoint(_tokenId);
week_cursor = ((ts + WEEK - 1) / WEEK) * WEEK;
}
if (week_cursor >= last_token_time) return 0;
if (week_cursor < _start_time) week_cursor = _start_time;
for (uint i = 0; i < 50; i++) {
if (week_cursor >= _last_token_time) break;
uint256 balance_of = IVotingEscrowV2(ve).balanceOfNFTAt(_tokenId, week_cursor);
if (balance_of == 0) continue;
if (balance_of != 0) {
/// @note potential brick if supply returns 0
to_distribute +=
(balance_of * tokens_per_week[week_cursor]) /
IVotingEscrowV2(ve).getPastTotalSupply(week_cursor);
}
week_cursor += WEEK;
}
time_cursor_of[_tokenId] = week_cursor;
emit Claimed(_tokenId, to_distribute, week_cursor, maxTs);
return to_distribute;
}
function _claimable(uint _tokenId, address ve, uint _last_token_time) internal view returns (uint) {
uint to_distribute = 0;
uint _start_time = start_time;
uint week_cursor = time_cursor_of[_tokenId];
if (week_cursor == 0) {
(, uint48 ts) = IVotingEscrowV2(ve).getFirstEscrowPoint(_tokenId);
if (ts == 0) return 0;
week_cursor = ((ts + WEEK - 1) / WEEK) * WEEK;
}
if (week_cursor >= last_token_time) return 0;
if (week_cursor < _start_time) week_cursor = _start_time;
for (uint i = 0; i < 50; i++) {
if (week_cursor >= _last_token_time) break;
uint256 balance_of = IVotingEscrowV2(ve).balanceOfNFTAt(_tokenId, week_cursor);
if (balance_of == 0) break;
if (balance_of != 0) {
/// @note potential brick if supply returns 0
to_distribute +=
(balance_of * tokens_per_week[week_cursor]) /
uint256(int256(IVotingEscrowV2(ve).getPastTotalSupply(week_cursor)));
}
week_cursor += WEEK;
}
return to_distribute;
}
function claimable(uint _tokenId) external view returns (uint) {
uint _last_token_time = (last_token_time / WEEK) * WEEK;
return _claimable(_tokenId, voting_escrow, _last_token_time);
}
/// @notice Get the claimable rewards for a token at a specific timestamp
/// @param _tokenId The token ID to check
/// @param _timestamp The timestamp to query rewards at
/// @return The claimable reward amount at the specified timestamp
function claimableAt(uint _tokenId, uint _timestamp) external view returns (uint) {
uint _last_token_time = (_timestamp / WEEK) * WEEK;
return _claimable(_tokenId, voting_escrow, _last_token_time);
}
function claim(uint _tokenId) external returns (uint) {
if (block.timestamp >= time_cursor) _checkpoint_total_supply();
uint _last_token_time = last_token_time;
_last_token_time = (_last_token_time / WEEK) * WEEK;
uint amount = _claim(_tokenId, voting_escrow, _last_token_time);
if (amount != 0) {
// if locked.end then send directly
IVotingEscrowV2.LockDetails memory _locked = IVotingEscrowV2(voting_escrow).lockDetails(_tokenId);
if (_locked.endTime <= block.timestamp && !_locked.isPermanent) {
address _nftOwner = IVotingEscrowV2(voting_escrow).ownerOf(_tokenId);
IERC20(token).transfer(_nftOwner, amount);
} else {
IVotingEscrowV2(voting_escrow).increaseAmount(_tokenId, amount);
}
token_last_balance -= amount;
}
return amount;
}
function claim_many(uint[] memory _tokenIds) external returns (bool) {
if (block.timestamp >= time_cursor) _checkpoint_total_supply();
uint _last_token_time = last_token_time;
_last_token_time = (_last_token_time / WEEK) * WEEK;
address _voting_escrow = voting_escrow;
uint total = 0;
for (uint i = 0; i < _tokenIds.length; i++) {
uint _tokenId = _tokenIds[i];
if (_tokenId == 0) break;
uint amount = _claim(_tokenId, _voting_escrow, _last_token_time);
if (amount != 0) {
// if locked.end then send directly
IVotingEscrowV2.LockDetails memory _locked = IVotingEscrowV2(_voting_escrow).lockDetails(_tokenId);
if (_locked.endTime < block.timestamp && !_locked.isPermanent) {
address _nftOwner = IVotingEscrowV2(_voting_escrow).ownerOf(_tokenId);
IERC20(token).transfer(_nftOwner, amount);
} else {
IVotingEscrowV2(_voting_escrow).increaseAmount(_tokenId, amount);
}
total += amount;
}
}
if (total != 0) {
token_last_balance -= total;
}
return true;
}
function setDepositor(address _depositor) external {
require(msg.sender == owner);
depositor = _depositor;
}
function setOwner(address _owner) external {
require(msg.sender == owner);
owner = _owner;
}
function withdrawERC20(address _token) external {
require(msg.sender == owner);
require(_token != address(0));
uint256 _balance = IERC20(_token).balanceOf(address(this));
IERC20(_token).transfer(msg.sender, _balance);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.0;
import "../IERC721Upgradeable.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721EnumerableUpgradeable is IERC721Upgradeable {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165Upgradeable.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721Upgradeable is IERC165Upgradeable {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165Upgradeable {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// 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
pragma solidity 0.8.13;
library Constants {
// FIXME: Temporary for testing
// uint48 constant EPOCH = 1 weeks;
uint48 constant EPOCH = 30 minutes;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IERC20 {
function totalSupply() external view returns (uint256);
function transfer(address recipient, uint amount) external returns (bool);
function decimals() external view returns (uint8);
function symbol() external view returns (string memory);
function balanceOf(address) external view returns (uint);
function transferFrom(address sender, address recipient, uint amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
interface IRewardsDistributor {
function checkpoint_token() external;
function voting_escrow() external view returns(address);
function checkpoint_total_supply() external;
function claimable(uint _tokenId) external view returns (uint);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IVersionable {
function VERSION() external view returns (string memory);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
library Math {
function max(uint a, uint b) internal pure returns (uint) {
return a >= b ? a : b;
}
function min(uint a, uint b) internal pure returns (uint) {
return a < b ? a : b;
}
function sqrt(uint y) internal pure returns (uint z) {
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
function cbrt(uint256 n) internal pure returns (uint256) { unchecked {
uint256 x = 0;
for (uint256 y = 1 << 255; y > 0; y >>= 3) {
x <<= 1;
uint256 z = 3 * x * (x + 1) + 1;
if (n / y >= z) {
n -= y * z;
x += 1;
}
}
return x;
}}
function sub(uint x, uint y) internal pure returns (uint z) {
require((z = x - y) <= x, 'Math: Sub-underflow');
}
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);
}
}
}// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
/**
* @title Non-Fungible Vesting Token Standard.
* @notice A non-fungible token standard used to vest ERC-20 tokens over a vesting release curve
* scheduled using timestamps.
* @dev Because this standard relies on timestamps for the vesting schedule, it's important to keep track of the
* tokens claimed per Vesting NFT so that a user cannot withdraw more tokens than allotted for a specific Vesting NFT.
* @custom:interface-id 0xbd3a202b
*/
interface IERC5725Upgradeable is IERC721Upgradeable {
/**
* This event is emitted when the payout is claimed through the claim function.
* @param tokenId the NFT tokenId of the assets being claimed.
* @param recipient The address which is receiving the payout.
* @param claimAmount The amount of tokens being claimed.
*/
event PayoutClaimed(uint256 indexed tokenId, address indexed recipient, uint256 claimAmount);
/**
* This event is emitted when an `owner` sets an address to manage token claims for all tokens.
* @param owner The address setting a manager to manage all tokens.
* @param spender The address being permitted to manage all tokens.
* @param approved A boolean indicating whether the spender is approved to claim for all tokens.
*/
event ClaimApprovalForAll(address indexed owner, address indexed spender, bool approved);
/**
* This event is emitted when an `owner` sets an address to manage token claims for a `tokenId`.
* @param owner The `owner` of `tokenId`.
* @param spender The address being permitted to manage a tokenId.
* @param tokenId The unique identifier of the token being managed.
* @param approved A boolean indicating whether the spender is approved to claim for `tokenId`.
*/
event ClaimApproval(address indexed owner, address indexed spender, uint256 indexed tokenId, bool approved);
/**
* @notice Claim the pending payout for the NFT.
* @dev MUST grant the claimablePayout value at the time of claim being called to `msg.sender`.
* MUST revert if not called by the token owner or approved users.
* MUST emit PayoutClaimed.
* SHOULD revert if there is nothing to claim.
* @param tokenId The NFT token id.
*/
function claim(uint256 tokenId) external;
/**
* @notice Number of tokens for the NFT which have been claimed at the current timestamp.
* @param tokenId The NFT token id.
* @return payout The total amount of payout tokens claimed for this NFT.
*/
function claimedPayout(uint256 tokenId) external view returns (uint256 payout);
/**
* @notice Number of tokens for the NFT which can be claimed at the current timestamp.
* @dev It is RECOMMENDED that this is calculated as the `vestedPayout()` subtracted from `payoutClaimed()`.
* @param tokenId The NFT token id.
* @return payout The amount of unlocked payout tokens for the NFT which have not yet been claimed.
*/
function claimablePayout(uint256 tokenId) external view returns (uint256 payout);
/**
* @notice Total amount of tokens which have been vested at the current timestamp.
* This number also includes vested tokens which have been claimed.
* @dev It is RECOMMENDED that this function calls `vestedPayoutAtTime`
* with `block.timestamp` as the `timestamp` parameter.
* @param tokenId The NFT token id.
* @return payout Total amount of tokens which have been vested at the current timestamp.
*/
function vestedPayout(uint256 tokenId) external view returns (uint256 payout);
/**
* @notice Total amount of vested tokens at the provided timestamp.
* This number also includes vested tokens which have been claimed.
* @dev `timestamp` MAY be both in the future and in the past.
* Zero MUST be returned if the timestamp is before the token was minted.
* @param tokenId The NFT token id.
* @param timestamp The timestamp to check on, can be both in the past and the future.
* @return payout Total amount of tokens which have been vested at the provided timestamp.
*/
function vestedPayoutAtTime(uint256 tokenId, uint256 timestamp) external view returns (uint256 payout);
/**
* @notice Number of tokens for an NFT which are currently vesting.
* @dev The sum of vestedPayout and vestingPayout SHOULD always be the total payout.
* @param tokenId The NFT token id.
* @return payout The number of tokens for the NFT which are vesting until a future date.
*/
function vestingPayout(uint256 tokenId) external view returns (uint256 payout);
/**
* @notice The start and end timestamps for the vesting of the provided NFT.
* MUST return the timestamp where no further increase in vestedPayout occurs for `vestingEnd`.
* @param tokenId The NFT token id.
* @return vestingStart The beginning of the vesting as a unix timestamp.
* @return vestingEnd The ending of the vesting as a unix timestamp.
*/
function vestingPeriod(uint256 tokenId) external view returns (uint256 vestingStart, uint256 vestingEnd);
/**
* @notice Token which is used to pay out the vesting claims.
* @param tokenId The NFT token id.
* @return token The token which is used to pay out the vesting claims.
*/
function payoutToken(uint256 tokenId) external view returns (address token);
/**
* @notice Sets a global `operator` with permission to manage all tokens owned by the current `msg.sender`.
* @param operator The address to let manage all tokens.
* @param approved A boolean indicating whether the spender is approved to claim for all tokens.
*/
function setClaimApprovalForAll(address operator, bool approved) external;
/**
* @notice Sets a tokenId `operator` with permission to manage a single `tokenId` owned by the `msg.sender`.
* @param operator The address to let manage a single `tokenId`.
* @param tokenId the `tokenId` to be managed.
* @param approved A boolean indicating whether the spender is approved to claim for all tokens.
*/
function setClaimApproval(address operator, bool approved, uint256 tokenId) external;
/**
* @notice Returns true if `owner` has set `operator` to manage all `tokenId`s.
* @param owner The owner allowing `operator` to manage all `tokenId`s.
* @param operator The address who is given permission to spend tokens on behalf of the `owner`.
*/
function isClaimApprovedForAll(address owner, address operator) external view returns (bool isClaimApproved);
/**
* @notice Returns the operating address for a `tokenId`.
* If `tokenId` is not managed, then returns the zero address.
* @param tokenId The NFT `tokenId` to query for a `tokenId` manager.
*/
function getClaimApproved(uint256 tokenId) external view returns (address operator);
}
interface IERC5725_ExtendedApproval is IERC5725Upgradeable {
/**
* @notice Returns true if `operator` is allowed to transfer the `tokenId` NFT.
* @param operator The address to check if it is approved for the transfer
* @param tokenId The token id to check if the operator is approved for
*/
function isApprovedOrOwner(address operator, uint tokenId) external view returns (bool);
/**
* @notice Returns true if `operator` is allowed to claim for the provided tokenId
* @param operator The address to check if it is approved for the claim or owner of the token
* @param tokenId The token id to check if the operator is approved for
*/
function isApprovedClaimOrOwner(address operator, uint256 tokenId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (governance/utils/IVotes.sol)
pragma solidity ^0.8.0;
/**
* @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
*
* _Available since v4.5._
*/
interface IVotes {
/**
* @dev Emitted when an account changes their delegate.
*/
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
/**
* @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
*/
event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);
/**
* @dev Returns the current amount of votes that `account` has.
*/
function getVotes(address account) external view returns (uint256);
/**
* @dev Returns the amount of votes that `account` had at a specific moment in the past. If the `clock()` is
* configured to use block numbers, this will return the value at the end of the corresponding block.
*/
function getPastVotes(address account, uint256 timepoint) external view returns (uint256);
/**
* @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is
* configured to use block numbers, this will return the value at the end of the corresponding block.
*
* NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
* Votes that have not been delegated are still part of total supply, even though they would not participate in a
* vote.
*/
function getPastTotalSupply(uint256 timepoint) external view returns (uint256);
/**
* @dev Returns the delegate that `account` has chosen.
*/
function delegates(address account) external view returns (address);
/**
* @dev Delegates votes from the sender to `delegatee`.
*/
function delegate(address delegatee) external;
/**
* @notice Removed from the interface to avoid signature conflicts.
* @dev Delegates votes from signer to `delegatee`.
*/
// function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {IVotes} from "./interfaces/IVotes.sol";
import {Checkpoints} from "./libraries/Checkpoints.sol";
import {IERC5725_ExtendedApproval} from "./erc5725/IERC5725Upgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {IERC721EnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol";
import {IVersionable} from "../../interfaces/IVersionable.sol";
/**
* @title Voting Escrow V2 Interface for Upgrades
*/
interface IVotingEscrowV2 is IVotes, IERC5725_ExtendedApproval, IERC721EnumerableUpgradeable, IVersionable {
struct LockDetails {
uint256 amount; /// @dev amount of tokens locked
uint256 startTime; /// @dev when locking started
uint256 endTime; /// @dev when locking ends
bool isPermanent; /// @dev if its a permanent lock
}
/// -----------------------------------------------------------------------
/// Events
/// -----------------------------------------------------------------------
event SupplyUpdated(uint256 oldSupply, uint256 newSupply);
/// @notice Lock events
event LockCreated(uint256 indexed tokenId, address indexed to, uint256 value, uint256 unlockTime, bool isPermanent);
event LockUpdated(uint256 indexed tokenId, uint256 value, uint256 unlockTime, bool isPermanent);
event LockMerged(
uint256 indexed fromTokenId,
uint256 indexed toTokenId,
uint256 totalValue,
uint256 unlockTime,
bool isPermanent
);
event LockSplit(uint256[] splitWeights, uint256 indexed _tokenId);
event LockDurationExtended(uint256 indexed tokenId, uint256 newUnlockTime, bool isPermanent);
event LockAmountIncreased(uint256 indexed tokenId, uint256 value);
event UnlockPermanent(uint256 indexed tokenId, address indexed sender, uint256 unlockTime);
/// @notice Delegate events
event LockDelegateChanged(
uint256 indexed tokenId,
address indexed delegator,
address fromDelegate,
address indexed toDelegate
);
/// -----------------------------------------------------------------------
/// Errors
/// -----------------------------------------------------------------------
error AlreadyVoted();
error InvalidNonce();
error InvalidDelegatee();
error InvalidSignature();
error InvalidSignatureS();
error InvalidWeights();
error LockDurationNotInFuture();
error LockDurationTooLong();
error LockExpired();
error LockNotExpired();
error LockHoldsValue();
error LockModifiedDelay();
error NotPermanentLock();
error PermanentLock();
error PermanentLockMismatch();
error SameNFT();
error SignatureExpired();
error ZeroAmount();
error NotLockOwner();
function supply() external view returns (uint);
function token() external view returns (IERC20Upgradeable);
function totalNftsMinted() external view returns (uint256);
function balanceOfNFT(uint256 _tokenId) external view returns (uint256);
function balanceOfNFTAt(uint256 _tokenId, uint256 _timestamp) external view returns (uint256);
function delegates(uint256 tokenId, uint48 timestamp) external view returns (address);
function lockDetails(uint256 tokenId) external view returns (LockDetails calldata);
function getPastEscrowPoint(
uint256 _tokenId,
uint256 _timePoint
) external view returns (Checkpoints.Point memory, uint48);
function getFirstEscrowPoint(uint256 _tokenId) external view returns (Checkpoints.Point memory, uint48);
function checkpoint() external;
function increaseAmount(uint256 _tokenId, uint256 _value) external;
function createLockFor(
uint256 _value,
uint256 _lockDuration,
address _to,
bool _permanent
) external returns (uint256);
function createDelegatedLockFor(
uint256 _value,
uint256 _lockDuration,
address _to,
address _delegatee,
bool _permanent
) external returns (uint256);
function split(uint256[] memory _weights, uint256 _tokenId) external;
function merge(uint256 _from, uint256 _to) external;
function burn(uint256 _tokenId) external;
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// This file was derived from OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/Checkpoints.sol)
pragma solidity 0.8.13;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
/**
* @dev This library defines the `Trace*` struct, for checkpointing values as they change at different points in
* time, and later looking up past values by block number. See {Votes} as an example.
*
* To create a history of checkpoints define a variable type `Checkpoints.Trace*` in your contract, and store a new
* checkpoint for the current transaction block using the {push} function.
*/
library Checkpoints {
struct Trace {
Checkpoint[] _checkpoints;
}
/**
* @dev Struct to keep track of the voting power over time.
*/
struct Point {
/// @dev The voting power at a specific time
/// - MUST never be negative.
int128 bias;
/// @dev The rate at which the voting power decreases over time.
int128 slope;
/// @dev The value of tokens which do not decrease over time, representing permanent voting power
/// - MUST never be negative.
int128 permanent;
}
struct Checkpoint {
uint48 _key;
Point _value;
}
/**
* @dev A value was attempted to be inserted on a past checkpoint.
*/
error CheckpointUnorderedInsertions();
/**
* @dev Pushes a (`key`, `value`) pair into a Trace so that it is stored as the checkpoint.
*
* Returns previous value and new value.
*
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint48).max` key set will disable the
* library.
*/
function push(Trace storage self, uint48 key, Point memory value) internal returns (Point memory, Point memory) {
return _insert(self._checkpoints, key, value);
}
/**
* @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
* there is none.
*/
function lowerLookup(Trace storage self, uint48 key) internal view returns (Point memory) {
uint256 len = self._checkpoints.length;
uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return pos == len ? blankPoint() : _unsafeAccess(self._checkpoints, pos)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*/
function upperLookup(
Trace storage self,
uint48 key
) internal view returns (bool exists, uint48 _key, Point memory _value) {
uint256 len = self._checkpoints.length;
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
exists = pos != 0;
_value = exists ? _unsafeAccess(self._checkpoints, pos - 1)._value : blankPoint();
_key = exists ? _unsafeAccess(self._checkpoints, pos - 1)._key : 0;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*
* NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
* keys).
*/
function upperLookupRecent(
Trace storage self,
uint48 key
) internal view returns (bool exists, uint48 _key, Point memory _value) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
exists = pos != 0;
_value = exists ? _unsafeAccess(self._checkpoints, pos - 1)._value : blankPoint();
_key = exists ? _unsafeAccess(self._checkpoints, pos - 1)._key : 0;
}
/**
* @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
*/
function latest(Trace storage self) internal view returns (Point memory) {
uint256 pos = self._checkpoints.length;
return pos == 0 ? blankPoint() : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
* in the most recent checkpoint.
*/
function latestCheckpoint(
Trace storage self
) internal view returns (bool exists, uint48 _key, Point memory _value) {
uint256 pos = self._checkpoints.length;
if (pos == 0) {
return (false, 0, blankPoint());
} else {
Checkpoint memory ckpt = _unsafeAccess(self._checkpoints, pos - 1);
return (true, ckpt._key, ckpt._value);
}
}
/**
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
* in the most recent checkpoint.
*/
function firstCheckpoint(
Trace storage self
) internal view returns (bool exists, uint48 _key, Point memory _value) {
uint256 pos = self._checkpoints.length;
if (pos == 0) {
return (false, 0, blankPoint());
} else {
Checkpoint memory ckpt = _unsafeAccess(self._checkpoints, 0);
return (true, ckpt._key, ckpt._value);
}
}
/**
* @dev Returns the number of checkpoint.
*/
function length(Trace storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
/**
* @dev Returns checkpoint at given position.
*/
function at(Trace storage self, uint48 pos) internal view returns (Checkpoint memory) {
return self._checkpoints[pos];
}
/**
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
* or by updating the last one.
*/
function _insert(
Checkpoint[] storage self,
uint48 key,
Point memory value
) private returns (Point memory, Point memory) {
uint256 pos = self.length;
if (pos > 0) {
// Copying to memory is important here.
Checkpoint memory last = _unsafeAccess(self, pos - 1);
// Checkpoint keys must be non-decreasing.
if (last._key > key) {
revert CheckpointUnorderedInsertions();
}
// Update or push new checkpoint
if (last._key == key) {
_unsafeAccess(self, pos - 1)._value = value;
} else {
self.push(Checkpoint({_key: key, _value: value}));
}
return (last._value, value);
} else {
self.push(Checkpoint({_key: key, _value: value}));
return (blankPoint(), value);
}
}
/**
* @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _upperBinaryLookup(
Checkpoint[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
/**
* @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or
* `high` if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and
* exclusive `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _lowerBinaryLookup(
Checkpoint[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
/**
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
*/
function _unsafeAccess(Checkpoint[] storage self, uint256 pos) private view returns (Checkpoint storage result) {
return self[pos];
}
/**
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
*/
function _realUnsafeAccess(
Checkpoint[] storage self,
uint256 pos
) private pure returns (Checkpoint storage result) {
assembly {
mstore(0, self.slot)
result.slot := add(keccak256(0, 0x20), pos)
}
}
function blankPoint() internal pure returns (Point memory) {
return Point({bias: 0, slope: 0, permanent: 0});
}
struct TraceAddress {
CheckpointAddress[] _checkpoints;
}
struct CheckpointAddress {
uint48 _key;
address _value;
}
/**
* @dev Pushes a (`key`, `value`) pair into a TraceAddress so that it is stored as the checkpoint.
*
* Returns previous value and new value.
*
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint48).max` key set will disable the
* library.
*/
function push(TraceAddress storage self, uint48 key, address value) internal returns (address, address) {
return _insert(self._checkpoints, key, value);
}
/**
* @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
* there is none.
*/
function lowerLookup(TraceAddress storage self, uint48 key) internal view returns (address) {
uint256 len = self._checkpoints.length;
uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return pos == len ? address(0) : _unsafeAccess(self._checkpoints, pos)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*/
function upperLookup(TraceAddress storage self, uint48 key) internal view returns (address) {
uint256 len = self._checkpoints.length;
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
return pos == 0 ? address(0) : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*
* NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
* keys).
*/
function upperLookupRecent(TraceAddress storage self, uint48 key) internal view returns (address) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
return pos == 0 ? address(0) : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
*/
function latest(TraceAddress storage self) internal view returns (address) {
uint256 pos = self._checkpoints.length;
return pos == 0 ? address(0) : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
* in the most recent checkpoint.
*/
function latestCheckpoint(
TraceAddress storage self
) internal view returns (bool exists, uint48 _key, address _value) {
uint256 pos = self._checkpoints.length;
if (pos == 0) {
return (false, 0, address(0));
} else {
CheckpointAddress memory ckpt = _unsafeAccess(self._checkpoints, pos - 1);
return (true, ckpt._key, ckpt._value);
}
}
/**
* @dev Returns the number of checkpoint.
*/
function length(TraceAddress storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
/**
* @dev Returns checkpoint at given position.
*/
function at(TraceAddress storage self, uint48 pos) internal view returns (CheckpointAddress memory) {
return self._checkpoints[pos];
}
/**
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
* or by updating the last one.
*/
function _insert(CheckpointAddress[] storage self, uint48 key, address value) private returns (address, address) {
uint256 pos = self.length;
if (pos > 0) {
// Copying to memory is important here.
CheckpointAddress memory last = _unsafeAccess(self, pos - 1);
// Checkpoint keys must be non-decreasing.
if (last._key > key) {
revert CheckpointUnorderedInsertions();
}
// Update or push new checkpoint
if (last._key == key) {
_unsafeAccess(self, pos - 1)._value = value;
} else {
self.push(CheckpointAddress({_key: key, _value: value}));
}
return (last._value, value);
} else {
self.push(CheckpointAddress({_key: key, _value: value}));
return (address(0), value);
}
}
/**
* @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _upperBinaryLookup(
CheckpointAddress[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
/**
* @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or
* `high` if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and
* exclusive `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _lowerBinaryLookup(
CheckpointAddress[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
/**
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
*/
function _unsafeAccess(
CheckpointAddress[] storage self,
uint256 pos
) private pure returns (CheckpointAddress storage result) {
assembly {
mstore(0, self.slot)
result.slot := add(keccak256(0, 0x20), pos)
}
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_voting_escrow","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokens","type":"uint256"}],"name":"CheckpointToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"claim_epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"max_epoch","type":"uint256"}],"name":"Claimed","type":"event"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpoint_token","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkpoint_total_supply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"claim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"}],"name":"claim_many","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"claimable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"claimableAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"depositor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"last_token_time","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_depositor","type":"address"}],"name":"setDepositor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"start_time","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"time_cursor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"time_cursor_of","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"timestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token_last_balance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokens_per_week","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"voting_escrow","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"withdrawERC20","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60e06040526107086080523480156200001757600080fd5b5060405162001d3738038062001d378339810160408190526200003a91620001b8565b6080516000906200004c8142620001df565b62000058919062000202565b90508060008190555080600381905550806001819055506000826001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000b0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000d69190620001b8565b6001600160a01b0381811660c081905290851660a081905266038d7ea4c680068054336001600160a01b0319918216811790925566038d7ea4c680058054909116909117905560405163095ea7b360e01b8152600481019190915260001960248201529192509063095ea7b3906044016020604051808303816000875af115801562000166573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200018c919062000230565b6200019657600080fd5b50505062000254565b6001600160a01b0381168114620001b557600080fd5b50565b600060208284031215620001cb57600080fd5b8151620001d8816200019f565b9392505050565b600082620001fd57634e487b7160e01b600052601260045260246000fd5b500490565b60008160001904831182151516156200022b57634e487b7160e01b600052601160045260246000fd5b500290565b6000602082840312156200024357600080fd5b81518015158114620001d857600080fd5b60805160a05160c051611a1262000325600039600081816102d1015281816105420152818161084d0152611349015260008181610271015281816103b8015281816106c201528181610708015281816107b1015281816108e0015281816109ab01528181610aaa0152610c47015260008181610381015281816106850152818161097001528181610a0e01528181610a4b01528181610a6c01528181610cc301528181610e1b01528181610fdb0152818161112d015281816112ea015281816113fb01526114420152611a126000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c80638da5cb5b116100b8578063dfe050311161007c578063dfe050311461026c578063edf5999714610293578063f2c098b7146102a6578063f4f3b200146102b9578063fc0c546a146102cc578063ffa1ad74146102f357600080fd5b80638da5cb5b146101ff578063b21ed50214610230578063b80777ea14610238578063c7c4ff4614610240578063d1d58b251461025957600080fd5b8063486d25fe116100ff578063486d25fe146101b257806367503cc3146101d25780637f58e8f8146101e5578063811a40fe146101ee578063834ee417146101f657600080fd5b8063127dcbd31461013c57806313af4035146101585780631f1db0431461016d57806322b04bfc14610190578063379607f51461019f575b600080fd5b61014560015481565b6040519081526020015b60405180910390f35b61016b6101663660046115f8565b610324565b005b61018061017b36600461165c565b610369565b604051901515815260200161014f565b61014566038d7ea4c680045481565b6101456101ad366004611702565b61066d565b6101456101c0366004611702565b60026020526000908152604090205481565b6101456101e036600461171b565b61096b565b61014560035481565b61016b6109d8565b61014560005481565b66038d7ea4c6800554610218906001600160a01b031681565b6040516001600160a01b03909116815260200161014f565b61016b610a02565b610145610a0a565b66038d7ea4c6800654610218906001600160a01b031681565b610145610267366004611702565b610a46565b6102187f000000000000000000000000000000000000000000000000000000000000000081565b6101456102a1366004611702565b610acf565b61016b6102b43660046115f8565b610aec565b61016b6102c73660046115f8565b610b31565b6102187f000000000000000000000000000000000000000000000000000000000000000081565b610317604051806040016040528060058152602001640322e312e360dc1b81525081565b60405161014f919061173d565b66038d7ea4c68005546001600160a01b0316331461034157600080fd5b66038d7ea4c6800580546001600160a01b0319166001600160a01b0392909216919091179055565b6000600154421061037c5761037c610c45565b6003547f00000000000000000000000000000000000000000000000000000000000000006103aa81836117a8565b6103b491906117ca565b90507f00000000000000000000000000000000000000000000000000000000000000006000805b855181101561063d5760008682815181106103f8576103f86117e9565b6020026020010151905080600003610410575061063d565b600061041d828688610cf9565b9050801561062857604051636318523760e01b8152600481018390526000906001600160a01b03871690636318523790602401608060405180830381865afa15801561046d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104919190611814565b90504281604001511080156104a857508060600151155b156105b8576040516331a9108f60e11b8152600481018490526000906001600160a01b03881690636352211e90602401602060405180830381865afa1580156104f5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610519919061187e565b60405163a9059cbb60e01b81526001600160a01b038083166004830152602482018690529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb906044016020604051808303816000875af115801561058d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b1919061189b565b505061061a565b60405163b2383e5560e01b815260048101849052602481018390526001600160a01b0387169063b2383e5590604401600060405180830381600087803b15801561060157600080fd5b505af1158015610615573d6000803e3d6000fd5b505050505b61062482866118b6565b9450505b50508080610635906118ce565b9150506103db565b508015610662578066038d7ea4c68004600082825461065c91906118e7565b90915550505b506001949350505050565b6000600154421061068057610680610c45565b6003547f00000000000000000000000000000000000000000000000000000000000000006106ae81836117a8565b6106b891906117ca565b905060006106e7847f000000000000000000000000000000000000000000000000000000000000000084610cf9565b9050801561096457604051636318523760e01b8152600481018590526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636318523790602401608060405180830381865afa158015610757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061077b9190611814565b90504281604001511115801561079357508060600151155b156108c3576040516331a9108f60e11b8152600481018690526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636352211e90602401602060405180830381865afa158015610800573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610824919061187e565b60405163a9059cbb60e01b81526001600160a01b038083166004830152602482018690529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb906044016020604051808303816000875af1158015610898573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108bc919061189b565b5050610945565b60405163b2383e5560e01b815260048101869052602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063b2383e5590604401600060405180830381600087803b15801561092c57600080fd5b505af1158015610940573d6000803e3d6000fd5b505050505b8166038d7ea4c68004600082825461095d91906118e7565b9091555050505b9392505050565b6000807f000000000000000000000000000000000000000000000000000000000000000061099981856117a8565b6109a391906117ca565b90506109d0847f000000000000000000000000000000000000000000000000000000000000000083611084565b949350505050565b66038d7ea4c68006546001600160a01b031633146109f8576109f86118fe565b610a00611331565b565b610a00610c45565b60007f0000000000000000000000000000000000000000000000000000000000000000610a3781426117a8565b610a4191906117ca565b905090565b6000807f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000600354610a9891906117a8565b610aa291906117ca565b9050610964837f000000000000000000000000000000000000000000000000000000000000000083611084565b60048166038d7ea4c680008110610ae557600080fd5b0154905081565b66038d7ea4c68005546001600160a01b03163314610b0957600080fd5b66038d7ea4c6800680546001600160a01b0319166001600160a01b0392909216919091179055565b66038d7ea4c68005546001600160a01b03163314610b4e57600080fd5b6001600160a01b038116610b6157600080fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610ba8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bcc9190611914565b60405163a9059cbb60e01b8152336004820152602481018290529091506001600160a01b0383169063a9059cbb906044016020604051808303816000875af1158015610c1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c40919061189b565b505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c2c4c5c16040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610ca057600080fd5b505af1158015610cb4573d6000803e3d6000fd5b505050506001544210610a00577f0000000000000000000000000000000000000000000000000000000000000000610cea610a0a565b610cf491906118b6565b600155565b6040516306c423c160e41b815260048101849052426024820152600090819081906001600160a01b03861690636c423c1090604401608060405180830381865afa158015610d4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d6f919061193f565b9150506000805490508165ffffffffffff16600003610d945760009350505050610964565b60008781526002602052604081205490819003610e735760405163f778e0a360e01b8152600481018990526000906001600160a01b0389169063f778e0a390602401608060405180830381865afa158015610df3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e17919061193f565b91507f00000000000000000000000000000000000000000000000000000000000000009050806001610e518265ffffffffffff86166118b6565b610e5b91906118e7565b610e6591906117a8565b610e6f91906117ca565b9150505b6003548110610e89576000945050505050610964565b81811015610e945750805b60005b6032811015611016578682101561101657604051637028a55d60e11b8152600481018a9052602481018390526000906001600160a01b038a169063e0514aba90604401602060405180830381865afa158015610ef7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1b9190611914565b905080600003610f2b5750611004565b8015610fd657604051632394e7a360e21b8152600481018490526001600160a01b038a1690638e539e8c90602401602060405180830381865afa158015610f76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f9a9190611914565b60048466038d7ea4c680008110610fb357610fb36117e9565b0154610fbf90836117ca565b610fc991906117a8565b610fd390876118b6565b95505b6110007f0000000000000000000000000000000000000000000000000000000000000000846118b6565b9250505b8061100e816118ce565b915050610e97565b5060008881526002602090815260409182902083905581518a815290810186905290810182905265ffffffffffff841660608201527fcae2990aa9af8eb1c64713b7eddb3a80bf18e49a94a13fe0d0002b5d61d58f009060800160405180910390a150919695505050505050565b60008054848252600260205260408220548291908083036111835760405163f778e0a360e01b8152600481018890526000906001600160a01b0388169063f778e0a390602401608060405180830381865afa1580156110e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061110b919061193f565b9150508065ffffffffffff1660000361112b576000945050505050610964565b7f00000000000000000000000000000000000000000000000000000000000000008060016111618265ffffffffffff86166118b6565b61116b91906118e7565b61117591906117a8565b61117f91906117ca565b9150505b60035481106111985760009350505050610964565b818110156111a35750805b60005b6032811015611325578582101561132557604051637028a55d60e11b815260048101899052602481018390526000906001600160a01b0389169063e0514aba90604401602060405180830381865afa158015611206573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061122a9190611914565b90508060000361123a5750611325565b80156112e557604051632394e7a360e21b8152600481018490526001600160a01b03891690638e539e8c90602401602060405180830381865afa158015611285573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112a99190611914565b60048466038d7ea4c6800081106112c2576112c26117e9565b01546112ce90836117ca565b6112d891906117a8565b6112e290866118b6565b94505b61130f7f0000000000000000000000000000000000000000000000000000000000000000846118b6565b925050808061131d906118ce565b9150506111a6565b50919695505050505050565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611398573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113bc9190611914565b9050600066038d7ea4c6800454826113d491906118e7565b66038d7ea4c6800483905560035490915060006113f182426118e7565b42600355905060007f000000000000000000000000000000000000000000000000000000000000000061142481856117a8565b61142e91906117ca565b90506000805b601481101561159e576114677f0000000000000000000000000000000000000000000000000000000000000000846118b6565b9150814210156114f0578315801561147e57508442145b156114b8578560048466038d7ea4c68000811061149d5761149d6117e9565b0160008282546114ad91906118b6565b9091555061159e9050565b836114c386426118e7565b6114cd90886117ca565b6114d791906117a8565b60048466038d7ea4c68000811061149d5761149d6117e9565b831580156114fd57508482145b15611537578560048466038d7ea4c68000811061151c5761151c6117e9565b01600082825461152c91906118b6565b909155506115859050565b8361154286846118e7565b61154c90886117ca565b61155691906117a8565b60048466038d7ea4c68000811061156f5761156f6117e9565b01600082825461157f91906118b6565b90915550505b8194508192508080611596906118ce565b915050611434565b5060408051428152602081018790527fce749457b74e10f393f2c6b1ce4261b78791376db5a3f501477a809f03f500d6910160405180910390a1505050505050565b6001600160a01b03811681146115f557600080fd5b50565b60006020828403121561160a57600080fd5b8135610964816115e0565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561165457611654611615565b604052919050565b6000602080838503121561166f57600080fd5b823567ffffffffffffffff8082111561168757600080fd5b818501915085601f83011261169b57600080fd5b8135818111156116ad576116ad611615565b8060051b91506116be84830161162b565b81815291830184019184810190888411156116d857600080fd5b938501935b838510156116f6578435825293850193908501906116dd565b98975050505050505050565b60006020828403121561171457600080fd5b5035919050565b6000806040838503121561172e57600080fd5b50508035926020909101359150565b600060208083528351808285015260005b8181101561176a5785810183015185820160400152820161174e565b8181111561177c576000604083870101525b50601f01601f1916929092016040019392505050565b634e487b7160e01b600052601160045260246000fd5b6000826117c557634e487b7160e01b600052601260045260246000fd5b500490565b60008160001904831182151516156117e4576117e4611792565b500290565b634e487b7160e01b600052603260045260246000fd5b8051801515811461180f57600080fd5b919050565b60006080828403121561182657600080fd5b6040516080810181811067ffffffffffffffff8211171561184957611849611615565b8060405250825181526020830151602082015260408301516040820152611872606084016117ff565b60608201529392505050565b60006020828403121561189057600080fd5b8151610964816115e0565b6000602082840312156118ad57600080fd5b610964826117ff565b600082198211156118c9576118c9611792565b500190565b6000600182016118e0576118e0611792565b5060010190565b6000828210156118f9576118f9611792565b500390565b634e487b7160e01b600052600160045260246000fd5b60006020828403121561192657600080fd5b5051919050565b8051600f81900b811461180f57600080fd5b600080828403608081121561195357600080fd5b606081121561196157600080fd5b506040516060810181811067ffffffffffffffff8211171561198557611985611615565b6040526119918461192d565b815261199f6020850161192d565b60208201526119b06040850161192d565b6040820152606084015190925065ffffffffffff811681146119d157600080fd5b80915050925092905056fea264697066735822122020e7011b304533d022f36083fb06ddec17959cafbb5f5ca2eb5c055e32f78eb664736f6c634300080d003300000000000000000000000034fda7125ecc2fa4c8c08ab091f85549bec059b7
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101375760003560e01c80638da5cb5b116100b8578063dfe050311161007c578063dfe050311461026c578063edf5999714610293578063f2c098b7146102a6578063f4f3b200146102b9578063fc0c546a146102cc578063ffa1ad74146102f357600080fd5b80638da5cb5b146101ff578063b21ed50214610230578063b80777ea14610238578063c7c4ff4614610240578063d1d58b251461025957600080fd5b8063486d25fe116100ff578063486d25fe146101b257806367503cc3146101d25780637f58e8f8146101e5578063811a40fe146101ee578063834ee417146101f657600080fd5b8063127dcbd31461013c57806313af4035146101585780631f1db0431461016d57806322b04bfc14610190578063379607f51461019f575b600080fd5b61014560015481565b6040519081526020015b60405180910390f35b61016b6101663660046115f8565b610324565b005b61018061017b36600461165c565b610369565b604051901515815260200161014f565b61014566038d7ea4c680045481565b6101456101ad366004611702565b61066d565b6101456101c0366004611702565b60026020526000908152604090205481565b6101456101e036600461171b565b61096b565b61014560035481565b61016b6109d8565b61014560005481565b66038d7ea4c6800554610218906001600160a01b031681565b6040516001600160a01b03909116815260200161014f565b61016b610a02565b610145610a0a565b66038d7ea4c6800654610218906001600160a01b031681565b610145610267366004611702565b610a46565b6102187f00000000000000000000000034fda7125ecc2fa4c8c08ab091f85549bec059b781565b6101456102a1366004611702565b610acf565b61016b6102b43660046115f8565b610aec565b61016b6102c73660046115f8565b610b31565b6102187f000000000000000000000000980e3e57b560ebf9347395de22c06fd20fa6752e81565b610317604051806040016040528060058152602001640322e312e360dc1b81525081565b60405161014f919061173d565b66038d7ea4c68005546001600160a01b0316331461034157600080fd5b66038d7ea4c6800580546001600160a01b0319166001600160a01b0392909216919091179055565b6000600154421061037c5761037c610c45565b6003547f00000000000000000000000000000000000000000000000000000000000007086103aa81836117a8565b6103b491906117ca565b90507f00000000000000000000000034fda7125ecc2fa4c8c08ab091f85549bec059b76000805b855181101561063d5760008682815181106103f8576103f86117e9565b6020026020010151905080600003610410575061063d565b600061041d828688610cf9565b9050801561062857604051636318523760e01b8152600481018390526000906001600160a01b03871690636318523790602401608060405180830381865afa15801561046d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104919190611814565b90504281604001511080156104a857508060600151155b156105b8576040516331a9108f60e11b8152600481018490526000906001600160a01b03881690636352211e90602401602060405180830381865afa1580156104f5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610519919061187e565b60405163a9059cbb60e01b81526001600160a01b038083166004830152602482018690529192507f000000000000000000000000980e3e57b560ebf9347395de22c06fd20fa6752e9091169063a9059cbb906044016020604051808303816000875af115801561058d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b1919061189b565b505061061a565b60405163b2383e5560e01b815260048101849052602481018390526001600160a01b0387169063b2383e5590604401600060405180830381600087803b15801561060157600080fd5b505af1158015610615573d6000803e3d6000fd5b505050505b61062482866118b6565b9450505b50508080610635906118ce565b9150506103db565b508015610662578066038d7ea4c68004600082825461065c91906118e7565b90915550505b506001949350505050565b6000600154421061068057610680610c45565b6003547f00000000000000000000000000000000000000000000000000000000000007086106ae81836117a8565b6106b891906117ca565b905060006106e7847f00000000000000000000000034fda7125ecc2fa4c8c08ab091f85549bec059b784610cf9565b9050801561096457604051636318523760e01b8152600481018590526000907f00000000000000000000000034fda7125ecc2fa4c8c08ab091f85549bec059b76001600160a01b031690636318523790602401608060405180830381865afa158015610757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061077b9190611814565b90504281604001511115801561079357508060600151155b156108c3576040516331a9108f60e11b8152600481018690526000907f00000000000000000000000034fda7125ecc2fa4c8c08ab091f85549bec059b76001600160a01b031690636352211e90602401602060405180830381865afa158015610800573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610824919061187e565b60405163a9059cbb60e01b81526001600160a01b038083166004830152602482018690529192507f000000000000000000000000980e3e57b560ebf9347395de22c06fd20fa6752e9091169063a9059cbb906044016020604051808303816000875af1158015610898573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108bc919061189b565b5050610945565b60405163b2383e5560e01b815260048101869052602481018390527f00000000000000000000000034fda7125ecc2fa4c8c08ab091f85549bec059b76001600160a01b03169063b2383e5590604401600060405180830381600087803b15801561092c57600080fd5b505af1158015610940573d6000803e3d6000fd5b505050505b8166038d7ea4c68004600082825461095d91906118e7565b9091555050505b9392505050565b6000807f000000000000000000000000000000000000000000000000000000000000070861099981856117a8565b6109a391906117ca565b90506109d0847f00000000000000000000000034fda7125ecc2fa4c8c08ab091f85549bec059b783611084565b949350505050565b66038d7ea4c68006546001600160a01b031633146109f8576109f86118fe565b610a00611331565b565b610a00610c45565b60007f0000000000000000000000000000000000000000000000000000000000000708610a3781426117a8565b610a4191906117ca565b905090565b6000807f00000000000000000000000000000000000000000000000000000000000007087f0000000000000000000000000000000000000000000000000000000000000708600354610a9891906117a8565b610aa291906117ca565b9050610964837f00000000000000000000000034fda7125ecc2fa4c8c08ab091f85549bec059b783611084565b60048166038d7ea4c680008110610ae557600080fd5b0154905081565b66038d7ea4c68005546001600160a01b03163314610b0957600080fd5b66038d7ea4c6800680546001600160a01b0319166001600160a01b0392909216919091179055565b66038d7ea4c68005546001600160a01b03163314610b4e57600080fd5b6001600160a01b038116610b6157600080fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610ba8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bcc9190611914565b60405163a9059cbb60e01b8152336004820152602481018290529091506001600160a01b0383169063a9059cbb906044016020604051808303816000875af1158015610c1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c40919061189b565b505050565b7f00000000000000000000000034fda7125ecc2fa4c8c08ab091f85549bec059b76001600160a01b031663c2c4c5c16040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610ca057600080fd5b505af1158015610cb4573d6000803e3d6000fd5b505050506001544210610a00577f0000000000000000000000000000000000000000000000000000000000000708610cea610a0a565b610cf491906118b6565b600155565b6040516306c423c160e41b815260048101849052426024820152600090819081906001600160a01b03861690636c423c1090604401608060405180830381865afa158015610d4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d6f919061193f565b9150506000805490508165ffffffffffff16600003610d945760009350505050610964565b60008781526002602052604081205490819003610e735760405163f778e0a360e01b8152600481018990526000906001600160a01b0389169063f778e0a390602401608060405180830381865afa158015610df3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e17919061193f565b91507f00000000000000000000000000000000000000000000000000000000000007089050806001610e518265ffffffffffff86166118b6565b610e5b91906118e7565b610e6591906117a8565b610e6f91906117ca565b9150505b6003548110610e89576000945050505050610964565b81811015610e945750805b60005b6032811015611016578682101561101657604051637028a55d60e11b8152600481018a9052602481018390526000906001600160a01b038a169063e0514aba90604401602060405180830381865afa158015610ef7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f1b9190611914565b905080600003610f2b5750611004565b8015610fd657604051632394e7a360e21b8152600481018490526001600160a01b038a1690638e539e8c90602401602060405180830381865afa158015610f76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f9a9190611914565b60048466038d7ea4c680008110610fb357610fb36117e9565b0154610fbf90836117ca565b610fc991906117a8565b610fd390876118b6565b95505b6110007f0000000000000000000000000000000000000000000000000000000000000708846118b6565b9250505b8061100e816118ce565b915050610e97565b5060008881526002602090815260409182902083905581518a815290810186905290810182905265ffffffffffff841660608201527fcae2990aa9af8eb1c64713b7eddb3a80bf18e49a94a13fe0d0002b5d61d58f009060800160405180910390a150919695505050505050565b60008054848252600260205260408220548291908083036111835760405163f778e0a360e01b8152600481018890526000906001600160a01b0388169063f778e0a390602401608060405180830381865afa1580156110e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061110b919061193f565b9150508065ffffffffffff1660000361112b576000945050505050610964565b7f00000000000000000000000000000000000000000000000000000000000007088060016111618265ffffffffffff86166118b6565b61116b91906118e7565b61117591906117a8565b61117f91906117ca565b9150505b60035481106111985760009350505050610964565b818110156111a35750805b60005b6032811015611325578582101561132557604051637028a55d60e11b815260048101899052602481018390526000906001600160a01b0389169063e0514aba90604401602060405180830381865afa158015611206573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061122a9190611914565b90508060000361123a5750611325565b80156112e557604051632394e7a360e21b8152600481018490526001600160a01b03891690638e539e8c90602401602060405180830381865afa158015611285573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112a99190611914565b60048466038d7ea4c6800081106112c2576112c26117e9565b01546112ce90836117ca565b6112d891906117a8565b6112e290866118b6565b94505b61130f7f0000000000000000000000000000000000000000000000000000000000000708846118b6565b925050808061131d906118ce565b9150506111a6565b50919695505050505050565b6040516370a0823160e01b81523060048201526000907f000000000000000000000000980e3e57b560ebf9347395de22c06fd20fa6752e6001600160a01b0316906370a0823190602401602060405180830381865afa158015611398573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113bc9190611914565b9050600066038d7ea4c6800454826113d491906118e7565b66038d7ea4c6800483905560035490915060006113f182426118e7565b42600355905060007f000000000000000000000000000000000000000000000000000000000000070861142481856117a8565b61142e91906117ca565b90506000805b601481101561159e576114677f0000000000000000000000000000000000000000000000000000000000000708846118b6565b9150814210156114f0578315801561147e57508442145b156114b8578560048466038d7ea4c68000811061149d5761149d6117e9565b0160008282546114ad91906118b6565b9091555061159e9050565b836114c386426118e7565b6114cd90886117ca565b6114d791906117a8565b60048466038d7ea4c68000811061149d5761149d6117e9565b831580156114fd57508482145b15611537578560048466038d7ea4c68000811061151c5761151c6117e9565b01600082825461152c91906118b6565b909155506115859050565b8361154286846118e7565b61154c90886117ca565b61155691906117a8565b60048466038d7ea4c68000811061156f5761156f6117e9565b01600082825461157f91906118b6565b90915550505b8194508192508080611596906118ce565b915050611434565b5060408051428152602081018790527fce749457b74e10f393f2c6b1ce4261b78791376db5a3f501477a809f03f500d6910160405180910390a1505050505050565b6001600160a01b03811681146115f557600080fd5b50565b60006020828403121561160a57600080fd5b8135610964816115e0565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561165457611654611615565b604052919050565b6000602080838503121561166f57600080fd5b823567ffffffffffffffff8082111561168757600080fd5b818501915085601f83011261169b57600080fd5b8135818111156116ad576116ad611615565b8060051b91506116be84830161162b565b81815291830184019184810190888411156116d857600080fd5b938501935b838510156116f6578435825293850193908501906116dd565b98975050505050505050565b60006020828403121561171457600080fd5b5035919050565b6000806040838503121561172e57600080fd5b50508035926020909101359150565b600060208083528351808285015260005b8181101561176a5785810183015185820160400152820161174e565b8181111561177c576000604083870101525b50601f01601f1916929092016040019392505050565b634e487b7160e01b600052601160045260246000fd5b6000826117c557634e487b7160e01b600052601260045260246000fd5b500490565b60008160001904831182151516156117e4576117e4611792565b500290565b634e487b7160e01b600052603260045260246000fd5b8051801515811461180f57600080fd5b919050565b60006080828403121561182657600080fd5b6040516080810181811067ffffffffffffffff8211171561184957611849611615565b8060405250825181526020830151602082015260408301516040820152611872606084016117ff565b60608201529392505050565b60006020828403121561189057600080fd5b8151610964816115e0565b6000602082840312156118ad57600080fd5b610964826117ff565b600082198211156118c9576118c9611792565b500190565b6000600182016118e0576118e0611792565b5060010190565b6000828210156118f9576118f9611792565b500390565b634e487b7160e01b600052600160045260246000fd5b60006020828403121561192657600080fd5b5051919050565b8051600f81900b811461180f57600080fd5b600080828403608081121561195357600080fd5b606081121561196157600080fd5b506040516060810181811067ffffffffffffffff8211171561198557611985611615565b6040526119918461192d565b815261199f6020850161192d565b60208201526119b06040850161192d565b6040820152606084015190925065ffffffffffff811681146119d157600080fd5b80915050925092905056fea264697066735822122020e7011b304533d022f36083fb06ddec17959cafbb5f5ca2eb5c055e32f78eb664736f6c634300080d0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000034fda7125ecc2fa4c8c08ab091f85549bec059b7
-----Decoded View---------------
Arg [0] : _voting_escrow (address): 0x34FdA7125eCc2Fa4C8C08ab091f85549BeC059b7
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000034fda7125ecc2fa4c8c08ab091f85549bec059b7
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.