Source Code
Overview
ETH Balance
ETH Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 12871213 | 422 days ago | 0 ETH | ||||
| 12871184 | 422 days ago | 0 ETH | ||||
| 12870966 | 422 days ago | 0 ETH | ||||
| 12870909 | 422 days ago | 0 ETH | ||||
| 12870907 | 422 days ago | 0 ETH | ||||
| 12870292 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH | ||||
| 12869945 | 422 days ago | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
VotingEscrowV2Upgradeable
Compiler Version
v0.8.13+commit.abaa5c0e
Optimization Enabled:
Yes with 1000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {IERC721EnumerableUpgradeable, ERC721EnumerableUpgradeable, IERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {ERC5725Upgradeable} from "./erc5725/ERC5725Upgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IVotingEscrowV2Upgradeable, IVotes} from "./interfaces/IVotingEscrowV2Upgradeable.sol";
import {IVeArtProxy} from "../../interfaces/IVeArtProxy.sol";
import {SafeCastLibrary} from "./libraries/SafeCastLibrary.sol";
import {EscrowDelegateCheckpoints, Checkpoints} from "./libraries/EscrowDelegateCheckpoints.sol";
import {EscrowDelegateStorage} from "./libraries/EscrowDelegateStorage.sol";
/**
* @title VotingEscrow
* @dev This contract is used for locking tokens and voting.
*
* - tokenIds always have a delegatee, with the owner being the default (see createLock)
* - On transfers, delegation is reset. (See _update)
* -
*/
contract VotingEscrowV2Upgradeable is
Initializable,
IVotingEscrowV2Upgradeable,
ERC5725Upgradeable,
EscrowDelegateStorage,
EIP712Upgradeable,
ReentrancyGuard
{
using SafeERC20Upgradeable for IERC20Upgradeable;
using SafeCastLibrary for uint256;
using EscrowDelegateCheckpoints for EscrowDelegateCheckpoints.EscrowDelegateStore;
enum DepositType {
DEPOSIT_FOR_TYPE,
CREATE_LOCK_TYPE,
INCREASE_LOCK_AMOUNT,
INCREASE_UNLOCK_TIME,
MERGE_TYPE,
SPLIT_TYPE
}
/// @notice The token being locked
IERC20Upgradeable public _token;
/// @notice Total locked supply
uint256 public supply;
uint8 public constant decimals = 18;
address public artProxy;
/// @notice The EIP-712 typehash for the delegation struct used by the contract
bytes32 public constant DELEGATION_TYPEHASH =
keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
/// @notice A record of states for signing / validating signatures
mapping(address => uint256) public nonces;
/// @dev OpenZeppelin v5 IVotes error
error VotesExpiredSignature(uint256 expiry);
/**
* @notice The constructor is disabled for this upgradeable contract.
*/
constructor() {
/// @dev Disable the initializers for implementation contracts to ensure that the contract is not left uninitialized.
_disableInitializers();
}
/**
* @dev Initializes the contract with the given parameters.
* @param _name The name to set for the token.
* @param _symbol The symbol to set for the token.
* @param version The version of the contract.
* @param mainToken The main token address that will be locked in the escrow.
* @param _artProxy The address of the art proxy contract.
*/
function initialize(
string memory _name,
string memory _symbol,
string memory version,
IERC20Upgradeable mainToken,
address _artProxy
) public initializer {
__ERC5725_init(_name, _symbol);
__EIP712_init(_name, version);
_token = mainToken;
artProxy = _artProxy;
// Reset MAX_TIME in proxy storage
MAX_TIME = uint256(uint128(EscrowDelegateCheckpoints.MAX_TIME));
}
modifier checkAuthorized(uint256 _tokenId) {
address owner = _ownerOf(_tokenId);
if (owner == address(0)) {
revert ERC721NonexistentToken(_tokenId);
}
address sender = _msgSender();
if (!_isAuthorized(owner, sender, _tokenId)) {
revert ERC721InsufficientApproval(sender, _tokenId);
}
_;
}
/// @dev Returns current token URI metadata
/// @param _tokenId Token ID to fetch URI for.
function tokenURI(uint _tokenId) public view override validToken(_tokenId) returns (string memory) {
LockDetails memory _locked = _lockDetails[_tokenId];
return
IVeArtProxy(artProxy)._tokenURI(
_tokenId,
balanceOfNFT(_tokenId),
_locked.endTime,
uint(int256(_locked.amount))
);
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(
bytes4 interfaceId
) public view virtual override(ERC5725Upgradeable, IERC165Upgradeable) returns (bool supported) {
return interfaceId == type(IVotingEscrowV2Upgradeable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-_beforeTokenTransfer}.
* Clears the approval of a given `tokenId` when the token is transferred or burned.
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 firstTokenId,
uint256 batchSize
) internal virtual override {
super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
for (uint256 i = 0; i < batchSize; i++) {
uint256 tokenId = firstTokenId + i;
if (from != to) {
/// @dev Sets delegatee to new owner on transfers
(address oldDelegatee, address newDelegatee) = edStore.delegate(
tokenId,
to,
_lockDetails[tokenId].endTime
);
emit DelegateChanged(to, oldDelegatee, newDelegatee);
emit LockDelegateChanged(tokenId, to, oldDelegatee, newDelegatee);
}
}
}
/**
* ERC-5725 and token-locking logic
*/
/// @notice maps the vesting data with tokenIds
mapping(uint256 => LockDetails) public _lockDetails;
/// @notice tracker of current NFT id
uint256 public totalNftsMinted = 0;
/**
* @notice Creates a new vesting NFT and mints it
* @dev Token amount should be approved to be transferred by this contract before executing create
* @param value The total assets to be locked over time
* @param duration Duration in seconds of the lock
* @param to The receiver of the lock
*/
function _createLock(
uint256 value,
uint256 duration,
address to,
address delegatee,
bool permanent,
DepositType depositType
) internal virtual returns (uint256) {
if (value == 0) revert ZeroAmount();
uint256 unlockTime;
totalNftsMinted++;
uint256 newTokenId = totalNftsMinted;
if (!permanent) {
unlockTime = toGlobalClock(block.timestamp + duration); // Locktime is rounded down to global clock (days)
if (unlockTime <= block.timestamp) revert LockDurationNotInFuture();
if (unlockTime > block.timestamp + MAX_TIME) revert LockDurationTooLong();
}
_safeMint(to, newTokenId);
_lockDetails[newTokenId].startTime = block.timestamp;
/// @dev Checkpoint created in _updateLock
_updateLock(newTokenId, value, unlockTime, _lockDetails[newTokenId], permanent, depositType);
edStore.delegate(newTokenId, delegatee, unlockTime);
emit LockCreated(newTokenId, delegatee, value, unlockTime, permanent);
emit DelegateChanged(to, address(0), delegatee);
emit LockDelegateChanged(newTokenId, to, address(0), delegatee);
return newTokenId;
}
/**
* @notice Creates a lock for the sender
* @param _value The total assets to be locked over time
* @param _lockDuration Duration in seconds of the lock
* @param _permanent Whether the lock is permanent or not
* @return The id of the newly created token
*/
function createLock(
uint256 _value,
uint256 _lockDuration,
bool _permanent
) external nonReentrant returns (uint256) {
return _createLock(_value, _lockDuration, _msgSender(), _msgSender(), _permanent, DepositType.CREATE_LOCK_TYPE);
}
/**
* @notice Creates a lock for a specified address
* @param _value The total assets to be locked over time
* @param _lockDuration Duration in seconds of the lock
* @param _to The receiver of the lock
* @param _permanent Whether the lock is permanent or not
* @return The id of the newly created token
*/
function createLockFor(
uint256 _value,
uint256 _lockDuration,
address _to,
bool _permanent
) external nonReentrant returns (uint256) {
return _createLock(_value, _lockDuration, _to, _to, _permanent, DepositType.CREATE_LOCK_TYPE);
}
/**
* @notice Creates a lock for a specified address
* @param _value The total assets to be locked over time
* @param _lockDuration Duration in seconds of the lock
* @param _to The receiver of the lock
* @param _delegatee The receiver of the lock
* @param _permanent Whether the lock is permanent or not
* @return The id of the newly created token
*/
function createDelegatedLockFor(
uint256 _value,
uint256 _lockDuration,
address _to,
address _delegatee,
bool _permanent
) external nonReentrant returns (uint256) {
return _createLock(_value, _lockDuration, _to, _delegatee, _permanent, DepositType.CREATE_LOCK_TYPE);
}
/**
* @notice Updates the global checkpoint
*/
function globalCheckpoint() public nonReentrant {
return edStore.globalCheckpoint();
}
function checkpoint() external override {
globalCheckpoint();
}
/**
* @notice Updates the checkpoint for a delegatee
* @param _delegateeAddress The address of the delegatee
*/
function checkpointDelegatee(address _delegateeAddress) external nonReentrant {
edStore.baseCheckpointDelegatee(_delegateeAddress);
}
/// @notice Deposit & update lock tokens for a user
/// @dev The supply is increased by the _value amount
/// @param _tokenId NFT that holds lock
/// @param _increasedValue Amount to deposit
/// @param _unlockTime New time when to unlock the tokens, or 0 if unchanged
/// @param _oldLocked Previous locked amount / timestamp
function _updateLock(
uint256 _tokenId,
uint256 _increasedValue,
uint256 _unlockTime,
LockDetails memory _oldLocked,
bool isPermanent,
DepositType depositType
) internal {
uint256 supplyBefore = supply;
supply += _increasedValue;
// Set newLocked to _oldLocked without mangling memory
LockDetails memory newLocked;
(newLocked.amount, newLocked.startTime, newLocked.endTime, newLocked.isPermanent) = (
_oldLocked.amount,
_oldLocked.startTime,
_oldLocked.endTime,
_oldLocked.isPermanent
);
// Adding to existing lock, or if a lock is expired - creating a new one
newLocked.amount += _increasedValue;
if (_unlockTime != 0 && !isPermanent) {
newLocked.endTime = _unlockTime;
}
if (isPermanent) {
newLocked.endTime = 0;
newLocked.isPermanent = true;
}
_lockDetails[_tokenId] = newLocked;
emit LockUpdated(_tokenId, _increasedValue, _unlockTime, isPermanent);
// Possibilities:
// Both _oldLocked.end could be current or expired (>/< block.timestamp)
// or if the lock is a permanent lock, then _oldLocked.end == 0
// value == 0 (extend lock) or value > 0 (add to lock or extend lock)
// newLocked.end > block.timestamp (always)
_checkpointLock(_tokenId, _oldLocked, newLocked);
if (_increasedValue != 0 && depositType != DepositType.SPLIT_TYPE) {
_token.safeTransferFrom(_msgSender(), address(this), _increasedValue);
}
emit SupplyUpdated(supply, supplyBefore + _increasedValue);
}
/// @notice Record global and per-user data to checkpoints. Used by VotingEscrow system.
/// @param _tokenId NFT token ID. No user checkpoint if 0
/// @param _oldLocked Previous locked amount / end lock time for the user
/// @param _newLocked New locked amount / end lock time for the user
function _checkpointLock(
uint256 _tokenId,
IVotingEscrowV2Upgradeable.LockDetails memory _oldLocked,
IVotingEscrowV2Upgradeable.LockDetails memory _newLocked
) internal {
edStore.checkpoint(
_tokenId,
_oldLocked.amount.toInt128(),
_newLocked.amount.toInt128(),
_oldLocked.endTime,
_newLocked.endTime
);
}
/// @notice Deposit `_value` tokens for `_tokenId` and add to the lock
/// @dev Anyone (even a smart contract) can deposit for someone else, but
/// cannot extend their locktime and deposit for a brand new user
/// @param _tokenId lock NFT
/// @param _value Amount to add to user's lock
function increaseAmount(uint256 _tokenId, uint256 _value) external nonReentrant {
if (_value == 0) revert ZeroAmount();
IVotingEscrowV2Upgradeable.LockDetails memory oldLocked = _lockDetails[_tokenId];
if (_ownerOf(_tokenId) == address(0)) revert NoLockFound();
if (oldLocked.endTime <= block.timestamp && !oldLocked.isPermanent) revert LockExpired();
_updateLock(_tokenId, _value, 0, oldLocked, oldLocked.isPermanent, DepositType.INCREASE_LOCK_AMOUNT);
}
/**
* @notice Increases the unlock time of a lock
* @param _tokenId The id of the token to increase the unlock time for
* @param _lockDuration The new duration of the lock
* @param _permanent Whether the lock is permanent or not
*/
function increaseUnlockTime(
uint256 _tokenId,
uint256 _lockDuration,
bool _permanent
) external nonReentrant checkAuthorized(_tokenId) {
LockDetails memory oldLocked = _lockDetails[_tokenId];
if (oldLocked.isPermanent) revert PermanentLock();
uint256 unlockTime;
if (!_permanent) {
unlockTime = toGlobalClock(block.timestamp + _lockDuration);
// Locktime is rounded down to global clock (days)
if (oldLocked.endTime <= block.timestamp) revert LockExpired();
if (unlockTime <= oldLocked.endTime) revert LockDurationNotInFuture();
if (unlockTime > block.timestamp + MAX_TIME) revert LockDurationTooLong();
}
_updateLock(_tokenId, 0, unlockTime, oldLocked, _permanent, DepositType.INCREASE_UNLOCK_TIME);
emit LockDurationExtended(_tokenId, unlockTime, _permanent);
}
/**
* @notice Unlocks a permanent lock
* @param _tokenId The id of the token to unlock
*/
function unlockPermanent(uint256 _tokenId) external nonReentrant checkAuthorized(_tokenId) {
LockDetails memory newLocked = _lockDetails[_tokenId];
if (!newLocked.isPermanent) revert NotPermanentLock();
// Set the end time to the maximum possible time
newLocked.endTime = toGlobalClock(block.timestamp + MAX_TIME);
// Set the lock to not be permanent
newLocked.isPermanent = false;
// Update the lock details
_checkpointLock(_tokenId, _lockDetails[_tokenId], newLocked);
_lockDetails[_tokenId] = newLocked;
emit UnlockPermanent(_tokenId, _msgSender(), newLocked.endTime);
}
/**
* @notice Claims the payout for a token
* @param _tokenId The id of the token to claim the payout for
*/
function _claim(uint256 _tokenId) internal validToken(_tokenId) nonReentrant checkAuthorized(_tokenId) {
IVotingEscrowV2Upgradeable.LockDetails memory oldLocked = _lockDetails[_tokenId];
if (oldLocked.isPermanent) revert PermanentLock();
uint256 amountClaimed = claimablePayout(_tokenId);
if (amountClaimed == 0) revert LockNotExpired();
// Reset the lock details
_lockDetails[_tokenId] = IVotingEscrowV2Upgradeable.LockDetails(0, 0, 0, false);
// Update the total supply
uint256 supplyBefore = supply;
supply -= amountClaimed;
// Update the lock details
_checkpointLock(_tokenId, oldLocked, _lockDetails[_tokenId]);
/// @notice ERC-5725 event
emit PayoutClaimed(_tokenId, msg.sender, amountClaimed);
// IERC5725 - Update the total amount claimed
_payoutClaimed[_tokenId] += amountClaimed;
// Transfer the claimed amount to the sender
IERC20Upgradeable(_payoutToken(_tokenId)).safeTransfer(msg.sender, amountClaimed);
emit SupplyUpdated(supplyBefore, supply);
}
/**
* @notice Claims the payout for a token
* @param _tokenId The id of the token to claim the payout for
*/
function claim(uint256 _tokenId) external override(ERC5725Upgradeable) {
_claim(_tokenId);
}
/**
* @notice Merges two tokens together
* @param _from The id of the token to merge from
* @param _to The id of the token to merge to
*/
function merge(uint256 _from, uint256 _to) external nonReentrant checkAuthorized(_from) checkAuthorized(_to) {
if (_from == _to) revert SameNFT();
IVotingEscrowV2Upgradeable.LockDetails memory oldLockedTo = _lockDetails[_to];
if (oldLockedTo.amount == 0) revert ZeroAmount();
if (oldLockedTo.endTime <= block.timestamp && !oldLockedTo.isPermanent) revert LockExpired();
IVotingEscrowV2Upgradeable.LockDetails memory oldLockedFrom = _lockDetails[_from];
if (oldLockedFrom.amount == 0) revert ZeroAmount();
if (oldLockedFrom.isPermanent == true && oldLockedFrom.isPermanent != oldLockedTo.isPermanent) revert PermanentLockMismatch();
// Calculate the new end time
uint256 end = oldLockedFrom.endTime >= oldLockedTo.endTime ? oldLockedFrom.endTime : oldLockedTo.endTime;
// Set lock amount to 0
_lockDetails[_from].amount = 0;
// Update the lock details
_checkpointLock(_from, oldLockedFrom, _lockDetails[_from]);
// Calculate the new lock details
LockDetails memory newLockedTo;
newLockedTo.amount = oldLockedTo.amount + oldLockedFrom.amount;
newLockedTo.isPermanent = oldLockedTo.isPermanent;
if (!newLockedTo.isPermanent) {
newLockedTo.endTime = end;
}
// Update the lock details
_checkpointLock(_to, oldLockedTo, newLockedTo);
_lockDetails[_to] = newLockedTo;
emit LockMerged(_from, _to, newLockedTo.amount, end, newLockedTo.isPermanent);
}
/**
* @notice Splits a token into multiple tokens
* @param _weights The percentages to split the token into
* @param _tokenId The id of the token to split
*/
function split(uint256[] memory _weights, uint256 _tokenId) external nonReentrant checkAuthorized(_tokenId) {
LockDetails memory locked = _lockDetails[_tokenId];
LockDetails storage lockedStorage = _lockDetails[_tokenId];
uint256 currentTime = block.timestamp;
/// @dev Pulling directly from locked struct to avoid stack-too-deep
if (locked.endTime <= currentTime && !locked.isPermanent) revert LockExpired();
if (locked.amount == 0 || _weights.length < 2) revert ZeroAmount();
// reset supply, _deposit_for increase it
supply -= uint256(int256(locked.amount));
// Capture owner for split
address owner = _ownerOf(_tokenId);
uint256 totalWeight = 0;
for (uint256 i = 0; i < _weights.length; i++) {
totalWeight += _weights[i];
}
if (totalWeight == 0) revert InvalidWeights();
uint256 duration = locked.isPermanent ? 0 : locked.endTime > currentTime ? locked.endTime - currentTime : 0;
uint256 amountLeftToSplit = locked.amount;
for (uint256 i = 0; i < _weights.length; i++) {
uint256 value = (uint256(int256(locked.amount)) * _weights[i]) / totalWeight;
if(i == _weights.length - 1) {
/// @dev Ensure no rounding errors occur by passing the remainder to the last split
value = amountLeftToSplit;
}
amountLeftToSplit -= value;
if (i == 0) {
lockedStorage.amount = value;
supply += value;
_checkpointLock(_tokenId, locked, lockedStorage);
} else {
_createLock(value, duration, owner, owner, locked.isPermanent, DepositType.SPLIT_TYPE);
}
}
emit LockSplit(_weights, _tokenId);
}
/**
* @notice Burns a token
* @param _tokenId The ids of the tokens to burn
*/
function burn(uint256 _tokenId) external {
if (_ownerOf(_tokenId) != _msgSender()) revert NotLockOwner();
if(_lockDetails[_tokenId].amount > 0) revert LockHoldsValue();
_burn(_tokenId);
}
/*///////////////////////////////////////////////////////////////
GAUGE REWARDS LOGIC
//////////////////////////////////////////////////////////////*/
function balanceOfNFT(uint256 _tokenId) public view returns (uint256) {
return edStore.getAdjustedEscrowBias(_tokenId, block.timestamp);
}
function balanceOfNFTAt(uint256 _tokenId, uint256 _timestamp) external view returns (uint256) {
return edStore.getAdjustedEscrowBias(_tokenId, _timestamp);
}
function getPastEscrowPoint(
uint256 _tokenId,
uint256 _timestamp
) external view override returns (Checkpoints.Point memory, uint48) {
return edStore.getAdjustedEscrow(_tokenId, _timestamp);
}
function getFirstEscrowPoint(uint256 _tokenId) external view override returns (Checkpoints.Point memory, uint48) {
return edStore.getFirstEscrowPoint(_tokenId);
}
function totalSupply() public view override(ERC721EnumerableUpgradeable, IERC721EnumerableUpgradeable) returns (uint256) {
return edStore.getAdjustedGlobalVotes(block.timestamp.toUint48());
}
/*///////////////////////////////////////////////////////////////
@dev See {IVotes}.
//////////////////////////////////////////////////////////////*/
/**
* @notice Gets the votes for a delegatee
* @param account The address of the delegatee
* @return The number of votes the delegatee has
*/
function getVotes(address account) external view override(IVotes) returns (uint256) {
return edStore.getAdjustedVotes(account, block.timestamp.toUint48());
}
/**
* @notice Gets the past votes for a delegatee at a specific time point
* @param account The address of the delegatee
* @param timepoint The time point to get the votes at
* @return The number of votes the delegatee had at the time point
*/
function getPastVotes(address account, uint256 timepoint) external view override(IVotes) returns (uint256) {
return edStore.getAdjustedVotes(account, timepoint.toUint48());
}
/**
* @notice Gets the total supply at a specific time point
* @param _timePoint The time point to get the total supply at
* @return The total supply at the time point
*/
function getPastTotalSupply(uint256 _timePoint) external view override(IVotes) returns (uint256) {
return edStore.getAdjustedGlobalVotes(_timePoint.toUint48());
}
/**
* @notice Delegates votes to a delegatee
* @param delegatee The account to delegate votes to
*/
function delegate(address delegatee) external override(IVotes) {
_delegate(_msgSender(), delegatee);
}
/**
* @notice Gets the delegate of a delegatee
* @dev This function implements IVotes interface.
* An account can have multiple delegates in this contract. If multiple
* different delegates are found, this function returns address(1) to
* indicate that there is not a single unique delegate.
* @param account The delegatee to get the delegate of
* @return The delegate of the delegatee, or address(1) if multiple different delegates are found
*/
function delegates(address account) external view override(IVotes) returns (address) {
address delegatee = address(0);
uint256 balance = balanceOf(account);
/// @dev out-of-gas protection
uint256 runs = 50 > balance ? balance : 50;
for (uint256 i = 0; i < runs; i++) {
uint256 tokenId = tokenOfOwnerByIndex(account, i);
address currentDelegatee = edStore.getEscrowDelegatee(tokenId);
/// @dev Hacky way to check if the delegatee is the same for all locks
if (delegatee == address(0)) {
delegatee = currentDelegatee;
} else if (delegatee != currentDelegatee) {
return address(1);
}
}
return delegatee;
}
/**
* @notice Delegates votes from a specific lock to a delegatee
* @param _tokenId The ID of the lock token delegating the votes
* @param delegatee The address to which the votes are being delegated
*/
function delegate(uint256 _tokenId, address delegatee) external checkAuthorized(_tokenId) {
(address fromDelegatee, address toDelegatee) = edStore.delegate(
_tokenId,
delegatee,
_lockDetails[_tokenId].endTime
);
emit LockDelegateChanged(_tokenId, _msgSender(), fromDelegatee, toDelegatee);
}
/**
* @notice Gets the delegatee of a given lock
* @param tokenId The ID of the lock token
* @return The address of the delegatee for the specified token
*/
function getLockDelegatee(uint256 tokenId) external view returns (address) {
return edStore.getEscrowDelegatee(tokenId);
}
/**
* @notice Gets all delegates of a delegatee
* @param account The delegatee to get the delegates of
* @return An array of all delegates of the delegatee
*/
function getAccountDelegates(address account) external view returns (address[] memory) {
uint256 balance = balanceOf(account);
address[] memory allDelegates = new address[](balance);
for (uint256 i = 0; i < balance; i++) {
uint256 tokenId = tokenOfOwnerByIndex(account, i);
allDelegates[i] = edStore.getEscrowDelegatee(tokenId);
}
return allDelegates;
}
/**
* @notice Public function to get the delegatee of a lock
* @param tokenId The ID of the token
* @param timestamp The timestamp to get the delegate at
* @return The address of the delegate
*/
function delegates(uint256 tokenId, uint48 timestamp) external view returns (address) {
return edStore.getEscrowDelegateeAtTime(tokenId, timestamp);
}
/**
* @notice Delegates votes by signature
* @param delegatee The delegatee to delegate votes to
* @param nonce The nonce for the signature
* @param expiry The expiry time for the signature
* @param v The recovery byte of the signature
* @param r Half of the ECDSA signature pair
* @param s Half of the ECDSA signature pair
*/
function delegateBySig(
address delegatee,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) external override(IVotes) {
// Removed for gas considerations. The code below uncommented adds 1.289 kbs to the contract size.
revert("delegateBySig: size cut");
/*
if (delegatee == msg.sender || delegatee == address(0)) revert InvalidDelegatee();
bytes32 domainSeparator = _domainSeparatorV4();
bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
address signatory = ECDSA.recover(digest, v, r, s);
if (signatory == address(0)) revert InvalidSignature();
if (nonce != nonces[signatory]++) revert InvalidNonce();
if (block.timestamp > expiry) revert VotesExpiredSignature(expiry);
return _delegate(signatory, delegatee);
*/
}
/**
* @notice Delegates votes from an owner to an delegatee
* @param delegator The owner of the tokenId delegating votes
* @param delegatee The account to delegate votes to
*/
function _delegate(address delegator, address delegatee) internal nonReentrant {
uint256 balance = balanceOf(delegator);
address fromDelegate = address(0);
for (uint256 i = 0; i < balance; i++) {
uint256 tokenId = tokenOfOwnerByIndex(delegator, i);
(address oldDelegate, address newDelegate) = edStore.delegate(
tokenId,
delegatee,
_lockDetails[tokenId].endTime
);
emit LockDelegateChanged(tokenId, delegator, oldDelegate, newDelegate);
/// @dev Hacky way to check if the delegatee is the same for all locks
if (fromDelegate == address(0)) {
fromDelegate = oldDelegate;
} else if (fromDelegate != address(1)) {
if (fromDelegate != oldDelegate) {
fromDelegate = address(1);
}
}
}
emit DelegateChanged(delegator, fromDelegate, delegatee);
}
/*///////////////////////////////////////////////////////////////
@dev See {IERC5725}.
//////////////////////////////////////////////////////////////*/
/**
* @dev See {ERC5725Upgradeable}.
*/
function vestedPayoutAtTime(
uint256 tokenId,
uint256 timestamp
) public view override(ERC5725Upgradeable) validToken(tokenId) returns (uint256 payout) {
if (timestamp >= _endTime(tokenId)) {
return _payout(tokenId);
}
return 0;
}
/**
* @dev See {ERC5725Upgradeable}.
*/
function _payoutToken(uint256 /*tokenId*/) internal view override returns (address) {
return address(_token);
}
/**
* @dev See {ERC5725Upgradeable}.
*/
function _payout(uint256 tokenId) internal view override returns (uint256) {
return _lockDetails[tokenId].amount;
}
/**
* @dev See {ERC5725Upgradeable}.
*/
function _startTime(uint256 tokenId) internal view override returns (uint256) {
return _lockDetails[tokenId].startTime;
}
/**
* @dev See {ERC5725Upgradeable}.
*/
function _endTime(uint256 tokenId) internal view override returns (uint256) {
return _lockDetails[tokenId].endTime;
}
function token() external view returns (IERC20Upgradeable) {
return _token;
}
function lockDetails(uint256 _tokenId) external view returns (LockDetails memory) {
return _lockDetails[_tokenId];
}
function isApprovedOrOwner(address user, uint tokenId) external view returns (bool) {
return _isAuthorized(ownerOf(tokenId), user, tokenId);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.0;
interface IERC5267Upgradeable {
/**
* @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) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20PermitUpgradeable {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// 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.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
import "../extensions/IERC20PermitUpgradeable.sol";
import "../../../utils/AddressUpgradeable.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20Upgradeable {
using AddressUpgradeable for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20PermitUpgradeable token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20Upgradeable token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && AddressUpgradeable.isContract(address(token));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol)
pragma solidity ^0.8.0;
import "./IERC721Upgradeable.sol";
import "./IERC721ReceiverUpgradeable.sol";
import "./extensions/IERC721MetadataUpgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../utils/StringsUpgradeable.sol";
import "../../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/
contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {
using AddressUpgradeable for address;
using StringsUpgradeable for uint256;
// Token name
string private _name;
// Token symbol
string private _symbol;
// Mapping from token ID to owner address
mapping(uint256 => address) private _owners;
// Mapping owner address to token count
mapping(address => uint256) private _balances;
// Mapping from token ID to approved address
mapping(uint256 => address) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing {
__ERC721_init_unchained(name_, symbol_);
}
function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
_name = name_;
_symbol = symbol_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
return
interfaceId == type(IERC721Upgradeable).interfaceId ||
interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "ERC721: address zero is not a valid owner");
return _balances[owner];
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
address owner = _ownerOf(tokenId);
require(owner != address(0), "ERC721: invalid token ID");
return owner;
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
_requireMinted(tokenId);
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual override {
address owner = ERC721Upgradeable.ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(
_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not token owner or approved for all"
);
_approve(to, tokenId);
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(uint256 tokenId) public view virtual override returns (address) {
_requireMinted(tokenId);
return _tokenApprovals[tokenId];
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(address from, address to, uint256 tokenId) public virtual override {
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
_transfer(from, to, tokenId);
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
_safeTransfer(from, to, tokenId, data);
}
/**
* @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.
*
* `data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
_transfer(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
}
/**
* @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
*/
function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
return _owners[tokenId];
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted (`_mint`),
* and stop existing when they are burned (`_burn`).
*/
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return _ownerOf(tokenId) != address(0);
}
/**
* @dev Returns whether `spender` is allowed to manage `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
address owner = ERC721Upgradeable.ownerOf(tokenId);
return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
}
/**
* @dev Safely mints `tokenId` and transfers it to `to`.
*
* Requirements:
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeMint(address to, uint256 tokenId) internal virtual {
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
require(
_checkOnERC721Received(address(0), to, tokenId, data),
"ERC721: transfer to non ERC721Receiver implementer"
);
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId, 1);
// Check that tokenId was not minted by `_beforeTokenTransfer` hook
require(!_exists(tokenId), "ERC721: token already minted");
unchecked {
// Will not overflow unless all 2**256 token ids are minted to the same owner.
// Given that tokens are minted one by one, it is impossible in practice that
// this ever happens. Might change if we allow batch minting.
// The ERC fails to describe this case.
_balances[to] += 1;
}
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
_afterTokenTransfer(address(0), to, tokenId, 1);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
* This is an internal function that does not check if the sender is authorized to operate on the token.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal virtual {
address owner = ERC721Upgradeable.ownerOf(tokenId);
_beforeTokenTransfer(owner, address(0), tokenId, 1);
// Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
owner = ERC721Upgradeable.ownerOf(tokenId);
// Clear approvals
delete _tokenApprovals[tokenId];
unchecked {
// Cannot overflow, as that would require more tokens to be burned/transferred
// out than the owner initially received through minting and transferring in.
_balances[owner] -= 1;
}
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
_afterTokenTransfer(owner, address(0), tokenId, 1);
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/
function _transfer(address from, address to, uint256 tokenId) internal virtual {
require(ERC721Upgradeable.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
require(to != address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId, 1);
// Check that tokenId was not transferred by `_beforeTokenTransfer` hook
require(ERC721Upgradeable.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
// Clear approvals from the previous owner
delete _tokenApprovals[tokenId];
unchecked {
// `_balances[from]` cannot overflow for the same reason as described in `_burn`:
// `from`'s balance is the number of token held, which is at least one before the current
// transfer.
// `_balances[to]` could overflow in the conditions described in `_mint`. That would require
// all 2**256 token ids to be minted, which in practice is impossible.
_balances[from] -= 1;
_balances[to] += 1;
}
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
_afterTokenTransfer(from, to, tokenId, 1);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* Emits an {Approval} event.
*/
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId);
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits an {ApprovalForAll} event.
*/
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
require(owner != operator, "ERC721: approve to caller");
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Reverts if the `tokenId` has not been minted yet.
*/
function _requireMinted(uint256 tokenId) internal view virtual {
require(_exists(tokenId), "ERC721: invalid token ID");
}
/**
* @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
* The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param data bytes optional data to send along with the call
* @return bool whether the call correctly returned the expected magic value
*/
function _checkOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory data
) private returns (bool) {
if (to.isContract()) {
try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
return retval == IERC721ReceiverUpgradeable.onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert("ERC721: transfer to non ERC721Receiver implementer");
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
/**
* @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
* used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
* - When `from` is zero, the tokens will be minted for `to`.
* - When `to` is zero, ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
* - `batchSize` is non-zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
/**
* @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
* used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
* - When `from` is zero, the tokens were minted for `to`.
* - When `to` is zero, ``from``'s tokens were burned.
* - `from` and `to` are never both zero.
* - `batchSize` is non-zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
/**
* @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
*
* WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant
* being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such
* that `ownerOf(tokenId)` is `a`.
*/
// solhint-disable-next-line func-name-mixedcase
function __unsafe_increaseBalance(address account, uint256 amount) internal {
_balances[account] += amount;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[44] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Enumerable.sol)
pragma solidity ^0.8.0;
import "../ERC721Upgradeable.sol";
import "./IERC721EnumerableUpgradeable.sol";
import {Initializable} from "../../../proxy/utils/Initializable.sol";
/**
* @dev This implements an optional extension of {ERC721} defined in the EIP that adds
* enumerability of all the token ids in the contract as well as all token ids owned by each
* account.
*/
abstract contract ERC721EnumerableUpgradeable is Initializable, ERC721Upgradeable, IERC721EnumerableUpgradeable {
// Mapping from owner to list of owned token IDs
mapping(address => mapping(uint256 => uint256)) private _ownedTokens;
// Mapping from token ID to index of the owner tokens list
mapping(uint256 => uint256) private _ownedTokensIndex;
// Array with all token ids, used for enumeration
uint256[] private _allTokens;
// Mapping from token id to position in the allTokens array
mapping(uint256 => uint256) private _allTokensIndex;
function __ERC721Enumerable_init() internal onlyInitializing {
}
function __ERC721Enumerable_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, ERC721Upgradeable) returns (bool) {
return interfaceId == type(IERC721EnumerableUpgradeable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
require(index < ERC721Upgradeable.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
return _ownedTokens[owner][index];
}
/**
* @dev See {IERC721Enumerable-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _allTokens.length;
}
/**
* @dev See {IERC721Enumerable-tokenByIndex}.
*/
function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
require(index < ERC721EnumerableUpgradeable.totalSupply(), "ERC721Enumerable: global index out of bounds");
return _allTokens[index];
}
/**
* @dev See {ERC721-_beforeTokenTransfer}.
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 firstTokenId,
uint256 batchSize
) internal virtual override {
super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
if (batchSize > 1) {
// Will only trigger during construction. Batch transferring (minting) is not available afterwards.
revert("ERC721Enumerable: consecutive transfers not supported");
}
uint256 tokenId = firstTokenId;
if (from == address(0)) {
_addTokenToAllTokensEnumeration(tokenId);
} else if (from != to) {
_removeTokenFromOwnerEnumeration(from, tokenId);
}
if (to == address(0)) {
_removeTokenFromAllTokensEnumeration(tokenId);
} else if (to != from) {
_addTokenToOwnerEnumeration(to, tokenId);
}
}
/**
* @dev Private function to add a token to this extension's ownership-tracking data structures.
* @param to address representing the new owner of the given token ID
* @param tokenId uint256 ID of the token to be added to the tokens list of the given address
*/
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
uint256 length = ERC721Upgradeable.balanceOf(to);
_ownedTokens[to][length] = tokenId;
_ownedTokensIndex[tokenId] = length;
}
/**
* @dev Private function to add a token to this extension's token tracking data structures.
* @param tokenId uint256 ID of the token to be added to the tokens list
*/
function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
_allTokensIndex[tokenId] = _allTokens.length;
_allTokens.push(tokenId);
}
/**
* @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
* while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
* gas optimizations e.g. when performing a transfer operation (avoiding double writes).
* This has O(1) time complexity, but alters the order of the _ownedTokens array.
* @param from address representing the previous owner of the given token ID
* @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
*/
function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
// To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = ERC721Upgradeable.balanceOf(from) - 1;
uint256 tokenIndex = _ownedTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary
if (tokenIndex != lastTokenIndex) {
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
_ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
}
// This also deletes the contents at the last position of the array
delete _ownedTokensIndex[tokenId];
delete _ownedTokens[from][lastTokenIndex];
}
/**
* @dev Private function to remove a token from this extension's token tracking data structures.
* This has O(1) time complexity, but alters the order of the _allTokens array.
* @param tokenId uint256 ID of the token to be removed from the tokens list
*/
function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
// To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = _allTokens.length - 1;
uint256 tokenIndex = _allTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
// rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
// an 'if' statement (like in _removeTokenFromOwnerEnumeration)
uint256 lastTokenId = _allTokens[lastTokenIndex];
_allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
// This also deletes the contents at the last position of the array
delete _allTokensIndex[tokenId];
_allTokens.pop();
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[46] private __gap;
}// 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 v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC721Upgradeable.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721MetadataUpgradeable is IERC721Upgradeable {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721ReceiverUpgradeable {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// 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 (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../StringsUpgradeable.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 ECDSAUpgradeable {
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", StringsUpgradeable.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/cryptography/EIP712.sol)
pragma solidity ^0.8.8;
import "./ECDSAUpgradeable.sol";
import "../../interfaces/IERC5267Upgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.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:storage-size 52
*/
abstract contract EIP712Upgradeable is Initializable, IERC5267Upgradeable {
bytes32 private constant _TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/// @custom:oz-renamed-from _HASHED_NAME
bytes32 private _hashedName;
/// @custom:oz-renamed-from _HASHED_VERSION
bytes32 private _hashedVersion;
string private _name;
string private _version;
/**
* @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].
*/
function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
__EIP712_init_unchained(name, version);
}
function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
_name = name;
_version = version;
// Reset prior values in storage if upgrading
_hashedName = 0;
_hashedVersion = 0;
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
return _buildDomainSeparator();
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), 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 ECDSAUpgradeable.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
)
{
// If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized
// and the EIP712 domain is not reliable, as it will be missing name and version.
require(_hashedName == 0 && _hashedVersion == 0, "EIP712: Uninitialized");
return (
hex"0f", // 01111
_EIP712Name(),
_EIP712Version(),
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
/**
* @dev The name parameter for the EIP712 domain.
*
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712Name() internal virtual view returns (string memory) {
return _name;
}
/**
* @dev The version parameter for the EIP712 domain.
*
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712Version() internal virtual view returns (string memory) {
return _version;
}
/**
* @dev The hash of the name parameter for the EIP712 domain.
*
* NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead.
*/
function _EIP712NameHash() internal view returns (bytes32) {
string memory name = _EIP712Name();
if (bytes(name).length > 0) {
return keccak256(bytes(name));
} else {
// If the name is empty, the contract may have been upgraded without initializing the new storage.
// We return the name hash in storage if non-zero, otherwise we assume the name is empty by design.
bytes32 hashedName = _hashedName;
if (hashedName != 0) {
return hashedName;
} else {
return keccak256("");
}
}
}
/**
* @dev The hash of the version parameter for the EIP712 domain.
*
* NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead.
*/
function _EIP712VersionHash() internal view returns (bytes32) {
string memory version = _EIP712Version();
if (bytes(version).length > 0) {
return keccak256(bytes(version));
} else {
// If the version is empty, the contract may have been upgraded without initializing the new storage.
// We return the version hash in storage if non-zero, otherwise we assume the version is empty by design.
bytes32 hashedVersion = _hashedVersion;
if (hashedVersion != 0) {
return hashedVersion;
} else {
return keccak256("");
}
}
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[48] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165Upgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165Upgradeable).interfaceId;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// 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 MathUpgradeable {
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/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMathUpgradeable {
/**
* @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);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/MathUpgradeable.sol";
import "./math/SignedMathUpgradeable.sol";
/**
* @dev String operations.
*/
library StringsUpgradeable {
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 = MathUpgradeable.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(SignedMathUpgradeable.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, MathUpgradeable.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) (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;
/**
* @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
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}// 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/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/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);
}
}
}// 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
pragma solidity 0.8.13;
interface IVeArtProxy {
function _tokenURI(uint _tokenId, uint _balanceOf, uint _locked_end, uint _value) external pure returns (string memory output);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.13;
import {ERC721EnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
/// @dev Official ERC-5725 interface
import {IERC5725Upgradeable, IERC165Upgradeable} from "./IERC5725Upgradeable.sol";
import {IERC721Errors} from "../interfaces/IERC721Errors.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
abstract contract ERC5725Upgradeable is
Initializable,
IERC5725Upgradeable,
ERC721EnumerableUpgradeable,
IERC721Errors
{
using SafeERC20Upgradeable for IERC20Upgradeable;
/// @dev mapping for claimed payouts
mapping(uint256 => uint256) /*tokenId*/ /*claimed*/ internal _payoutClaimed;
/// @dev Mapping from token ID to approved tokenId operator
mapping(uint256 => address) private _tokenIdApprovals;
/// @dev Mapping from owner to operator approvals
mapping(address => mapping(address => bool)) /* owner */ /*(operator, isApproved)*/ internal _operatorApprovals;
/// @dev Reserved storage space to allow for layout changes in the future.
uint256[50] private __gap;
constructor() {
/// @dev Disable the initializers for implementation contracts to ensure that the contract is not left uninitialized.
_disableInitializers();
}
function __ERC5725_init(string memory name_, string memory symbol_) internal initializer {
__ERC721_init(name_, symbol_);
/// @dev Currently this call does nothing, but it is left here for future compatibility.
__ERC721Enumerable_init();
}
/**
* @notice Checks if the tokenId exists and its valid
* @param tokenId The NFT token id
*/
modifier validToken(uint256 tokenId) {
address owner = _ownerOf(tokenId);
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
_;
}
/**
* @dev See {IERC5725Upgradeable}.
*/
function claim(uint256 tokenId) external virtual override(IERC5725Upgradeable);
/**
* @dev See {IERC5725Upgradeable}.
*/
function setClaimApprovalForAll(address operator, bool approved) external override(IERC5725Upgradeable) {
_setClaimApprovalForAll(operator, approved);
emit ClaimApprovalForAll(msg.sender, operator, approved);
}
/**
* @dev See {IERC5725Upgradeable}.
*/
function setClaimApproval(
address operator,
bool approved,
uint256 tokenId
) external override(IERC5725Upgradeable) validToken(tokenId) {
_setClaimApproval(operator, tokenId);
emit ClaimApproval(msg.sender, operator, tokenId, approved);
}
/**
* @dev See {IERC5725Upgradeable}.
*/
function vestedPayout(uint256 tokenId) public view override(IERC5725Upgradeable) returns (uint256 payout) {
return vestedPayoutAtTime(tokenId, block.timestamp);
}
/**
* @dev See {IERC5725Upgradeable}.
*/
function vestedPayoutAtTime(
uint256 tokenId,
uint256 timestamp
) public view virtual override(IERC5725Upgradeable) returns (uint256 payout);
/**
* @dev See {IERC5725Upgradeable}.
*/
function vestingPayout(
uint256 tokenId
) public view override(IERC5725Upgradeable) validToken(tokenId) returns (uint256 payout) {
return _payout(tokenId) - vestedPayout(tokenId);
}
/**
* @dev See {IERC5725Upgradeable}.
*/
function claimablePayout(
uint256 tokenId
) public view override(IERC5725Upgradeable) validToken(tokenId) returns (uint256 payout) {
return vestedPayout(tokenId) - _payoutClaimed[tokenId];
}
/**
* @dev See {IERC5725Upgradeable}.
*/
function claimedPayout(
uint256 tokenId
) public view override(IERC5725Upgradeable) validToken(tokenId) returns (uint256 payout) {
return _payoutClaimed[tokenId];
}
/**
* @dev See {IERC5725Upgradeable}.
*/
function vestingPeriod(
uint256 tokenId
) public view override(IERC5725Upgradeable) validToken(tokenId) returns (uint256 vestingStart, uint256 vestingEnd) {
return (_startTime(tokenId), _endTime(tokenId));
}
/**
* @dev See {IERC5725Upgradeable}.
*/
function payoutToken(uint256 tokenId) public view override(IERC5725Upgradeable) validToken(tokenId) returns (address token) {
return _payoutToken(tokenId);
}
/**
* @dev See {IERC165-supportsInterface}.
* IERC5725Upgradeable interfaceId = 0xbd3a202b
*/
function supportsInterface(
bytes4 interfaceId
) public view virtual override(ERC721EnumerableUpgradeable, IERC165Upgradeable) returns (bool supported) {
return interfaceId == type(IERC5725Upgradeable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC5725Upgradeable}.
*/
function getClaimApproved(uint256 tokenId) public view returns (address operator) {
return _tokenIdApprovals[tokenId];
}
/**
* @dev 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) public view returns (bool isClaimApproved) {
return _operatorApprovals[owner][operator];
}
/**
* @dev Public view which returns true if the operator has permission to claim for `tokenId`
* @notice To remove permissions, set operator to zero address.
*
* @param operator The address that has permission for a `tokenId`.
* @param tokenId The NFT `tokenId`.
*/
function isApprovedClaimOrOwner(address operator, uint256 tokenId) public view virtual returns (bool) {
address owner = ownerOf(tokenId);
return (operator == owner || isClaimApprovedForAll(owner, operator) || getClaimApproved(tokenId) == operator);
}
/**
* @dev Internal function to set the operator status for a given owner to manage all `tokenId`s.
* @notice To remove permissions, set approved to false.
*
* @param operator The address who is given permission to spend vested tokens.
* @param approved The approved status.
*/
function _setClaimApprovalForAll(address operator, bool approved) internal virtual {
_operatorApprovals[msg.sender][operator] = approved;
}
/**
* @dev Internal function to set the operator status for a given tokenId.
* @notice To remove permissions, set operator to zero address.
*
* @param operator The address who is given permission to spend vested tokens.
* @param tokenId The NFT `tokenId`.
*/
function _setClaimApproval(address operator, uint256 tokenId) internal virtual {
if (ownerOf(tokenId) != msg.sender) revert ERC721IncorrectOwner(msg.sender, tokenId, ownerOf(tokenId));
_tokenIdApprovals[tokenId] = operator;
}
/**
* @dev See {IERC721-_beforeTokenTransfer}.
* Clears the approval of a given `tokenId` when the token is transferred or burned.
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 firstTokenId,
uint256 batchSize
) internal virtual override {
super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
for (uint256 i = 0; i < batchSize; i++) {
uint256 tokenId = firstTokenId + i;
if (from != address(0) || from != to) {
delete _tokenIdApprovals[tokenId];
}
}
}
/**
* @dev Internal function to get the payout token of a given vesting NFT
*
* @param tokenId on which to check the payout token address
* @return address payout token address
*/
function _payoutToken(uint256 tokenId) internal view virtual returns (address);
/**
* @dev Internal function to get the total payout of a given vesting NFT.
* @dev This is the total that will be paid out to the NFT owner, including historical tokens.
*
* @param tokenId to check
* @return uint256 the total payout of a given vesting NFT
*/
function _payout(uint256 tokenId) internal view virtual returns (uint256);
/**
* @dev Internal function to get the start time of a given vesting NFT
*
* @param tokenId to check
* @return uint256 the start time in epoch timestamp
*/
function _startTime(uint256 tokenId) internal view virtual returns (uint256);
/**
* @dev Internal function to get the end time of a given vesting NFT
*
* @param tokenId to check
* @return uint256 the end time in epoch timestamp
*/
function _endTime(uint256 tokenId) internal view virtual returns (uint256);
/**
* @dev Checks if an address is authorized to manage the given token ID.
* Used to verify if an address has the necessary permissions to execute actions on behalf of the token owner.
*
* @param owner the owner of the token
* @param spender the address attempting to act on the token
* @param tokenId the token ID to check for authorization
* @return bool true if the spender is authorized, false otherwise
*/
function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
return
spender != address(0) &&
(owner == spender || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
}
}// 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);
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Standard ERC-721 Errors * @notice This update is included in openzeppelin/[email protected], but not in openzeppelin/[email protected] * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens. * @custom:source https://github.com/OpenZeppelin/openzeppelin-contracts/blob/... * 0b343abcb5cecc42c40b95565cb7f5affb542727/contracts/interfaces/draft-IERC6093.sol#L55 */ interface IERC721Errors { /** * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20. * Used in balance queries. * @param owner Address of the current owner of a token. */ error ERC721InvalidOwner(address owner); /** * @dev Indicates a `tokenId` whose `owner` is the zero address. * @param tokenId Identifier number of a token. */ error ERC721NonexistentToken(uint256 tokenId); /** * @dev Indicates an error related to the ownership over a particular token. Used in transfers. * @param sender Address whose tokens are being transferred. * @param tokenId Identifier number of a token. * @param owner Address of the current owner of a token. */ error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC721InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC721InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param tokenId Identifier number of a token. */ error ERC721InsufficientApproval(address operator, uint256 tokenId); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC721InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC721InvalidOperator(address operator); }
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {IVotes} from "@openzeppelin/contracts/governance/utils/IVotes.sol";
import {Checkpoints} from "../libraries/Checkpoints.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {IERC721EnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol";
interface IVotingEscrowV2Upgradeable is IVotes, IERC721EnumerableUpgradeable {
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 NoLockFound();
error NotPermanentLock();
error PermanentLock();
error PermanentLockMismatch();
error SameNFT();
error SignatureExpired();
error ZeroAmount();
error LockHoldsValue();
error NotLockOwner();
function supply() external view returns (uint);
function token() external view returns (IERC20Upgradeable);
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 isApprovedOrOwner(address user, uint tokenId) external view returns (bool);
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)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {SafeCastLibrary} from "../libraries/SafeCastLibrary.sol";
import {Checkpoints} from "../libraries/Checkpoints.sol";
import {Time} from "../libraries/Time.sol";
/**
* @title CheckPointSystem
* @dev This contract is used to manage checkpoints in the system.
*/
library EscrowDelegateCheckpoints {
using Checkpoints for Checkpoints.Trace;
using Checkpoints for Checkpoints.TraceAddress;
using SafeCastLibrary for int128;
using SafeCastLibrary for uint256;
/// @notice Maximum time for a checkpoint
int128 public constant MAX_TIME = 2 * 365 * 86400;
/// @notice Unit of time for the clock
uint48 public constant CLOCK_UNIT = 7 days;
struct EscrowDelegateStore {
/// @notice Global checkpoints
Checkpoints.Trace _globalCheckpoints;
/// @notice Mapping of global slope changes
/// @dev Intended to be exposed with a getter
mapping(uint256 => int128) globalSlopeChanges;
/// @notice escrow lock checkpoints
mapping(uint256 => Checkpoints.Trace) _escrowCheckpoints;
/// @notice Delegate checkpoints
mapping(address => Checkpoints.Trace) _delegateCheckpoints;
/// @notice escrow lock to delegatee mapping
mapping(uint256 => Checkpoints.TraceAddress) _escrowDelegateeAddress;
/// @notice Delegatee slope changes
/// @dev Intended to be exposed with a getter
mapping(address => mapping(uint256 => int128)) delegateeSlopeChanges;
}
event CheckpointGlobal(uint48 timestamp, int128 slope, int128 bias, int128 permanent);
event CheckpointDelegate(address delegatee, uint48 timestamp, int128 slope, int128 bias, int128 permanent);
event CheckpointEscrow(uint256 escrowId, uint48 timestamp, int128 slope, int128 bias, int128 permanent);
/**
* @notice Clock used for flagging checkpoints.
* @return Current timestamp
*/
function clock() public view returns (uint48) {
return Time.timestamp();
}
/**
* @notice Clock used for flagging global checkpoints.
* @return Current timestamp rounded to the nearest clock unit
*/
function globalClock() public view returns (uint48) {
return toGlobalClock(Time.timestamp());
}
/**
* @notice Converts a timestamp to a global clock value.
* @param timestamp The timestamp to convert
* @return The converted global clock value
*/
function toGlobalClock(uint256 timestamp) internal pure returns (uint48) {
return uint48((timestamp / CLOCK_UNIT) * CLOCK_UNIT);
}
/**
* @dev Record global and per-escrow data to checkpoints. Used by VotingEscrow system.
* @param store_ The EscrowDelegateStore struct containing all the storage mappings.
* @param escrowId NFT escrow lock ID. No escrow checkpoint if 0
* @param uOldAmount Locked amount from last checkpoint
* @param uNewAmount Locked amount from current checkpoint
* @param uOldEndTime Last checkpoint time
* @param uNewEndTime Current checkpoint time
*/
function checkpoint(
EscrowDelegateStore storage store_,
uint256 escrowId,
int128 uOldAmount,
int128 uNewAmount,
uint256 uOldEndTime,
uint256 uNewEndTime
) external {
int128 oldDslope = 0;
int128 newDslope = 0;
Checkpoints.Point memory uOldPoint = Checkpoints.blankPoint();
Checkpoints.Point memory uNewPoint = Checkpoints.blankPoint();
/// @notice if this is not rounded to CLOCK_UNIT
/// the system will not be able to go too long without checkpoints
uNewEndTime = toGlobalClock(uNewEndTime);
if (escrowId != 0) {
// Calculate slopes and biases
// Kept at zero when they have to
uNewPoint.permanent = uNewEndTime == 0 ? uNewAmount : int128(0);
uOldPoint.permanent = uOldEndTime == 0 ? uOldAmount : int128(0);
if (uOldEndTime > block.timestamp && uOldAmount > 0) {
/// @dev Calculate the slope based on the older checkpoint amount
uOldPoint.slope = (uOldAmount) / MAX_TIME;
uOldPoint.bias = (uOldPoint.slope * (uOldEndTime - block.timestamp).toInt128());
}
if (uNewEndTime > block.timestamp && uNewAmount > 0) {
uNewPoint.slope = (uNewAmount) / MAX_TIME;
uNewPoint.bias = (uNewPoint.slope * (uNewEndTime - block.timestamp).toInt128());
}
oldDslope = store_.globalSlopeChanges[uOldEndTime];
if (uNewEndTime != 0) {
if (uNewEndTime == uOldEndTime) {
newDslope = oldDslope;
} else {
newDslope = store_.globalSlopeChanges[uNewEndTime];
}
}
// Schedule the slope changes (slope is going down)
// We subtract new escrow slope from [_newLocked.endTime]
// and add old_escrow_slope to [_oldLocked.end]
if (uOldEndTime > block.timestamp) {
// oldDslope was <something> - uOld.slope, so we cancel that
oldDslope += uOldPoint.slope;
if (uOldEndTime == uNewEndTime) {
oldDslope -= uNewPoint.slope; // It was a new deposit, not extension
}
store_.globalSlopeChanges[uOldEndTime] = oldDslope;
}
if (uNewEndTime > block.timestamp) {
// update slope if new lock is greater than old lock and is not permanent or if old lock is permanent
if ((uNewEndTime > uOldEndTime)) {
newDslope -= uNewPoint.slope; // old slope disappeared at this point
store_.globalSlopeChanges[uNewEndTime] = newDslope;
}
// else: we recorded it already in oldDslope
}
/// @dev Add the new point to the escrowId Checkpoints.Trace
_pushPointAtClock(store_._escrowCheckpoints[escrowId], uNewPoint);
emit CheckpointEscrow(escrowId, clock(), uNewPoint.slope, uNewPoint.bias, uNewPoint.permanent);
(, uint48 delegateTs, address delegateeAddress) = store_
._escrowDelegateeAddress[escrowId]
.latestCheckpoint();
if (delegateTs != 0) {
/// @notice this can likely be handled more efficiently
_checkpointDelegatee(store_, delegateeAddress, uOldPoint, uOldEndTime, false);
_checkpointDelegatee(store_, delegateeAddress, uNewPoint, uNewEndTime, true);
}
}
/// @dev If escrowId is 0, this will still create a global checkpoint
globalCheckpoint(store_, escrowId, uOldPoint, uNewPoint);
}
/**
* @dev Function to update global checkpoint
*/
function globalCheckpoint(EscrowDelegateStore storage store_) external {
globalCheckpoint(store_, 0, Checkpoints.blankPoint(), Checkpoints.blankPoint());
}
/**
* @dev Function to update global checkpoint with new points
* @param escrowId The ID of the escrow lock
* - If
* @param uOldPoint The old point to be updated
* @param uNewPoint The new point to be updated
*/
function globalCheckpoint(
EscrowDelegateStore storage store_,
uint256 escrowId,
Checkpoints.Point memory uOldPoint,
Checkpoints.Point memory uNewPoint
) public {
(, uint48 lastPoint, Checkpoints.Point memory lastGlobal) = store_._globalCheckpoints.latestCheckpoint();
uint48 lastCheckpoint = lastPoint != 0 ? lastPoint : uint48(block.timestamp);
{
// Go over weeks to fill history and calculate what the current point is
uint48 testTime = toGlobalClock(lastCheckpoint); /// @dev lastCheckpoint > tesTime
uint256 maxTime = testTime + MAX_TIME.toUint256();
while (testTime < block.timestamp) {
testTime += CLOCK_UNIT;
int128 dSlope = 0;
if (testTime > block.timestamp) {
testTime = block.timestamp.toUint48();
} else {
dSlope = store_.globalSlopeChanges[testTime];
}
if (dSlope != 0) {
lastGlobal.bias -= lastGlobal.slope * uint256(testTime - lastCheckpoint).toInt128();
lastGlobal.slope += dSlope;
if (lastGlobal.bias < 0) {
lastGlobal.bias = 0;
}
if (lastGlobal.slope < 0) {
lastGlobal.slope = 0;
}
lastCheckpoint = testTime;
store_._globalCheckpoints.push(lastCheckpoint, lastGlobal);
}
if (testTime > maxTime) break;
}
}
if (escrowId != 0) {
lastGlobal.bias = lastGlobal.bias - ((lastGlobal.slope * (block.timestamp - lastCheckpoint).toInt128()));
lastGlobal.slope += uNewPoint.slope - uOldPoint.slope;
lastGlobal.bias += uNewPoint.bias - uOldPoint.bias;
lastGlobal.permanent += uNewPoint.permanent - uOldPoint.permanent;
} else {
// Initial value of testTime is always larger than the ts of the last point
uint256 testTime = block.timestamp;
lastGlobal.bias -= (lastGlobal.slope * (testTime - lastCheckpoint).toInt128());
}
_pushPointAtClock(store_._globalCheckpoints, lastGlobal);
emit CheckpointGlobal(clock(), lastGlobal.slope, lastGlobal.bias, lastGlobal.permanent);
}
/**
* @dev Function to calculate total voting power at some point in the past
* @param _delegateeAddress The address of the delegatee
* @param timestamp Time to calculate the total voting power at
* @return Total voting power at that time
*/
function getAdjustedVotes(
EscrowDelegateStore storage store_,
address _delegateeAddress,
uint48 timestamp
) external view returns (uint256) {
Checkpoints.Point memory lastPoint = _getAdjustedVotesCheckpoint(store_, _delegateeAddress, timestamp);
return (lastPoint.bias + lastPoint.permanent).toUint256();
}
/**
* @dev Function to get delegated votes checkpoint at some point in the past
* @param _delegateeAddress The address of the delegatee
* @param timestamp Time to calculate the total voting power at
* @return Total voting power at that time
*/
function _getAdjustedVotesCheckpoint(
EscrowDelegateStore storage store_,
address _delegateeAddress,
uint48 timestamp
) internal view returns (Checkpoints.Point memory) {
(bool exists, uint48 lastCheckpointTs, Checkpoints.Point memory lastPoint) = store_
._delegateCheckpoints[_delegateeAddress]
.upperLookupRecent(timestamp);
if (!exists) return lastPoint;
uint48 testTime = toGlobalClock(lastCheckpointTs); /// @dev lastCheckpointTs > tesTime
uint256 maxTime = testTime + MAX_TIME.toUint256();
while (testTime < timestamp) {
testTime += CLOCK_UNIT;
int128 dSlope = 0;
if (testTime > timestamp) {
testTime = timestamp;
} else {
dSlope = store_.delegateeSlopeChanges[_delegateeAddress][testTime];
}
if (dSlope != 0) {
lastPoint.bias -= lastPoint.slope * uint256(testTime - lastCheckpointTs).toInt128();
lastPoint.slope += dSlope;
if (lastPoint.bias < 0) {
lastPoint.bias = 0;
}
if (lastPoint.slope < 0) {
lastPoint.slope = 0;
}
lastCheckpointTs = uint48(testTime);
}
if (testTime > maxTime) break;
}
int128 change = lastPoint.slope * uint256(timestamp - lastCheckpointTs).toInt128();
lastPoint.bias = lastPoint.bias < change ? int128(0) : lastPoint.bias - change;
return lastPoint;
}
/**
* @notice Public function to get the delegatee of an escrow lock
* @param escrowId The ID of the escrow
* @return The address of the delegate
*/
function getEscrowDelegatee(EscrowDelegateStore storage store_, uint256 escrowId) external view returns (address) {
return getEscrowDelegateeAtTime(store_, escrowId, block.timestamp.toUint48());
}
/**
* @notice Public function to get the delegatee of an escrow lock
* @param escrowId The ID of the escrow lock
* @param timestamp The timestamp to get the delegate at
* @return The address of the delegate
*/
function getEscrowDelegateeAtTime(
EscrowDelegateStore storage store_,
uint256 escrowId,
uint48 timestamp
) public view returns (address) {
return store_._escrowDelegateeAddress[escrowId].upperLookupRecent(timestamp);
}
/**
* @dev Function to record escrow delegation checkpoints. Used by voting system.
* @param escrowId The ID of the escrow lock
* @param delegatee The address of the delegatee
* @param endTime The end time of the delegation
*/
function delegate(
EscrowDelegateStore storage store_,
uint256 escrowId,
address delegatee,
uint256 endTime
) external returns (address oldDelegatee, address newDelegatee) {
oldDelegatee = store_._escrowDelegateeAddress[escrowId].latest();
if (oldDelegatee == delegatee) return (oldDelegatee, delegatee);
(, uint48 ts, Checkpoints.Point memory lastPoint) = store_._escrowCheckpoints[escrowId].latestCheckpoint();
lastPoint.bias -= ((lastPoint.slope * (block.timestamp - ts).toInt128()));
if (lastPoint.bias < 0) {
lastPoint.bias = 0;
}
if (oldDelegatee != delegatee && oldDelegatee != address(0)) {
_checkpointDelegatee(store_, oldDelegatee, lastPoint, endTime, false);
}
// Delegate to new delegator
_checkpointDelegatee(store_, delegatee, lastPoint, endTime, true);
_pushAddressAtClock(store_._escrowDelegateeAddress[escrowId], delegatee);
return (oldDelegatee, delegatee);
}
/**
* @dev Function to update delegatee's `delegatedBalance` by `balance`.
* Only updates if delegating to a new delegatee.
* @param delegateeAddress The address of the delegatee
* @param escrowPoint The point of the escrow
* @param endTime The end time of the delegation
* @param increase Whether to increase or decrease the balance
*/
function _checkpointDelegatee(
EscrowDelegateStore storage store_,
address delegateeAddress,
Checkpoints.Point memory escrowPoint,
uint256 endTime,
bool increase
) internal {
(Checkpoints.Point memory lastPoint, uint48 lastCheckpoint) = baseCheckpointDelegatee(store_, delegateeAddress);
int128 baseBias = lastPoint.bias - (lastPoint.slope * (block.timestamp - lastCheckpoint).toInt128());
if (!increase) {
if (endTime > block.timestamp) {
store_.delegateeSlopeChanges[delegateeAddress][endTime] += escrowPoint.slope;
lastPoint.slope = escrowPoint.slope < lastPoint.slope ? lastPoint.slope - escrowPoint.slope : int128(0);
}
lastPoint.bias = escrowPoint.bias < baseBias ? baseBias - escrowPoint.bias : int128(0);
lastPoint.permanent = escrowPoint.permanent < lastPoint.permanent
? lastPoint.permanent - escrowPoint.permanent
: int128(0);
} else {
if (endTime > block.timestamp) {
store_.delegateeSlopeChanges[delegateeAddress][endTime] -= escrowPoint.slope;
lastPoint.slope = lastPoint.slope + escrowPoint.slope;
}
lastPoint.bias = baseBias + escrowPoint.bias;
lastPoint.permanent = lastPoint.permanent + escrowPoint.permanent;
}
/// @dev bias can be rounded up by lack of precision. If slope is 0 we are out
if (lastPoint.slope == 0) {
lastPoint.bias = 0;
}
_pushPointAtClock(store_._delegateCheckpoints[delegateeAddress], lastPoint);
emit CheckpointDelegate(delegateeAddress, clock(), lastPoint.slope, lastPoint.bias, lastPoint.permanent);
}
/**
* @dev Function to update delegatee's checkpoint
* @param delegateeAddress The address of the delegatee
* @return lastPoint The last point of the delegatee
* @return lastCheckpoint The last checkpoint time of the delegatee
*/
function baseCheckpointDelegatee(
EscrowDelegateStore storage store_,
address delegateeAddress
) public returns (Checkpoints.Point memory lastPoint, uint48 lastCheckpoint) {
(bool exists, uint48 ts, Checkpoints.Point memory point) = store_
._delegateCheckpoints[delegateeAddress]
.latestCheckpoint();
lastPoint = point;
lastCheckpoint = ts;
if (exists) {
// Go over days to fill history and calculate what the current point is
uint48 testTime = toGlobalClock(lastCheckpoint); /// @dev lastCheckpoint > tesTime
uint256 maxTime = testTime + MAX_TIME.toUint256();
// Iterate over time until current block timestamp or maxtime
while (testTime < block.timestamp) {
testTime += CLOCK_UNIT;
int128 dSlope = 0;
if (testTime > block.timestamp) {
testTime = uint48(block.timestamp);
} else {
dSlope = store_.delegateeSlopeChanges[delegateeAddress][testTime];
}
if (dSlope != 0) {
lastPoint.bias -= lastPoint.slope * uint256(testTime - lastCheckpoint).toInt128();
lastPoint.slope += dSlope;
if (lastPoint.bias < 0) {
lastPoint.bias = 0;
}
if (lastPoint.slope < 0) {
lastPoint.slope = 0;
}
lastCheckpoint = uint48(testTime);
store_._delegateCheckpoints[delegateeAddress].push(lastCheckpoint, lastPoint);
}
if (testTime > maxTime) break;
}
}
emit CheckpointDelegate(delegateeAddress, clock(), lastPoint.slope, lastPoint.bias, lastPoint.permanent);
}
/**
* @dev Function to calculate total voting power at some point in the past
* @param timestamp Time to calculate the total voting power at
* @return Total voting power at that time
*/
function getAdjustedGlobalVotes(
EscrowDelegateStore storage store_,
uint48 timestamp
) external view returns (uint256) {
Checkpoints.Point memory lastPoint = _getAdjustedCheckpoint(store_, timestamp);
return (lastPoint.bias + lastPoint.permanent).toUint256();
}
/**
* @dev Function to get latest checkpoint of some point in the past
* @param timestamp Time to calculate the total voting power at
* @return Total voting power at that time
*/
function _getAdjustedCheckpoint(
EscrowDelegateStore storage store_,
uint48 timestamp
) internal view returns (Checkpoints.Point memory) {
uint48 clockTime = timestamp;
(bool exists, uint48 lastCheckpointTs, Checkpoints.Point memory lastGlobal) = store_
._globalCheckpoints
.upperLookupRecent(clockTime);
if (!exists) return lastGlobal;
uint48 testTime = toGlobalClock(lastCheckpointTs); /// @dev lastCheckpointTs > tesTime
uint256 maxTime = testTime + MAX_TIME.toUint256();
// Iterate over time until the specified timestamp or maxtime is reached
while (testTime < timestamp) {
testTime += CLOCK_UNIT;
int128 dSlope = 0;
if (testTime > timestamp) {
testTime = timestamp;
} else {
dSlope = store_.globalSlopeChanges[testTime];
}
if (dSlope != 0) {
lastGlobal.bias -= lastGlobal.slope * uint256(testTime - lastCheckpointTs).toInt128();
lastGlobal.slope += dSlope;
if (lastGlobal.bias < 0) {
lastGlobal.bias = 0;
}
if (lastGlobal.slope < 0) {
lastGlobal.slope = 0;
}
lastCheckpointTs = uint48(testTime);
}
if (testTime > maxTime) break;
}
int128 change = lastGlobal.slope * uint256(clockTime - lastCheckpointTs).toInt128();
lastGlobal.bias = lastGlobal.bias < change ? int128(0) : lastGlobal.bias - change;
return lastGlobal;
}
/**
* @notice Get the current bias for `escrowId` at `timestamp`
* @dev Adheres to the ERC20 `balanceOf` interface for Aragon compatibility
* @dev Fetches last escrow point prior to a certain timestamp, then walks forward to timestamp.
* @param escrowId NFT for lock
* @param timestamp Epoch time to return bias power at
* @return NFT bias
*/
function getAdjustedEscrowBias(
EscrowDelegateStore storage store_,
uint256 escrowId,
uint256 timestamp
) external view returns (uint256) {
uint48 clockTime = timestamp.toUint48();
(Checkpoints.Point memory lastPoint,) = getAdjustedEscrow(store_, escrowId, clockTime);
if (lastPoint.permanent != 0) return lastPoint.permanent.toUint256();
return lastPoint.bias.toUint256();
}
/**
* @notice Get the current bias for `escrowId` at `timestamp`
* @dev Adheres to the ERC20 `balanceOf` interface for Aragon compatibility
* @dev Fetches last escrow point prior to a certain timestamp, then walks forward to timestamp.
* @param escrowId NFT for lock
* @param timestamp Epoch time to return bias power at
* @return NFT bias
*/
function getAdjustedEscrow(
EscrowDelegateStore storage store_,
uint256 escrowId,
uint256 timestamp
) public view returns (Checkpoints.Point memory, uint48) {
uint48 clockTime = timestamp.toUint48();
(bool exists, uint48 ts, Checkpoints.Point memory lastPoint) = store_
._escrowCheckpoints[escrowId]
.upperLookupRecent(clockTime);
if (!exists) return (lastPoint, ts);
int128 change = ((lastPoint.slope * uint256(clockTime - ts).toInt128()));
lastPoint.bias = lastPoint.bias < change ? int128(0) : lastPoint.bias - change;
return (lastPoint, ts);
}
function getFirstEscrowPoint(
EscrowDelegateStore storage store_,
uint256 escrowId
) internal view returns (Checkpoints.Point memory, uint48) {
(, uint48 ts, Checkpoints.Point memory point) = store_._escrowCheckpoints[escrowId].firstCheckpoint();
return (point, ts);
}
/// -----------------------------------------------------------------------
/// Private functions
/// -----------------------------------------------------------------------
/**
* @dev Function to push an address to the checkpoint
* @param store The storage to push the address to
* @param value The address to be pushed
* @return The old and new address
*/
function _pushAddressAtClock(
Checkpoints.TraceAddress storage store,
address value
) private returns (address, address) {
return store.push(clock(), value);
}
/**
* @dev Function to push a struct to the checkpoint
* @param store The storage to push the struct to
* @param value The struct to be pushed
* @return The old and new struct
*/
function _pushPointAtClock(
Checkpoints.Trace storage store,
Checkpoints.Point memory value
) private returns (Checkpoints.Point memory, Checkpoints.Point memory) {
return store.push(clock(), value);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {EscrowDelegateCheckpoints} from "./EscrowDelegateCheckpoints.sol";
/**
* @title EscrowDelegateStorage
* @dev This contract serves as the storage for checkpoints in the system.
*/
contract EscrowDelegateStorage {
using EscrowDelegateCheckpoints for EscrowDelegateCheckpoints.EscrowDelegateStore;
/// @notice Storage struct for the checkpoint system
EscrowDelegateCheckpoints.EscrowDelegateStore internal edStore;
/// @dev Must be reset in initialization for upgradeability
uint256 MAX_TIME = uint256(uint128(EscrowDelegateCheckpoints.MAX_TIME));
/// @notice Gap for future upgrades
uint256[50] private __gap;
/// -----------------------------------------------------------------------
/// Getters
/// -----------------------------------------------------------------------
function globalSlopeChanges(uint256 _timestamp) external view returns (int128) {
return edStore.globalSlopeChanges[_timestamp];
}
function delegateeSlopeChanges(address _delegatee, uint256 _timestamp) external view returns (int128) {
return edStore.delegateeSlopeChanges[_delegatee][_timestamp];
}
function toGlobalClock(uint256 _timestamp) public pure virtual returns (uint48) {
return EscrowDelegateCheckpoints.toGlobalClock(_timestamp);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
/// @title SafeCast Library
/// @author velodrome.finance
/// @notice Safely convert unsigned and signed integers without overflow / underflow
library SafeCastLibrary {
error SafeCastOverflow();
error SafeCastUnderflow();
/// @dev Safely convert uint256 to int128
function toInt128(uint256 value) internal pure returns (int128) {
if (value > uint128(type(int128).max)) revert SafeCastOverflow();
return int128(uint128(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
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) revert SafeCastOverflow();
return uint48(value);
}
/// @dev Safely convert int128 to uint256
function toUint256(int128 value) internal pure returns (uint256) {
if (value < 0) revert SafeCastUnderflow();
return uint256(int256(value));
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {SafeCastLibrary} from "./SafeCastLibrary.sol";
/**
* @notice Adapted from OpenZeppelin's Time library: v5.0.0 for solc 0.8.13
* @dev This library provides helpers for manipulating time-related objects.
*
* It uses the following types:
* - `uint48` for timepoints
* - `uint32` for durations
*
* While the library doesn't provide specific types for timepoints and duration, it does provide:
* - a `Delay` type to represent duration that can be programmed to change value automatically at a given point
* - additional helper functions
*/
library Time {
using Time for *;
/**
* @dev Get the block timestamp as a Timepoint.
*/
function timestamp() internal view returns (uint48) {
return SafeCastLibrary.toUint48(block.timestamp);
}
/**
* @dev Get the block number as a Timepoint.
*/
function blockNumber() internal view returns (uint48) {
return SafeCastLibrary.toUint48(block.number);
}
}{
"optimizer": {
"enabled": true,
"runs": 1000
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {
"contracts/VoterV5/VotingEscrow/libraries/EscrowDelegateCheckpoints.sol": {
"EscrowDelegateCheckpoints": "0xa615388bd2f920ee2fec7606f26908b454f0c249"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyVoted","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721IncorrectOwner","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721InsufficientApproval","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC721InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"ERC721InvalidOperator","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721InvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC721InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC721InvalidSender","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonexistentToken","type":"error"},{"inputs":[],"name":"InvalidDelegatee","type":"error"},{"inputs":[],"name":"InvalidNonce","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidSignatureS","type":"error"},{"inputs":[],"name":"InvalidWeights","type":"error"},{"inputs":[],"name":"LockDurationNotInFuture","type":"error"},{"inputs":[],"name":"LockDurationTooLong","type":"error"},{"inputs":[],"name":"LockExpired","type":"error"},{"inputs":[],"name":"LockHoldsValue","type":"error"},{"inputs":[],"name":"LockNotExpired","type":"error"},{"inputs":[],"name":"NoLockFound","type":"error"},{"inputs":[],"name":"NotLockOwner","type":"error"},{"inputs":[],"name":"NotPermanentLock","type":"error"},{"inputs":[],"name":"PermanentLock","type":"error"},{"inputs":[],"name":"PermanentLockMismatch","type":"error"},{"inputs":[],"name":"SafeCastOverflow","type":"error"},{"inputs":[],"name":"SameNFT","type":"error"},{"inputs":[],"name":"SignatureExpired","type":"error"},{"inputs":[{"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"VotesExpiredSignature","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ClaimApproval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ClaimApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"LockAmountIncreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unlockTime","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isPermanent","type":"bool"}],"name":"LockCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"LockDelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newUnlockTime","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isPermanent","type":"bool"}],"name":"LockDurationExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unlockTime","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isPermanent","type":"bool"}],"name":"LockMerged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"splitWeights","type":"uint256[]"},{"indexed":true,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"LockSplit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unlockTime","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isPermanent","type":"bool"}],"name":"LockUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimAmount","type":"uint256"}],"name":"PayoutClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newSupply","type":"uint256"}],"name":"SupplyUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"unlockTime","type":"uint256"}],"name":"UnlockPermanent","type":"event"},{"inputs":[],"name":"DELEGATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"_lockDetails","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bool","name":"isPermanent","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_token","outputs":[{"internalType":"contract IERC20Upgradeable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"artProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"balanceOfNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"balanceOfNFTAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_delegateeAddress","type":"address"}],"name":"checkpointDelegatee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"claimablePayout","outputs":[{"internalType":"uint256","name":"payout","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"claimedPayout","outputs":[{"internalType":"uint256","name":"payout","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lockDuration","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"address","name":"_delegatee","type":"address"},{"internalType":"bool","name":"_permanent","type":"bool"}],"name":"createDelegatedLockFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lockDuration","type":"uint256"},{"internalType":"bool","name":"_permanent","type":"bool"}],"name":"createLock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lockDuration","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"bool","name":"_permanent","type":"bool"}],"name":"createLockFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_delegatee","type":"address"},{"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"delegateeSlopeChanges","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint48","name":"timestamp","type":"uint48"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountDelegates","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getClaimApproved","outputs":[{"internalType":"address","name":"operator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getFirstEscrowPoint","outputs":[{"components":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"int128","name":"permanent","type":"int128"}],"internalType":"struct Checkpoints.Point","name":"","type":"tuple"},{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getLockDelegatee","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"getPastEscrowPoint","outputs":[{"components":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"int128","name":"permanent","type":"int128"}],"internalType":"struct Checkpoints.Point","name":"","type":"tuple"},{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_timePoint","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"timepoint","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalCheckpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"globalSlopeChanges","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"increaseAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_lockDuration","type":"uint256"},{"internalType":"bool","name":"_permanent","type":"bool"}],"name":"increaseUnlockTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"contract IERC20Upgradeable","name":"mainToken","type":"address"},{"internalType":"address","name":"_artProxy","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"isApprovedClaimOrOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"isApprovedOrOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isClaimApprovedForAll","outputs":[{"internalType":"bool","name":"isClaimApproved","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"lockDetails","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bool","name":"isPermanent","type":"bool"}],"internalType":"struct IVotingEscrowV2Upgradeable.LockDetails","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_from","type":"uint256"},{"internalType":"uint256","name":"_to","type":"uint256"}],"name":"merge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"payoutToken","outputs":[{"internalType":"address","name":"token","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"setClaimApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setClaimApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_weights","type":"uint256[]"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"split","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"supported","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"toGlobalClock","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20Upgradeable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalNftsMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"unlockPermanent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"vestedPayout","outputs":[{"internalType":"uint256","name":"payout","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"vestedPayoutAtTime","outputs":[{"internalType":"uint256","name":"payout","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"vestingPayout","outputs":[{"internalType":"uint256","name":"payout","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"vestingPeriod","outputs":[{"internalType":"uint256","name":"vestingStart","type":"uint256"},{"internalType":"uint256","name":"vestingEnd","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60806040526303c26700610104556000610171553480156200002057600080fd5b506200002b62000041565b600161016b556200003b62000041565b62000102565b600054610100900460ff1615620000ae5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff9081161462000100576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b615f4780620001126000396000f3fe608060405234801561001057600080fd5b506004361061048d5760003560e01c80637ecebe001161026b578063c7d7d90f11610150578063db900b9d116100c8578063e7e242d411610097578063ecd0c0c31161007c578063ecd0c0c314610b72578063f778e0a314610b86578063fc0c546a14610b9957600080fd5b8063e7e242d414610b23578063e985e9c514610b3657600080fd5b8063db900b9d14610ac3578063e0514aba14610ad6578063e58ee17d14610ae9578063e7a324dc14610afc57600080fd5b8063d1c2babb1161011f578063d6d0faee11610104578063d6d0faee14610a7d578063d744515f14610a90578063d93aafb514610aa357600080fd5b8063d1c2babb14610a57578063d60371a714610a6a57600080fd5b8063c7d7d90f1461099c578063c87b56dd146109f7578063cae57c8914610a0a578063d113cfba14610a4457600080fd5b80639e0bd808116101e3578063b2383e55116101b2578063bb7cfce211610197578063bb7cfce21461094b578063c2c4c5c114610981578063c3cda5201461098957600080fd5b8063b2383e5514610925578063b88d4fde1461093857600080fd5b80639e0bd808146108b05780639f194422146108c3578063a22cb465146108d6578063a2e8a50c146108e957600080fd5b806384e968e61161023a5780638e539e8c1161021f5780638e539e8c1461088257806395d89b41146108955780639ab24eb01461089d57600080fd5b806384e968e61461085c5780638b9cb90b1461086f57600080fd5b80637ecebe00146107e4578063807c35311461080557806381d0526d1461082e57806384b0196e1461084157600080fd5b80633a46b1a811610391578063587cde1e116103095780636bec7397116102d857806370a08231116102bd57806370a08231146107b457806372fbaacb146107c7578063753c7328146107d157600080fd5b80636bec7397146107555780636c423c101461076857600080fd5b8063587cde1e146106d45780635c19a95c146106e757806363185237146106fa5780636352211e1461074257600080fd5b8063430c2081116103605780635594a045116103455780635594a0451461068557806356afe74414610699578063576561d2146106ac57600080fd5b8063430c20811461065f5780634f6ccce71461067257600080fd5b80633a46b1a8146106135780633c340b321461062657806342842e0e1461063957806342966c681461064c57600080fd5b80631ac40e66116104245780632f745c59116103f3578063323418c1116103d8578063323418c1146105da57806335b0f6bd146105ed578063379607f51461060057600080fd5b80632f745c59146105ad578063313ce567146105c057600080fd5b80631ac40e6614610555578063212d18c91461055d57806323b872dd146105875780632b3b09ec1461059a57600080fd5b806308bbb8241161046057806308bbb82414610512578063095ea7b3146105275780630a2abdb31461053a57806318160ddd1461054d57600080fd5b806301ffc9a714610492578063047fc9aa146104ba57806306fdde03146104d2578063081812fc146104e7575b600080fd5b6104a56104a036600461548a565b610bab565b60405190151581526020015b60405180910390f35b6104c461016d5481565b6040519081526020016104b1565b6104da610bef565b6040516104b191906154ff565b6104fa6104f5366004615512565b610c81565b6040516001600160a01b0390911681526020016104b1565b610525610520366004615540565b610ca8565b005b610525610535366004615570565b610e37565b6104c46105483660046155aa565b610f68565b6104c4610f96565b61052561102f565b61057061056b366004615512565b6110c0565b60405165ffffffffffff90911681526020016104b1565b6105256105953660046155f4565b6110cb565b6104fa6105a8366004615649565b611142565b6104c46105bb366004615570565b6111ee565b6105c8601281565b60405160ff90911681526020016104b1565b6105256105e836600461566e565b611296565b6105256105fb366004615512565b61130b565b61052561060e366004615512565b611522565b6104c4610621366004615570565b61152b565b6104c461063436600461569c565b6115d1565b6105256106473660046155f4565b611600565b61052561065a366004615512565b61161b565b6104a561066d366004615570565b6116bb565b6104c4610680366004615512565b6116d0565b61016e546104fa906001600160a01b031681565b6105256106a7366004615742565b611774565b6106bf6106ba366004615512565b611ad2565b604080519283526020830191909152016104b1565b6104fa6106e23660046157ee565b611b45565b6105256106f53660046157ee565b611c5f565b61070d610708366004615512565b611c69565b6040516104b1919081518152602080830151908201526040808301519082015260609182015115159181019190915260800190565b6104fa610750366004615512565b611ce1565b6104a5610763366004615570565b611d46565b61077b61077636600461580b565b611dce565b604080518351600f90810b8252602080860151820b908301529382015190930b9083015265ffffffffffff1660608201526080016104b1565b6104c46107c23660046157ee565b611e94565b6104c46101715481565b6105256107df3660046157ee565b611f2e565b6104c46107f23660046157ee565b61016f6020526000908152604090205481565b6104fa610813366004615512565b600090815260ca60205260409020546001600160a01b031690565b6104c461083c366004615512565b611fdf565b61084961203e565b6040516104b19796959493929190615868565b6104c461086a366004615512565b612102565b6104fa61087d366004615512565b612152565b6104c4610890366004615512565b61219f565b6104da612234565b6104c46108ab3660046157ee565b612243565b6104c46108be366004615512565b6122ac565b6105256108d13660046158f2565b6122ff565b6105256108e436600461566e565b6124d2565b6104a56108f736600461592b565b6001600160a01b03918216600090815260cb6020908152604080832093909416825291909152205460ff1690565b61052561093336600461580b565b6124dd565b6105256109463660046159bf565b6125e5565b61096e610959366004615512565b600090815260ff6020526040902054600f0b90565b604051600f9190910b81526020016104b1565b610525612663565b610525610997366004615a3f565b61266b565b6109d56109aa366004615512565b6101706020526000908152604090208054600182015460028301546003909301549192909160ff1684565b60408051948552602085019390935291830152151560608201526080016104b1565b6104da610a05366004615512565b6126b3565b61096e610a18366004615570565b6001600160a01b03919091166000908152610103602090815260408083209383529290522054600f0b90565b610525610a52366004615aa1565b6127d9565b610525610a6536600461580b565b612875565b6104c4610a783660046158f2565b612c64565b610525610a8b366004615af1565b612c8a565b6104c4610a9e36600461580b565b612df6565b610ab6610ab13660046157ee565b612e6e565b6040516104b19190615b93565b6104c4610ad1366004615512565b612f9e565b6104c4610ae436600461580b565b612faa565b6104fa610af7366004615512565b612ff3565b6104c47fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf81565b6104c4610b31366004615512565b613071565b6104a5610b4436600461592b565b6001600160a01b039182166000908152606a6020908152604080832093909416825291909152205460ff1690565b61016c546104fa906001600160a01b031681565b61077b610b94366004615512565b6130b9565b61016c546001600160a01b03166104fa565b60006001600160e01b031982167fddf0aba2000000000000000000000000000000000000000000000000000000001480610be95750610be9826130ea565b92915050565b606060658054610bfe90615be0565b80601f0160208091040260200160405190810160405280929190818152602001828054610c2a90615be0565b8015610c775780601f10610c4c57610100808354040283529160200191610c77565b820191906000526020600020905b815481529060010190602001808311610c5a57829003601f168201915b5050505050905090565b6000610c8c82613128565b506000908152606960205260409020546001600160a01b031690565b60008281526067602052604090205482906001600160a01b031680610ce857604051637e27328960e01b8152600481018390526024015b60405180910390fd5b33610cf482828561318c565b610d235760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610cdf565b60008581526101706020526040808220600201549051639bdd2e4d60e01b815260fe6004820152602481018890526001600160a01b03871660448201526064810191909152819073a615388bd2f920ee2fec7606f26908b454f0c24990639bdd2e4d906084016040805180830381865af4158015610da5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc99190615c1a565b91509150806001600160a01b0316610dde3390565b6001600160a01b0316887f39d7dc46c82aa27d4484a57e21232e5c6f22fe6c7ffc01795304084ba872811e85604051610e2691906001600160a01b0391909116815260200190565b60405180910390a450505050505050565b6000610e4282611ce1565b9050806001600160a01b0316836001600160a01b031603610ecb5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f72000000000000000000000000000000000000000000000000000000000000006064820152608401610cdf565b336001600160a01b0382161480610ee75750610ee78133610b44565b610f595760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c0000006064820152608401610cdf565b610f6383836131ff565b505050565b6000610f7261326d565b610f81858585868660016132c8565b9050610f8e600161016b55565b949350505050565b600073a615388bd2f920ee2fec7606f26908b454f0c2496334e78a9e60fe610fbd4261353e565b6040516001600160e01b031960e085901b168152600481019290925265ffffffffffff166024820152604401602060405180830381865af4158015611006573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061102a9190615c49565b905090565b61103761326d565b6040517f6977e3a900000000000000000000000000000000000000000000000000000000815260fe600482015273a615388bd2f920ee2fec7606f26908b454f0c24990636977e3a99060240160006040518083038186803b15801561109b57600080fd5b505af41580156110af573d6000803e3d6000fd5b505050506110be600161016b55565b565b6000610be98261356b565b6110d53382613585565b6111375760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201526c1c881bdc88185c1c1c9bdd9959609a1b6064820152608401610cdf565b610f638383836135f1565b6040517f9511aa5300000000000000000000000000000000000000000000000000000000815260fe60048201526024810183905265ffffffffffff8216604482015260009073a615388bd2f920ee2fec7606f26908b454f0c24990639511aa5390606401602060405180830381865af41580156111c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e79190615c62565b9392505050565b60006111f983611e94565b821061126d5760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201527f74206f6620626f756e64730000000000000000000000000000000000000000006064820152608401610cdf565b506001600160a01b03919091166000908152609760209081526040808320938352929052205490565b33600090815260cb602090815260408083206001600160a01b03861684529091529020805460ff191682151517905560405181151581526001600160a01b0383169033907f199acb87f54d9aa34d4c071192d94f5ec4e0b152f52b81209d89085dd7dade7e9060200160405180910390a35050565b61131361326d565b60008181526067602052604090205481906001600160a01b03168061134e57604051637e27328960e01b815260048101839052602401610cdf565b3361135a82828561318c565b6113895760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610cdf565b6000848152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff16151560608201819052611408576040517f2188f8ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61141a610104544261056b9190615c95565b65ffffffffffff166040808301919091526000606080840182905287825261017060209081529183902083516080810185528154815260018201549381019390935260028101549383019390935260039092015460ff16151591810191909152611486908690836137f7565b600085815261017060209081526040918290208351815590830151600182015590820151600282015560608201516003909101805460ff1916911515919091179055336001600160a01b0316857f7fbde6b5a06f47dab128f45cea79f339e710e4f5d9e5cda522585afe7b30539b836040015160405161150891815260200190565b60405180910390a35050505061151f600161016b55565b50565b61151f816138b1565b600073a615388bd2f920ee2fec7606f26908b454f0c24963f8183ecf60fe856115538661353e565b6040516001600160e01b031960e086901b16815260048101939093526001600160a01b03909116602483015265ffffffffffff1660448201526064015b602060405180830381865af41580156115ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e79190615c49565b60006115db61326d565b6115ea868686868660016132c8565b90506115f7600161016b55565b95945050505050565b610f63838383604051806020016040528060008152506125e5565b6000818152606760205260409020546001600160a01b0316331461166b576040517f8e5693a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526101706020526040902054156116b2576040517f61442e5e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61151f81613bab565b60006111e76116c983611ce1565b848461318c565b60006116db60995490565b821061174f5760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201527f7574206f6620626f756e647300000000000000000000000000000000000000006064820152608401610cdf565b6099828154811061176257611762615cad565b90600052602060002001549050919050565b61177c61326d565b60008181526067602052604090205481906001600160a01b0316806117b757604051637e27328960e01b815260048101839052602401610cdf565b336117c382828561318c565b6117f25760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610cdf565b6000848152610170602081815260408084208151608081018352815481526001820154818501526002820154928101928352600382015460ff16151560608201529489905292909152514290811080159061184f57508260600151155b1561186d576040516307b7d7dd60e51b815260040160405180910390fd5b8251158061187c575060028851105b1561189a57604051631f2a200560e01b815260040160405180910390fd5b825161016d80546000906118af908490615cc3565b90915550506000878152606760205260408120546001600160a01b031690805b8a51811015611911578a81815181106118ea576118ea615cad565b6020026020010151826118fd9190615c95565b91508061190981615cda565b9150506118cf565b508060000361194c576040517f84677ce800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000856060015161197e5783866040015111611969576000611981565b8386604001516119799190615cc3565b611981565b60005b865190915060005b8c51811015611a80576000848e83815181106119a7576119a7615cad565b60200260200101518a600001516119be9190615cf3565b6119c89190615d12565b905060018e516119d89190615cc3565b82036119e15750815b6119eb8184615cc3565b925081600003611a585780885561016d8054829190600090611a0e908490615c95565b9091555050604080516080810182528954815260018a0154602082015260028a015491810191909152600389015460ff1615156060820152611a53908e908b906137f7565b611a6d565b611a6b818588898d6060015160056132c8565b505b5080611a7881615cda565b915050611989565b508a7fef2d6e0b68eee68a522999443f2c077264967f8dec5d69a61161668da51663f58d604051611ab19190615d34565b60405180910390a250505050505050505050611ace600161016b55565b5050565b600080826000611af7826000908152606760205260409020546001600160a01b031690565b90506001600160a01b038116611b2357604051637e27328960e01b815260048101839052602401610cdf565b5050506000918252506101706020526040902060018101546002909101549091565b60008080611b5284611e94565b9050600081603211611b65576032611b67565b815b905060005b81811015611c55576000611b8087836111ee565b60405163f83bb31360e01b815260fe60048201526024810182905290915060009073a615388bd2f920ee2fec7606f26908b454f0c2499063f83bb31390604401602060405180830381865af4158015611bdd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c019190615c62565b90506001600160a01b038616611c1957809550611c40565b806001600160a01b0316866001600160a01b031614611c4057506001979650505050505050565b50508080611c4d90615cda565b915050611b6c565b5091949350505050565b61151f3382613c4e565b611c9660405180608001604052806000815260200160008152602001600081526020016000151581525090565b506000908152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff161515606082015290565b6000818152606760205260408120546001600160a01b031680610be95760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e20494400000000000000006044820152606401610cdf565b600080611d5283611ce1565b9050806001600160a01b0316846001600160a01b03161480611d9957506001600160a01b03808216600090815260cb602090815260408083209388168352929052205460ff165b80610f8e5750600083815260ca60205260409020546001600160a01b0380861691165b6001600160a01b031614949350505050565b60408051606081018252600080825260208201819052918101919091526040517fe680a31000000000000000000000000000000000000000000000000000000000815260fe6004820152602481018490526044810183905260009073a615388bd2f920ee2fec7606f26908b454f0c2499063e680a31090606401608060405180830381865af4158015611e65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e899190615d5e565b915091509250929050565b60006001600160a01b038216611f125760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f74206120766160448201527f6c6964206f776e657200000000000000000000000000000000000000000000006064820152608401610cdf565b506001600160a01b031660009081526068602052604090205490565b611f3661326d565b6040517fd7ac26da00000000000000000000000000000000000000000000000000000000815260fe60048201526001600160a01b038216602482015273a615388bd2f920ee2fec7606f26908b454f0c2499063d7ac26da90604401608060405180830381865af4158015611fae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fd29190615d5e565b505061151f600161016b55565b60008181526067602052604081205482906001600160a01b03168061201a57604051637e27328960e01b815260048101839052602401610cdf565b61202384612f9e565b600085815261017060205260409020545b610f8e9190615cc3565b600060608060008060006060610137546000801b148015612060575061013854155b6120ac5760405162461bcd60e51b815260206004820152601560248201527f4549503731323a20556e696e697469616c697a656400000000000000000000006044820152606401610cdf565b6120b4613e37565b6120bc613e47565b604080516000808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009b939a50919850469750309650945092509050565b60008181526067602052604081205482906001600160a01b03168061213d57604051637e27328960e01b815260048101839052602401610cdf565b505050600090815260c9602052604090205490565b60008181526067602052604081205482906001600160a01b03168061218d57604051637e27328960e01b815260048101839052602401610cdf565b61016c546001600160a01b0316610f8e565b600073a615388bd2f920ee2fec7606f26908b454f0c2496334e78a9e60fe6121c68561353e565b6040516001600160e01b031960e085901b168152600481019290925265ffffffffffff1660248201526044015b602060405180830381865af4158015612210573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be99190615c49565b606060668054610bfe90615be0565b600073a615388bd2f920ee2fec7606f26908b454f0c24963f8183ecf60fe8461226b4261353e565b6040516001600160e01b031960e086901b16815260048101939093526001600160a01b03909116602483015265ffffffffffff1660448201526064016121f3565b60008181526067602052604081205482906001600160a01b0316806122e757604051637e27328960e01b815260048101839052602401610cdf565b600084815260c9602052604090205461203485612f9e565b61230761326d565b60008381526067602052604090205483906001600160a01b03168061234257604051637e27328960e01b815260048101839052602401610cdf565b3361234e82828561318c565b61237d5760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610cdf565b6000868152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff1615801560608301526123e2576040516334d10f9560e11b815260040160405180910390fd5b600085612476576123f661056b8842615c95565b65ffffffffffff16905042826040015111612424576040516307b7d7dd60e51b815260040160405180910390fd5b8160400151811161244857604051638e6b5b6760e01b815260040160405180910390fd5b610104546124569042615c95565b8111156124765760405163f761f1cd60e01b815260040160405180910390fd5b61248688600083858a6003613e57565b60408051828152871515602082015289917fa239317f70ee4cfafe56d2bc53cac05379afea1286f6088d963d7a02da99a75d910160405180910390a25050505050610f63600161016b55565b611ace338383614012565b6124e561326d565b8060000361250657604051631f2a200560e01b815260040160405180910390fd5b60008281526101706020908152604080832081516080810183528154815260018201548185015260028201548184015260039091015460ff16151560608201528584526067909252909120546001600160a01b0316612591576040517ff90e998d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b428160400151111580156125a757508060600151155b156125c5576040516307b7d7dd60e51b815260040160405180910390fd5b6125d9838360008485606001516002613e57565b50611ace600161016b55565b6125ef3383613585565b6126515760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201526c1c881bdc88185c1c1c9bdd9959609a1b6064820152608401610cdf565b61265d848484846140e0565b50505050565b6110be61102f565b60405162461bcd60e51b815260206004820152601760248201527f64656c656761746542795369673a2073697a65206375740000000000000000006044820152606401610cdf565b60008181526067602052604090205460609082906001600160a01b0316806126f157604051637e27328960e01b815260048101839052602401610cdf565b6000848152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff161515606082015261016e546001600160a01b031663dd9ec1498661275481613071565b604080860151865191516001600160e01b031960e087901b1681526127949493929060040193845260208401929092526040830152606082015260800190565b600060405180830381865afa1580156127b1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115f79190810190615de5565b60008181526067602052604090205481906001600160a01b03168061281457604051637e27328960e01b815260048101839052602401610cdf565b61281e858461415e565b82856001600160a01b0316336001600160a01b03167fb688daf266707251b810cde26b1bedfbbc6aa98cfc5667ea84fc354483e2ac8e87604051612866911515815260200190565b60405180910390a45050505050565b61287d61326d565b60008281526067602052604090205482906001600160a01b0316806128b857604051637e27328960e01b815260048101839052602401610cdf565b336128c482828561318c565b6128f35760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610cdf565b60008481526067602052604090205484906001600160a01b03168061292e57604051637e27328960e01b815260048101839052602401610cdf565b3361293a82828561318c565b6129695760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610cdf565b8688036129a2576040517f93b50ef200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600087815261017060209081526040808320815160808101835281548082526001830154948201949094526002820154928101929092526003015460ff16151560608201529103612a0657604051631f2a200560e01b815260040160405180910390fd5b42816040015111158015612a1c57508060600151155b15612a3a576040516307b7d7dd60e51b815260040160405180910390fd5b600089815261017060209081526040808320815160808101835281548082526001830154948201949094526002820154928101929092526003015460ff16151560608201529103612a9e57604051631f2a200560e01b815260040160405180910390fd5b606081015115156001148015612ac05750816060015115158160600151151514155b15612af7576040517f67fd3ad900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000826040015182604001511015612b13578260400151612b19565b81604001515b60008c81526101706020908152604080832083815581516080810183529384526001810154928401929092526002820154908301526003015460ff1615156060820152909150612b6c908c9084906137f7565b612b9960405180608001604052806000815260200160008152602001600081526020016000151581525090565b82518451612ba79190615c95565b81526060808501511515908201819052612bc357604081018290525b612bce8b85836137f7565b60008b8152610170602090815260409182902083518082558483015160018301558484015160028301556060808601516003909301805460ff191693151593841790558451918252928101869052928301528c918e917fec3b82c6fa54397270e9474a17cd38c92b766ae2b2541f87715357ac7ddd48e0910160405180910390a350505050505050505050611ace600161016b55565b6000612c6e61326d565b612c7d848433338660016132c8565b90506111e7600161016b55565b600054610100900460ff1615808015612caa5750600054600160ff909116105b80612cc45750303b158015612cc4575060005460ff166001145b612d365760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610cdf565b6000805460ff191660011790558015612d59576000805461ff0019166101001790555b612d6386866141fd565b612d6d8685614328565b61016c80546001600160a01b038086166001600160a01b03199283161790925561016e8054928516929091169190911790556303c26700610104558015612dee576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b60008281526067602052604081205483906001600160a01b031680612e3157604051637e27328960e01b815260048101839052602401610cdf565b600085815261017060205260409020600201548410612e6157600085815261017060205260409020549250612e66565b600092505b505092915050565b60606000612e7b83611e94565b905060008167ffffffffffffffff811115612e9857612e986156fb565b604051908082528060200260200182016040528015612ec1578160200160208202803683370190505b50905060005b82811015612f96576000612edb86836111ee565b60405163f83bb31360e01b815260fe60048201526024810182905290915073a615388bd2f920ee2fec7606f26908b454f0c2499063f83bb31390604401602060405180830381865af4158015612f35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f599190615c62565b838381518110612f6b57612f6b615cad565b6001600160a01b03909216602092830291909101909101525080612f8e81615cda565b915050612ec7565b509392505050565b6000610be98242612df6565b604051633d003cf160e21b815260fe6004820152602481018390526044810182905260009073a615388bd2f920ee2fec7606f26908b454f0c2499063f400f3c490606401611590565b60405163f83bb31360e01b815260fe60048201526024810182905260009073a615388bd2f920ee2fec7606f26908b454f0c2499063f83bb31390604401602060405180830381865af415801561304d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be99190615c62565b604051633d003cf160e21b815260fe60048201526024810182905242604482015260009073a615388bd2f920ee2fec7606f26908b454f0c2499063f400f3c4906064016121f3565b60408051606081018252600080825260208201819052918101829052906130e160fe8461439d565b91509150915091565b60006001600160e01b031982167fbd3a202b000000000000000000000000000000000000000000000000000000001480610be95750610be9826143e4565b6000818152606760205260409020546001600160a01b031661151f5760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e20494400000000000000006044820152606401610cdf565b60006001600160a01b03831615801590610f8e5750826001600160a01b0316846001600160a01b031614806131e657506001600160a01b038085166000908152606a602090815260408083209387168352929052205460ff165b80610f8e5750826001600160a01b0316611dbc83610c81565b600081815260696020526040902080546001600160a01b0319166001600160a01b038416908117909155819061323482611ce1565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600261016b54036132c05760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610cdf565b600261016b55565b6000866000036132eb57604051631f2a200560e01b815260040160405180910390fd5b6101718054600091826132fd83615cda565b909155505061017154846133705761331861056b8942615c95565b65ffffffffffff16915042821161334257604051638e6b5b6760e01b815260040160405180910390fd5b610104546133509042615c95565b8211156133705760405163f761f1cd60e01b815260040160405180910390fd5b61337a8782614422565b6000818152610170602090815260409182902042600182018190558351608081018552825481529283015260028101549282019290925260039091015460ff16151560608201526133d29082908b9085908989613e57565b604051639bdd2e4d60e01b815260fe6004820152602481018290526001600160a01b03871660448201526064810183905273a615388bd2f920ee2fec7606f26908b454f0c24990639bdd2e4d906084016040805180830381865af415801561343e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134629190615c1a565b5050604080518a8152602081018490528615158183015290516001600160a01b0388169183917f551141c76a3b7e9a59ed1e32ccc940d667b98cfd98931126e89f1a17838f43519181900360600190a36040516001600160a01b03808816916000918a16907f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f908390a4604051600081526001600160a01b03808816919089169083907f39d7dc46c82aa27d4484a57e21232e5c6f22fe6c7ffc01795304084ba872811e9060200160405180910390a498975050505050505050565b600065ffffffffffff821115613567576040516393dafdf160e01b815260040160405180910390fd5b5090565b600062093a8061357b8184615d12565b610be99190615cf3565b60008061359183611ce1565b9050806001600160a01b0316846001600160a01b031614806135d857506001600160a01b038082166000908152606a602090815260408083209388168352929052205460ff165b80610f8e5750836001600160a01b0316611dbc84610c81565b826001600160a01b031661360482611ce1565b6001600160a01b0316146136685760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610cdf565b6001600160a01b0382166136e35760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610cdf565b6136f0838383600161443c565b826001600160a01b031661370382611ce1565b6001600160a01b0316146137675760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610cdf565b600081815260696020908152604080832080546001600160a01b03199081169091556001600160a01b0387811680865260688552838620805460001901905590871680865283862080546001019055868652606790945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b60fe73a615388bd2f920ee2fec7606f26908b454f0c249634cd2605690918561382386600001516145d2565b855161382e906145d2565b6040808901518882015191516001600160e01b031960e089901b16815260048101969096526024860194909452600f92830b6044860152910b6064840152608483019190915260a482015260c40160006040518083038186803b15801561389457600080fd5b505af41580156138a8573d6000803e3d6000fd5b50505050505050565b60008181526067602052604090205481906001600160a01b0316806138ec57604051637e27328960e01b815260048101839052602401610cdf565b6138f461326d565b60008381526067602052604090205483906001600160a01b03168061392f57604051637e27328960e01b815260048101839052602401610cdf565b3361393b82828561318c565b61396a5760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610cdf565b6000868152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff1615801560608301526139cf576040516334d10f9560e11b815260040160405180910390fd5b60006139da886122ac565b905080600003613a16576040517f6855a80200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160808101825260008082526020808301828152838501838152606085018481528e8552610170909352948320935184555160018401559251600283015591516003909101805460ff191691151591909117905561016d805491839190613a808385615cc3565b90915550506000898152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff1615156060820152613ad9908a9085906137f7565b60405182815233908a907fe97cee5a4c0549d3fdc81e322b718ddf0aeb3418ec87dce4f9a7fb28d117c3129060200160405180910390a3600089815260c9602052604081208054849290613b2e908490615c95565b90915550613b5c90503383613b4c61016c546001600160a01b031690565b6001600160a01b03169190614605565b61016d546040805183815260208101929092527f313edf9a07fc8449830af260b03eb5d8ffd3e7fc45a0c71cc26775972a4669a3910160405180910390a1505050505050610f63600161016b55565b6000613bb682611ce1565b9050613bc681600084600161443c565b613bcf82611ce1565b600083815260696020908152604080832080546001600160a01b03199081169091556001600160a01b0385168085526068845282852080546000190190558785526067909352818420805490911690555192935084927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b613c5661326d565b6000613c6183611e94565b90506000805b82811015613ddf576000613c7b86836111ee565b60008181526101706020526040808220600201549051639bdd2e4d60e01b815260fe6004820152602481018490526001600160a01b0389166044820152606481019190915291925090819073a615388bd2f920ee2fec7606f26908b454f0c24990639bdd2e4d906084016040805180830381865af4158015613d01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d259190615c1a565b91509150806001600160a01b0316886001600160a01b0316847f39d7dc46c82aa27d4484a57e21232e5c6f22fe6c7ffc01795304084ba872811e85604051613d7c91906001600160a01b0391909116815260200190565b60405180910390a46001600160a01b038516613d9a57819450613dc9565b6001600160a01b038516600114613dc957816001600160a01b0316856001600160a01b031614613dc957600194505b5050508080613dd790615cda565b915050613c67565b50826001600160a01b0316816001600160a01b0316856001600160a01b03167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f60405160405180910390a45050611ace600161016b55565b60606101398054610bfe90615be0565b606061013a8054610bfe90615be0565b61016d80549086906000613e6b8385615c95565b9091555050604080516080810182526000808252602080830182815283850183815260608086019485528a51938b0151968b0151908b01511515909452929092529290528181529087908290613ec2908390615c95565b9052508515801590613ed2575083155b15613edf57604081018690525b8315613ef45760006040820152600160608201525b60008881526101706020908152604091829020835181558382015160018201558383015160028201556060808501516003909201805460ff19169215159290921790915582518a81529182018990528615158284015291518a927fca0c517aab9ec63932670fd23484369811c8412142cc2535c9f78f008ecd0ce1928290030190a2613f818886836137f7565b8615801590613fa257506005836005811115613f9f57613f9f615e53565b14155b15613fc057613fc03361016c546001600160a01b031690308a614696565b61016d547f313edf9a07fc8449830af260b03eb5d8ffd3e7fc45a0c71cc26775972a4669a390613ff08985615c95565b6040805192835260208301919091520160405180910390a15050505050505050565b816001600160a01b0316836001600160a01b0316036140735760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610cdf565b6001600160a01b038381166000818152606a6020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6140eb8484846135f1565b6140f7848484846146e7565b61265d5760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b6064820152608401610cdf565b3361416882611ce1565b6001600160a01b0316146141cf57338161418183611ce1565b6040517f64283d7b0000000000000000000000000000000000000000000000000000000081526001600160a01b03938416600482015260248101929092529091166044820152606401610cdf565b600090815260ca6020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b600054610100900460ff161580801561421d5750600054600160ff909116105b806142375750303b158015614237575060005460ff166001145b6142a95760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610cdf565b6000805460ff1916600117905580156142cc576000805461ff0019166101001790555b6142d68383614830565b6142de6148a5565b8015610f63576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050565b600054610100900460ff166143935760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401610cdf565b611ace8282614910565b604080516060810182526000808252602082018190529181019190915260008281526002840160205260408120819081906143d7906149b5565b9890975095505050505050565b60006001600160e01b031982167f780e9d63000000000000000000000000000000000000000000000000000000001480610be95750610be982614aa7565b611ace828260405180602001604052806000815250614b42565b61444884848484614bc0565b60005b818110156145cb57600061445f8285615c95565b9050846001600160a01b0316866001600160a01b0316146145b85760008181526101706020526040808220600201549051639bdd2e4d60e01b815260fe6004820152602481018490526001600160a01b03881660448201526064810191909152819073a615388bd2f920ee2fec7606f26908b454f0c24990639bdd2e4d906084016040805180830381865af41580156144fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145209190615c1a565b91509150806001600160a01b0316826001600160a01b0316886001600160a01b03167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f60405160405180910390a46040516001600160a01b038381168252808316919089169085907f39d7dc46c82aa27d4484a57e21232e5c6f22fe6c7ffc01795304084ba872811e9060200160405180910390a450505b50806145c381615cda565b91505061444b565b5050505050565b60006f7fffffffffffffffffffffffffffffff821115613567576040516393dafdf160e01b815260040160405180910390fd5b6040516001600160a01b038316602482015260448101829052610f639084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b031990931692909217909152614c43565b6040516001600160a01b038085166024830152831660448201526064810182905261265d9085907f23b872dd000000000000000000000000000000000000000000000000000000009060840161464a565b60006001600160a01b0384163b1561482857604051630a85bd0160e11b81526001600160a01b0385169063150b7a029061472b903390899088908890600401615e69565b6020604051808303816000875af1925050508015614766575060408051601f3d908101601f1916820190925261476391810190615ea5565b60015b61480e573d808015614794576040519150601f19603f3d011682016040523d82523d6000602084013e614799565b606091505b5080516000036148065760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b6064820152608401610cdf565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610f8e565b506001610f8e565b600054610100900460ff1661489b5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401610cdf565b611ace8282614d2b565b600054610100900460ff166110be5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401610cdf565b600054610100900460ff1661497b5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401610cdf565b815161498f906101399060208501906153e4565b5080516149a49061013a9060208401906153e4565b505060006101378190556101385550565b60408051606081018252600080825260208201819052918101829052819083546000819003614a2557600080614a19604080516060808201835260008083526020808401829052928401819052835191820184528082529181018290529182015290565b93509350935050614aa0565b6000614a318682614dbd565b604080518082018252825465ffffffffffff1681528151606081018352600180850154600f81810b8452700100000000000000000000000000000000909104810b602084810191909152600290960154900b938201939093529281018390525190965094509250614aa0915050565b9193909250565b60006001600160e01b031982167f80ac58cd000000000000000000000000000000000000000000000000000000001480614b0a57506001600160e01b031982167f5b5e139f00000000000000000000000000000000000000000000000000000000145b80610be957507f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b0319831614610be9565b614b4c8383614de7565b614b5960008484846146e7565b610f635760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b6064820152608401610cdf565b614bcc84848484614f80565b60005b818110156145cb576000614be38285615c95565b90506001600160a01b038616151580614c0e5750846001600160a01b0316866001600160a01b031614155b15614c3057600081815260ca6020526040902080546001600160a01b03191690555b5080614c3b81615cda565b915050614bcf565b6000614c98826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166150b59092919063ffffffff16565b9050805160001480614cb9575080806020019051810190614cb99190615ec2565b610f635760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610cdf565b600054610100900460ff16614d965760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401610cdf565b8151614da99060659060208501906153e4565b508051610f639060669060208401906153e4565b6000828281548110614dd157614dd1615cad565b9060005260206000209060030201905092915050565b6001600160a01b038216614e3d5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610cdf565b6000818152606760205260409020546001600160a01b031615614ea25760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610cdf565b614eb060008383600161443c565b6000818152606760205260409020546001600160a01b031615614f155760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610cdf565b6001600160a01b038216600081815260686020908152604080832080546001019055848352606790915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6001811115614ff75760405162461bcd60e51b815260206004820152603560248201527f455243373231456e756d657261626c653a20636f6e736563757469766520747260448201527f616e7366657273206e6f7420737570706f7274656400000000000000000000006064820152608401610cdf565b816001600160a01b0385166150535761504e81609980546000838152609a60205260408120829055600182018355919091527f72a152ddfb8e864297c917af52ea6c1c68aead0fee1a62673fcc7e0c94979d000155565b615076565b836001600160a01b0316856001600160a01b0316146150765761507685826150c4565b6001600160a01b0384166150925761508d81615161565b6145cb565b846001600160a01b0316846001600160a01b0316146145cb576145cb8482615210565b6060610f8e8484600085615254565b600060016150d184611e94565b6150db9190615cc3565b60008381526098602052604090205490915080821461512e576001600160a01b03841660009081526097602090815260408083208584528252808320548484528184208190558352609890915290208190555b5060009182526098602090815260408084208490556001600160a01b039094168352609781528383209183525290812055565b60995460009061517390600190615cc3565b6000838152609a60205260408120546099805493945090928490811061519b5761519b615cad565b9060005260206000200154905080609983815481106151bc576151bc615cad565b6000918252602080832090910192909255828152609a909152604080822084905585825281205560998054806151f4576151f4615edf565b6001900381819060005260206000200160009055905550505050565b600061521b83611e94565b6001600160a01b039093166000908152609760209081526040808320868452825280832085905593825260989052919091209190915550565b6060824710156152cc5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610cdf565b600080866001600160a01b031685876040516152e89190615ef5565b60006040518083038185875af1925050503d8060008114615325576040519150601f19603f3d011682016040523d82523d6000602084013e61532a565b606091505b509150915061533b87838387615346565b979650505050505050565b606083156153b55782516000036153ae576001600160a01b0385163b6153ae5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610cdf565b5081610f8e565b610f8e83838151156153ca5781518083602001fd5b8060405162461bcd60e51b8152600401610cdf91906154ff565b8280546153f090615be0565b90600052602060002090601f0160209004810192826154125760008555615458565b82601f1061542b57805160ff1916838001178555615458565b82800160010185558215615458579182015b8281111561545857825182559160200191906001019061543d565b506135679291505b808211156135675760008155600101615460565b6001600160e01b03198116811461151f57600080fd5b60006020828403121561549c57600080fd5b81356111e781615474565b60005b838110156154c25781810151838201526020016154aa565b8381111561265d5750506000910152565b600081518084526154eb8160208601602086016154a7565b601f01601f19169290920160200192915050565b6020815260006111e760208301846154d3565b60006020828403121561552457600080fd5b5035919050565b6001600160a01b038116811461151f57600080fd5b6000806040838503121561555357600080fd5b8235915060208301356155658161552b565b809150509250929050565b6000806040838503121561558357600080fd5b823561558e8161552b565b946020939093013593505050565b801515811461151f57600080fd5b600080600080608085870312156155c057600080fd5b843593506020850135925060408501356155d98161552b565b915060608501356155e98161559c565b939692955090935050565b60008060006060848603121561560957600080fd5b83356156148161552b565b925060208401356156248161552b565b929592945050506040919091013590565b65ffffffffffff8116811461151f57600080fd5b6000806040838503121561565c57600080fd5b82359150602083013561556581615635565b6000806040838503121561568157600080fd5b823561568c8161552b565b915060208301356155658161559c565b600080600080600060a086880312156156b457600080fd5b853594506020860135935060408601356156cd8161552b565b925060608601356156dd8161552b565b915060808601356156ed8161559c565b809150509295509295909350565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561573a5761573a6156fb565b604052919050565b6000806040838503121561575557600080fd5b823567ffffffffffffffff8082111561576d57600080fd5b818501915085601f83011261578157600080fd5b8135602082821115615795576157956156fb565b8160051b92506157a6818401615711565b82815292840181019281810190898511156157c057600080fd5b948201945b848610156157de578535825294820194908201906157c5565b9997909101359750505050505050565b60006020828403121561580057600080fd5b81356111e78161552b565b6000806040838503121561581e57600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b8381101561585d57815187529582019590820190600101615841565b509495945050505050565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e0602082015260006158a360e08301896154d3565b82810360408401526158b581896154d3565b90508660608401526001600160a01b03861660808401528460a084015282810360c08401526158e4818561582d565b9a9950505050505050505050565b60008060006060848603121561590757600080fd5b833592506020840135915060408401356159208161559c565b809150509250925092565b6000806040838503121561593e57600080fd5b82356159498161552b565b915060208301356155658161552b565b600067ffffffffffffffff821115615973576159736156fb565b50601f01601f191660200190565b600061599461598f84615959565b615711565b90508281528383830111156159a857600080fd5b828260208301376000602084830101529392505050565b600080600080608085870312156159d557600080fd5b84356159e08161552b565b935060208501356159f08161552b565b925060408501359150606085013567ffffffffffffffff811115615a1357600080fd5b8501601f81018713615a2457600080fd5b615a3387823560208401615981565b91505092959194509250565b60008060008060008060c08789031215615a5857600080fd5b8635615a638161552b565b95506020870135945060408701359350606087013560ff81168114615a8757600080fd5b9598949750929560808101359460a0909101359350915050565b600080600060608486031215615ab657600080fd5b8335615ac18161552b565b925060208401356156248161559c565b600082601f830112615ae257600080fd5b6111e783833560208501615981565b600080600080600060a08688031215615b0957600080fd5b853567ffffffffffffffff80821115615b2157600080fd5b615b2d89838a01615ad1565b96506020880135915080821115615b4357600080fd5b615b4f89838a01615ad1565b95506040880135915080821115615b6557600080fd5b50615b7288828901615ad1565b9350506060860135615b838161552b565b915060808601356156ed8161552b565b6020808252825182820181905260009190848201906040850190845b81811015615bd45783516001600160a01b031683529284019291840191600101615baf565b50909695505050505050565b600181811c90821680615bf457607f821691505b602082108103615c1457634e487b7160e01b600052602260045260246000fd5b50919050565b60008060408385031215615c2d57600080fd5b8251615c388161552b565b60208401519092506155658161552b565b600060208284031215615c5b57600080fd5b5051919050565b600060208284031215615c7457600080fd5b81516111e78161552b565b634e487b7160e01b600052601160045260246000fd5b60008219821115615ca857615ca8615c7f565b500190565b634e487b7160e01b600052603260045260246000fd5b600082821015615cd557615cd5615c7f565b500390565b600060018201615cec57615cec615c7f565b5060010190565b6000816000190483118215151615615d0d57615d0d615c7f565b500290565b600082615d2f57634e487b7160e01b600052601260045260246000fd5b500490565b6020815260006111e7602083018461582d565b8051600f81900b8114615d5957600080fd5b919050565b6000808284036080811215615d7257600080fd5b6060811215615d8057600080fd5b506040516060810181811067ffffffffffffffff82111715615da457615da46156fb565b604052615db084615d47565b8152615dbe60208501615d47565b6020820152615dcf60408501615d47565b6040820152606084015190925061556581615635565b600060208284031215615df757600080fd5b815167ffffffffffffffff811115615e0e57600080fd5b8201601f81018413615e1f57600080fd5b8051615e2d61598f82615959565b818152856020838501011115615e4257600080fd5b6115f78260208301602086016154a7565b634e487b7160e01b600052602160045260246000fd5b60006001600160a01b03808716835280861660208401525083604083015260806060830152615e9b60808301846154d3565b9695505050505050565b600060208284031215615eb757600080fd5b81516111e781615474565b600060208284031215615ed457600080fd5b81516111e78161559c565b634e487b7160e01b600052603160045260246000fd5b60008251615f078184602087016154a7565b919091019291505056fea26469706673582212207c41d260adabcc3ca693a2f348ba09e3532b0220fd8c1572f4a3ac5e149ce61a64736f6c634300080d0033
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061048d5760003560e01c80637ecebe001161026b578063c7d7d90f11610150578063db900b9d116100c8578063e7e242d411610097578063ecd0c0c31161007c578063ecd0c0c314610b72578063f778e0a314610b86578063fc0c546a14610b9957600080fd5b8063e7e242d414610b23578063e985e9c514610b3657600080fd5b8063db900b9d14610ac3578063e0514aba14610ad6578063e58ee17d14610ae9578063e7a324dc14610afc57600080fd5b8063d1c2babb1161011f578063d6d0faee11610104578063d6d0faee14610a7d578063d744515f14610a90578063d93aafb514610aa357600080fd5b8063d1c2babb14610a57578063d60371a714610a6a57600080fd5b8063c7d7d90f1461099c578063c87b56dd146109f7578063cae57c8914610a0a578063d113cfba14610a4457600080fd5b80639e0bd808116101e3578063b2383e55116101b2578063bb7cfce211610197578063bb7cfce21461094b578063c2c4c5c114610981578063c3cda5201461098957600080fd5b8063b2383e5514610925578063b88d4fde1461093857600080fd5b80639e0bd808146108b05780639f194422146108c3578063a22cb465146108d6578063a2e8a50c146108e957600080fd5b806384e968e61161023a5780638e539e8c1161021f5780638e539e8c1461088257806395d89b41146108955780639ab24eb01461089d57600080fd5b806384e968e61461085c5780638b9cb90b1461086f57600080fd5b80637ecebe00146107e4578063807c35311461080557806381d0526d1461082e57806384b0196e1461084157600080fd5b80633a46b1a811610391578063587cde1e116103095780636bec7397116102d857806370a08231116102bd57806370a08231146107b457806372fbaacb146107c7578063753c7328146107d157600080fd5b80636bec7397146107555780636c423c101461076857600080fd5b8063587cde1e146106d45780635c19a95c146106e757806363185237146106fa5780636352211e1461074257600080fd5b8063430c2081116103605780635594a045116103455780635594a0451461068557806356afe74414610699578063576561d2146106ac57600080fd5b8063430c20811461065f5780634f6ccce71461067257600080fd5b80633a46b1a8146106135780633c340b321461062657806342842e0e1461063957806342966c681461064c57600080fd5b80631ac40e66116104245780632f745c59116103f3578063323418c1116103d8578063323418c1146105da57806335b0f6bd146105ed578063379607f51461060057600080fd5b80632f745c59146105ad578063313ce567146105c057600080fd5b80631ac40e6614610555578063212d18c91461055d57806323b872dd146105875780632b3b09ec1461059a57600080fd5b806308bbb8241161046057806308bbb82414610512578063095ea7b3146105275780630a2abdb31461053a57806318160ddd1461054d57600080fd5b806301ffc9a714610492578063047fc9aa146104ba57806306fdde03146104d2578063081812fc146104e7575b600080fd5b6104a56104a036600461548a565b610bab565b60405190151581526020015b60405180910390f35b6104c461016d5481565b6040519081526020016104b1565b6104da610bef565b6040516104b191906154ff565b6104fa6104f5366004615512565b610c81565b6040516001600160a01b0390911681526020016104b1565b610525610520366004615540565b610ca8565b005b610525610535366004615570565b610e37565b6104c46105483660046155aa565b610f68565b6104c4610f96565b61052561102f565b61057061056b366004615512565b6110c0565b60405165ffffffffffff90911681526020016104b1565b6105256105953660046155f4565b6110cb565b6104fa6105a8366004615649565b611142565b6104c46105bb366004615570565b6111ee565b6105c8601281565b60405160ff90911681526020016104b1565b6105256105e836600461566e565b611296565b6105256105fb366004615512565b61130b565b61052561060e366004615512565b611522565b6104c4610621366004615570565b61152b565b6104c461063436600461569c565b6115d1565b6105256106473660046155f4565b611600565b61052561065a366004615512565b61161b565b6104a561066d366004615570565b6116bb565b6104c4610680366004615512565b6116d0565b61016e546104fa906001600160a01b031681565b6105256106a7366004615742565b611774565b6106bf6106ba366004615512565b611ad2565b604080519283526020830191909152016104b1565b6104fa6106e23660046157ee565b611b45565b6105256106f53660046157ee565b611c5f565b61070d610708366004615512565b611c69565b6040516104b1919081518152602080830151908201526040808301519082015260609182015115159181019190915260800190565b6104fa610750366004615512565b611ce1565b6104a5610763366004615570565b611d46565b61077b61077636600461580b565b611dce565b604080518351600f90810b8252602080860151820b908301529382015190930b9083015265ffffffffffff1660608201526080016104b1565b6104c46107c23660046157ee565b611e94565b6104c46101715481565b6105256107df3660046157ee565b611f2e565b6104c46107f23660046157ee565b61016f6020526000908152604090205481565b6104fa610813366004615512565b600090815260ca60205260409020546001600160a01b031690565b6104c461083c366004615512565b611fdf565b61084961203e565b6040516104b19796959493929190615868565b6104c461086a366004615512565b612102565b6104fa61087d366004615512565b612152565b6104c4610890366004615512565b61219f565b6104da612234565b6104c46108ab3660046157ee565b612243565b6104c46108be366004615512565b6122ac565b6105256108d13660046158f2565b6122ff565b6105256108e436600461566e565b6124d2565b6104a56108f736600461592b565b6001600160a01b03918216600090815260cb6020908152604080832093909416825291909152205460ff1690565b61052561093336600461580b565b6124dd565b6105256109463660046159bf565b6125e5565b61096e610959366004615512565b600090815260ff6020526040902054600f0b90565b604051600f9190910b81526020016104b1565b610525612663565b610525610997366004615a3f565b61266b565b6109d56109aa366004615512565b6101706020526000908152604090208054600182015460028301546003909301549192909160ff1684565b60408051948552602085019390935291830152151560608201526080016104b1565b6104da610a05366004615512565b6126b3565b61096e610a18366004615570565b6001600160a01b03919091166000908152610103602090815260408083209383529290522054600f0b90565b610525610a52366004615aa1565b6127d9565b610525610a6536600461580b565b612875565b6104c4610a783660046158f2565b612c64565b610525610a8b366004615af1565b612c8a565b6104c4610a9e36600461580b565b612df6565b610ab6610ab13660046157ee565b612e6e565b6040516104b19190615b93565b6104c4610ad1366004615512565b612f9e565b6104c4610ae436600461580b565b612faa565b6104fa610af7366004615512565b612ff3565b6104c47fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf81565b6104c4610b31366004615512565b613071565b6104a5610b4436600461592b565b6001600160a01b039182166000908152606a6020908152604080832093909416825291909152205460ff1690565b61016c546104fa906001600160a01b031681565b61077b610b94366004615512565b6130b9565b61016c546001600160a01b03166104fa565b60006001600160e01b031982167fddf0aba2000000000000000000000000000000000000000000000000000000001480610be95750610be9826130ea565b92915050565b606060658054610bfe90615be0565b80601f0160208091040260200160405190810160405280929190818152602001828054610c2a90615be0565b8015610c775780601f10610c4c57610100808354040283529160200191610c77565b820191906000526020600020905b815481529060010190602001808311610c5a57829003601f168201915b5050505050905090565b6000610c8c82613128565b506000908152606960205260409020546001600160a01b031690565b60008281526067602052604090205482906001600160a01b031680610ce857604051637e27328960e01b8152600481018390526024015b60405180910390fd5b33610cf482828561318c565b610d235760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610cdf565b60008581526101706020526040808220600201549051639bdd2e4d60e01b815260fe6004820152602481018890526001600160a01b03871660448201526064810191909152819073a615388bd2f920ee2fec7606f26908b454f0c24990639bdd2e4d906084016040805180830381865af4158015610da5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc99190615c1a565b91509150806001600160a01b0316610dde3390565b6001600160a01b0316887f39d7dc46c82aa27d4484a57e21232e5c6f22fe6c7ffc01795304084ba872811e85604051610e2691906001600160a01b0391909116815260200190565b60405180910390a450505050505050565b6000610e4282611ce1565b9050806001600160a01b0316836001600160a01b031603610ecb5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f72000000000000000000000000000000000000000000000000000000000000006064820152608401610cdf565b336001600160a01b0382161480610ee75750610ee78133610b44565b610f595760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c0000006064820152608401610cdf565b610f6383836131ff565b505050565b6000610f7261326d565b610f81858585868660016132c8565b9050610f8e600161016b55565b949350505050565b600073a615388bd2f920ee2fec7606f26908b454f0c2496334e78a9e60fe610fbd4261353e565b6040516001600160e01b031960e085901b168152600481019290925265ffffffffffff166024820152604401602060405180830381865af4158015611006573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061102a9190615c49565b905090565b61103761326d565b6040517f6977e3a900000000000000000000000000000000000000000000000000000000815260fe600482015273a615388bd2f920ee2fec7606f26908b454f0c24990636977e3a99060240160006040518083038186803b15801561109b57600080fd5b505af41580156110af573d6000803e3d6000fd5b505050506110be600161016b55565b565b6000610be98261356b565b6110d53382613585565b6111375760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201526c1c881bdc88185c1c1c9bdd9959609a1b6064820152608401610cdf565b610f638383836135f1565b6040517f9511aa5300000000000000000000000000000000000000000000000000000000815260fe60048201526024810183905265ffffffffffff8216604482015260009073a615388bd2f920ee2fec7606f26908b454f0c24990639511aa5390606401602060405180830381865af41580156111c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e79190615c62565b9392505050565b60006111f983611e94565b821061126d5760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201527f74206f6620626f756e64730000000000000000000000000000000000000000006064820152608401610cdf565b506001600160a01b03919091166000908152609760209081526040808320938352929052205490565b33600090815260cb602090815260408083206001600160a01b03861684529091529020805460ff191682151517905560405181151581526001600160a01b0383169033907f199acb87f54d9aa34d4c071192d94f5ec4e0b152f52b81209d89085dd7dade7e9060200160405180910390a35050565b61131361326d565b60008181526067602052604090205481906001600160a01b03168061134e57604051637e27328960e01b815260048101839052602401610cdf565b3361135a82828561318c565b6113895760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610cdf565b6000848152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff16151560608201819052611408576040517f2188f8ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61141a610104544261056b9190615c95565b65ffffffffffff166040808301919091526000606080840182905287825261017060209081529183902083516080810185528154815260018201549381019390935260028101549383019390935260039092015460ff16151591810191909152611486908690836137f7565b600085815261017060209081526040918290208351815590830151600182015590820151600282015560608201516003909101805460ff1916911515919091179055336001600160a01b0316857f7fbde6b5a06f47dab128f45cea79f339e710e4f5d9e5cda522585afe7b30539b836040015160405161150891815260200190565b60405180910390a35050505061151f600161016b55565b50565b61151f816138b1565b600073a615388bd2f920ee2fec7606f26908b454f0c24963f8183ecf60fe856115538661353e565b6040516001600160e01b031960e086901b16815260048101939093526001600160a01b03909116602483015265ffffffffffff1660448201526064015b602060405180830381865af41580156115ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e79190615c49565b60006115db61326d565b6115ea868686868660016132c8565b90506115f7600161016b55565b95945050505050565b610f63838383604051806020016040528060008152506125e5565b6000818152606760205260409020546001600160a01b0316331461166b576040517f8e5693a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526101706020526040902054156116b2576040517f61442e5e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61151f81613bab565b60006111e76116c983611ce1565b848461318c565b60006116db60995490565b821061174f5760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201527f7574206f6620626f756e647300000000000000000000000000000000000000006064820152608401610cdf565b6099828154811061176257611762615cad565b90600052602060002001549050919050565b61177c61326d565b60008181526067602052604090205481906001600160a01b0316806117b757604051637e27328960e01b815260048101839052602401610cdf565b336117c382828561318c565b6117f25760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610cdf565b6000848152610170602081815260408084208151608081018352815481526001820154818501526002820154928101928352600382015460ff16151560608201529489905292909152514290811080159061184f57508260600151155b1561186d576040516307b7d7dd60e51b815260040160405180910390fd5b8251158061187c575060028851105b1561189a57604051631f2a200560e01b815260040160405180910390fd5b825161016d80546000906118af908490615cc3565b90915550506000878152606760205260408120546001600160a01b031690805b8a51811015611911578a81815181106118ea576118ea615cad565b6020026020010151826118fd9190615c95565b91508061190981615cda565b9150506118cf565b508060000361194c576040517f84677ce800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000856060015161197e5783866040015111611969576000611981565b8386604001516119799190615cc3565b611981565b60005b865190915060005b8c51811015611a80576000848e83815181106119a7576119a7615cad565b60200260200101518a600001516119be9190615cf3565b6119c89190615d12565b905060018e516119d89190615cc3565b82036119e15750815b6119eb8184615cc3565b925081600003611a585780885561016d8054829190600090611a0e908490615c95565b9091555050604080516080810182528954815260018a0154602082015260028a015491810191909152600389015460ff1615156060820152611a53908e908b906137f7565b611a6d565b611a6b818588898d6060015160056132c8565b505b5080611a7881615cda565b915050611989565b508a7fef2d6e0b68eee68a522999443f2c077264967f8dec5d69a61161668da51663f58d604051611ab19190615d34565b60405180910390a250505050505050505050611ace600161016b55565b5050565b600080826000611af7826000908152606760205260409020546001600160a01b031690565b90506001600160a01b038116611b2357604051637e27328960e01b815260048101839052602401610cdf565b5050506000918252506101706020526040902060018101546002909101549091565b60008080611b5284611e94565b9050600081603211611b65576032611b67565b815b905060005b81811015611c55576000611b8087836111ee565b60405163f83bb31360e01b815260fe60048201526024810182905290915060009073a615388bd2f920ee2fec7606f26908b454f0c2499063f83bb31390604401602060405180830381865af4158015611bdd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c019190615c62565b90506001600160a01b038616611c1957809550611c40565b806001600160a01b0316866001600160a01b031614611c4057506001979650505050505050565b50508080611c4d90615cda565b915050611b6c565b5091949350505050565b61151f3382613c4e565b611c9660405180608001604052806000815260200160008152602001600081526020016000151581525090565b506000908152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff161515606082015290565b6000818152606760205260408120546001600160a01b031680610be95760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e20494400000000000000006044820152606401610cdf565b600080611d5283611ce1565b9050806001600160a01b0316846001600160a01b03161480611d9957506001600160a01b03808216600090815260cb602090815260408083209388168352929052205460ff165b80610f8e5750600083815260ca60205260409020546001600160a01b0380861691165b6001600160a01b031614949350505050565b60408051606081018252600080825260208201819052918101919091526040517fe680a31000000000000000000000000000000000000000000000000000000000815260fe6004820152602481018490526044810183905260009073a615388bd2f920ee2fec7606f26908b454f0c2499063e680a31090606401608060405180830381865af4158015611e65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e899190615d5e565b915091509250929050565b60006001600160a01b038216611f125760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f74206120766160448201527f6c6964206f776e657200000000000000000000000000000000000000000000006064820152608401610cdf565b506001600160a01b031660009081526068602052604090205490565b611f3661326d565b6040517fd7ac26da00000000000000000000000000000000000000000000000000000000815260fe60048201526001600160a01b038216602482015273a615388bd2f920ee2fec7606f26908b454f0c2499063d7ac26da90604401608060405180830381865af4158015611fae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fd29190615d5e565b505061151f600161016b55565b60008181526067602052604081205482906001600160a01b03168061201a57604051637e27328960e01b815260048101839052602401610cdf565b61202384612f9e565b600085815261017060205260409020545b610f8e9190615cc3565b600060608060008060006060610137546000801b148015612060575061013854155b6120ac5760405162461bcd60e51b815260206004820152601560248201527f4549503731323a20556e696e697469616c697a656400000000000000000000006044820152606401610cdf565b6120b4613e37565b6120bc613e47565b604080516000808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009b939a50919850469750309650945092509050565b60008181526067602052604081205482906001600160a01b03168061213d57604051637e27328960e01b815260048101839052602401610cdf565b505050600090815260c9602052604090205490565b60008181526067602052604081205482906001600160a01b03168061218d57604051637e27328960e01b815260048101839052602401610cdf565b61016c546001600160a01b0316610f8e565b600073a615388bd2f920ee2fec7606f26908b454f0c2496334e78a9e60fe6121c68561353e565b6040516001600160e01b031960e085901b168152600481019290925265ffffffffffff1660248201526044015b602060405180830381865af4158015612210573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be99190615c49565b606060668054610bfe90615be0565b600073a615388bd2f920ee2fec7606f26908b454f0c24963f8183ecf60fe8461226b4261353e565b6040516001600160e01b031960e086901b16815260048101939093526001600160a01b03909116602483015265ffffffffffff1660448201526064016121f3565b60008181526067602052604081205482906001600160a01b0316806122e757604051637e27328960e01b815260048101839052602401610cdf565b600084815260c9602052604090205461203485612f9e565b61230761326d565b60008381526067602052604090205483906001600160a01b03168061234257604051637e27328960e01b815260048101839052602401610cdf565b3361234e82828561318c565b61237d5760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610cdf565b6000868152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff1615801560608301526123e2576040516334d10f9560e11b815260040160405180910390fd5b600085612476576123f661056b8842615c95565b65ffffffffffff16905042826040015111612424576040516307b7d7dd60e51b815260040160405180910390fd5b8160400151811161244857604051638e6b5b6760e01b815260040160405180910390fd5b610104546124569042615c95565b8111156124765760405163f761f1cd60e01b815260040160405180910390fd5b61248688600083858a6003613e57565b60408051828152871515602082015289917fa239317f70ee4cfafe56d2bc53cac05379afea1286f6088d963d7a02da99a75d910160405180910390a25050505050610f63600161016b55565b611ace338383614012565b6124e561326d565b8060000361250657604051631f2a200560e01b815260040160405180910390fd5b60008281526101706020908152604080832081516080810183528154815260018201548185015260028201548184015260039091015460ff16151560608201528584526067909252909120546001600160a01b0316612591576040517ff90e998d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b428160400151111580156125a757508060600151155b156125c5576040516307b7d7dd60e51b815260040160405180910390fd5b6125d9838360008485606001516002613e57565b50611ace600161016b55565b6125ef3383613585565b6126515760405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201526c1c881bdc88185c1c1c9bdd9959609a1b6064820152608401610cdf565b61265d848484846140e0565b50505050565b6110be61102f565b60405162461bcd60e51b815260206004820152601760248201527f64656c656761746542795369673a2073697a65206375740000000000000000006044820152606401610cdf565b60008181526067602052604090205460609082906001600160a01b0316806126f157604051637e27328960e01b815260048101839052602401610cdf565b6000848152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff161515606082015261016e546001600160a01b031663dd9ec1498661275481613071565b604080860151865191516001600160e01b031960e087901b1681526127949493929060040193845260208401929092526040830152606082015260800190565b600060405180830381865afa1580156127b1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115f79190810190615de5565b60008181526067602052604090205481906001600160a01b03168061281457604051637e27328960e01b815260048101839052602401610cdf565b61281e858461415e565b82856001600160a01b0316336001600160a01b03167fb688daf266707251b810cde26b1bedfbbc6aa98cfc5667ea84fc354483e2ac8e87604051612866911515815260200190565b60405180910390a45050505050565b61287d61326d565b60008281526067602052604090205482906001600160a01b0316806128b857604051637e27328960e01b815260048101839052602401610cdf565b336128c482828561318c565b6128f35760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610cdf565b60008481526067602052604090205484906001600160a01b03168061292e57604051637e27328960e01b815260048101839052602401610cdf565b3361293a82828561318c565b6129695760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610cdf565b8688036129a2576040517f93b50ef200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600087815261017060209081526040808320815160808101835281548082526001830154948201949094526002820154928101929092526003015460ff16151560608201529103612a0657604051631f2a200560e01b815260040160405180910390fd5b42816040015111158015612a1c57508060600151155b15612a3a576040516307b7d7dd60e51b815260040160405180910390fd5b600089815261017060209081526040808320815160808101835281548082526001830154948201949094526002820154928101929092526003015460ff16151560608201529103612a9e57604051631f2a200560e01b815260040160405180910390fd5b606081015115156001148015612ac05750816060015115158160600151151514155b15612af7576040517f67fd3ad900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000826040015182604001511015612b13578260400151612b19565b81604001515b60008c81526101706020908152604080832083815581516080810183529384526001810154928401929092526002820154908301526003015460ff1615156060820152909150612b6c908c9084906137f7565b612b9960405180608001604052806000815260200160008152602001600081526020016000151581525090565b82518451612ba79190615c95565b81526060808501511515908201819052612bc357604081018290525b612bce8b85836137f7565b60008b8152610170602090815260409182902083518082558483015160018301558484015160028301556060808601516003909301805460ff191693151593841790558451918252928101869052928301528c918e917fec3b82c6fa54397270e9474a17cd38c92b766ae2b2541f87715357ac7ddd48e0910160405180910390a350505050505050505050611ace600161016b55565b6000612c6e61326d565b612c7d848433338660016132c8565b90506111e7600161016b55565b600054610100900460ff1615808015612caa5750600054600160ff909116105b80612cc45750303b158015612cc4575060005460ff166001145b612d365760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610cdf565b6000805460ff191660011790558015612d59576000805461ff0019166101001790555b612d6386866141fd565b612d6d8685614328565b61016c80546001600160a01b038086166001600160a01b03199283161790925561016e8054928516929091169190911790556303c26700610104558015612dee576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b60008281526067602052604081205483906001600160a01b031680612e3157604051637e27328960e01b815260048101839052602401610cdf565b600085815261017060205260409020600201548410612e6157600085815261017060205260409020549250612e66565b600092505b505092915050565b60606000612e7b83611e94565b905060008167ffffffffffffffff811115612e9857612e986156fb565b604051908082528060200260200182016040528015612ec1578160200160208202803683370190505b50905060005b82811015612f96576000612edb86836111ee565b60405163f83bb31360e01b815260fe60048201526024810182905290915073a615388bd2f920ee2fec7606f26908b454f0c2499063f83bb31390604401602060405180830381865af4158015612f35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f599190615c62565b838381518110612f6b57612f6b615cad565b6001600160a01b03909216602092830291909101909101525080612f8e81615cda565b915050612ec7565b509392505050565b6000610be98242612df6565b604051633d003cf160e21b815260fe6004820152602481018390526044810182905260009073a615388bd2f920ee2fec7606f26908b454f0c2499063f400f3c490606401611590565b60405163f83bb31360e01b815260fe60048201526024810182905260009073a615388bd2f920ee2fec7606f26908b454f0c2499063f83bb31390604401602060405180830381865af415801561304d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be99190615c62565b604051633d003cf160e21b815260fe60048201526024810182905242604482015260009073a615388bd2f920ee2fec7606f26908b454f0c2499063f400f3c4906064016121f3565b60408051606081018252600080825260208201819052918101829052906130e160fe8461439d565b91509150915091565b60006001600160e01b031982167fbd3a202b000000000000000000000000000000000000000000000000000000001480610be95750610be9826143e4565b6000818152606760205260409020546001600160a01b031661151f5760405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e20494400000000000000006044820152606401610cdf565b60006001600160a01b03831615801590610f8e5750826001600160a01b0316846001600160a01b031614806131e657506001600160a01b038085166000908152606a602090815260408083209387168352929052205460ff165b80610f8e5750826001600160a01b0316611dbc83610c81565b600081815260696020526040902080546001600160a01b0319166001600160a01b038416908117909155819061323482611ce1565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600261016b54036132c05760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610cdf565b600261016b55565b6000866000036132eb57604051631f2a200560e01b815260040160405180910390fd5b6101718054600091826132fd83615cda565b909155505061017154846133705761331861056b8942615c95565b65ffffffffffff16915042821161334257604051638e6b5b6760e01b815260040160405180910390fd5b610104546133509042615c95565b8211156133705760405163f761f1cd60e01b815260040160405180910390fd5b61337a8782614422565b6000818152610170602090815260409182902042600182018190558351608081018552825481529283015260028101549282019290925260039091015460ff16151560608201526133d29082908b9085908989613e57565b604051639bdd2e4d60e01b815260fe6004820152602481018290526001600160a01b03871660448201526064810183905273a615388bd2f920ee2fec7606f26908b454f0c24990639bdd2e4d906084016040805180830381865af415801561343e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134629190615c1a565b5050604080518a8152602081018490528615158183015290516001600160a01b0388169183917f551141c76a3b7e9a59ed1e32ccc940d667b98cfd98931126e89f1a17838f43519181900360600190a36040516001600160a01b03808816916000918a16907f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f908390a4604051600081526001600160a01b03808816919089169083907f39d7dc46c82aa27d4484a57e21232e5c6f22fe6c7ffc01795304084ba872811e9060200160405180910390a498975050505050505050565b600065ffffffffffff821115613567576040516393dafdf160e01b815260040160405180910390fd5b5090565b600062093a8061357b8184615d12565b610be99190615cf3565b60008061359183611ce1565b9050806001600160a01b0316846001600160a01b031614806135d857506001600160a01b038082166000908152606a602090815260408083209388168352929052205460ff165b80610f8e5750836001600160a01b0316611dbc84610c81565b826001600160a01b031661360482611ce1565b6001600160a01b0316146136685760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610cdf565b6001600160a01b0382166136e35760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610cdf565b6136f0838383600161443c565b826001600160a01b031661370382611ce1565b6001600160a01b0316146137675760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610cdf565b600081815260696020908152604080832080546001600160a01b03199081169091556001600160a01b0387811680865260688552838620805460001901905590871680865283862080546001019055868652606790945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b60fe73a615388bd2f920ee2fec7606f26908b454f0c249634cd2605690918561382386600001516145d2565b855161382e906145d2565b6040808901518882015191516001600160e01b031960e089901b16815260048101969096526024860194909452600f92830b6044860152910b6064840152608483019190915260a482015260c40160006040518083038186803b15801561389457600080fd5b505af41580156138a8573d6000803e3d6000fd5b50505050505050565b60008181526067602052604090205481906001600160a01b0316806138ec57604051637e27328960e01b815260048101839052602401610cdf565b6138f461326d565b60008381526067602052604090205483906001600160a01b03168061392f57604051637e27328960e01b815260048101839052602401610cdf565b3361393b82828561318c565b61396a5760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610cdf565b6000868152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff1615801560608301526139cf576040516334d10f9560e11b815260040160405180910390fd5b60006139da886122ac565b905080600003613a16576040517f6855a80200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160808101825260008082526020808301828152838501838152606085018481528e8552610170909352948320935184555160018401559251600283015591516003909101805460ff191691151591909117905561016d805491839190613a808385615cc3565b90915550506000898152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff1615156060820152613ad9908a9085906137f7565b60405182815233908a907fe97cee5a4c0549d3fdc81e322b718ddf0aeb3418ec87dce4f9a7fb28d117c3129060200160405180910390a3600089815260c9602052604081208054849290613b2e908490615c95565b90915550613b5c90503383613b4c61016c546001600160a01b031690565b6001600160a01b03169190614605565b61016d546040805183815260208101929092527f313edf9a07fc8449830af260b03eb5d8ffd3e7fc45a0c71cc26775972a4669a3910160405180910390a1505050505050610f63600161016b55565b6000613bb682611ce1565b9050613bc681600084600161443c565b613bcf82611ce1565b600083815260696020908152604080832080546001600160a01b03199081169091556001600160a01b0385168085526068845282852080546000190190558785526067909352818420805490911690555192935084927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b613c5661326d565b6000613c6183611e94565b90506000805b82811015613ddf576000613c7b86836111ee565b60008181526101706020526040808220600201549051639bdd2e4d60e01b815260fe6004820152602481018490526001600160a01b0389166044820152606481019190915291925090819073a615388bd2f920ee2fec7606f26908b454f0c24990639bdd2e4d906084016040805180830381865af4158015613d01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d259190615c1a565b91509150806001600160a01b0316886001600160a01b0316847f39d7dc46c82aa27d4484a57e21232e5c6f22fe6c7ffc01795304084ba872811e85604051613d7c91906001600160a01b0391909116815260200190565b60405180910390a46001600160a01b038516613d9a57819450613dc9565b6001600160a01b038516600114613dc957816001600160a01b0316856001600160a01b031614613dc957600194505b5050508080613dd790615cda565b915050613c67565b50826001600160a01b0316816001600160a01b0316856001600160a01b03167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f60405160405180910390a45050611ace600161016b55565b60606101398054610bfe90615be0565b606061013a8054610bfe90615be0565b61016d80549086906000613e6b8385615c95565b9091555050604080516080810182526000808252602080830182815283850183815260608086019485528a51938b0151968b0151908b01511515909452929092529290528181529087908290613ec2908390615c95565b9052508515801590613ed2575083155b15613edf57604081018690525b8315613ef45760006040820152600160608201525b60008881526101706020908152604091829020835181558382015160018201558383015160028201556060808501516003909201805460ff19169215159290921790915582518a81529182018990528615158284015291518a927fca0c517aab9ec63932670fd23484369811c8412142cc2535c9f78f008ecd0ce1928290030190a2613f818886836137f7565b8615801590613fa257506005836005811115613f9f57613f9f615e53565b14155b15613fc057613fc03361016c546001600160a01b031690308a614696565b61016d547f313edf9a07fc8449830af260b03eb5d8ffd3e7fc45a0c71cc26775972a4669a390613ff08985615c95565b6040805192835260208301919091520160405180910390a15050505050505050565b816001600160a01b0316836001600160a01b0316036140735760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610cdf565b6001600160a01b038381166000818152606a6020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6140eb8484846135f1565b6140f7848484846146e7565b61265d5760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b6064820152608401610cdf565b3361416882611ce1565b6001600160a01b0316146141cf57338161418183611ce1565b6040517f64283d7b0000000000000000000000000000000000000000000000000000000081526001600160a01b03938416600482015260248101929092529091166044820152606401610cdf565b600090815260ca6020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b600054610100900460ff161580801561421d5750600054600160ff909116105b806142375750303b158015614237575060005460ff166001145b6142a95760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610cdf565b6000805460ff1916600117905580156142cc576000805461ff0019166101001790555b6142d68383614830565b6142de6148a5565b8015610f63576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050565b600054610100900460ff166143935760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401610cdf565b611ace8282614910565b604080516060810182526000808252602082018190529181019190915260008281526002840160205260408120819081906143d7906149b5565b9890975095505050505050565b60006001600160e01b031982167f780e9d63000000000000000000000000000000000000000000000000000000001480610be95750610be982614aa7565b611ace828260405180602001604052806000815250614b42565b61444884848484614bc0565b60005b818110156145cb57600061445f8285615c95565b9050846001600160a01b0316866001600160a01b0316146145b85760008181526101706020526040808220600201549051639bdd2e4d60e01b815260fe6004820152602481018490526001600160a01b03881660448201526064810191909152819073a615388bd2f920ee2fec7606f26908b454f0c24990639bdd2e4d906084016040805180830381865af41580156144fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145209190615c1a565b91509150806001600160a01b0316826001600160a01b0316886001600160a01b03167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f60405160405180910390a46040516001600160a01b038381168252808316919089169085907f39d7dc46c82aa27d4484a57e21232e5c6f22fe6c7ffc01795304084ba872811e9060200160405180910390a450505b50806145c381615cda565b91505061444b565b5050505050565b60006f7fffffffffffffffffffffffffffffff821115613567576040516393dafdf160e01b815260040160405180910390fd5b6040516001600160a01b038316602482015260448101829052610f639084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b031990931692909217909152614c43565b6040516001600160a01b038085166024830152831660448201526064810182905261265d9085907f23b872dd000000000000000000000000000000000000000000000000000000009060840161464a565b60006001600160a01b0384163b1561482857604051630a85bd0160e11b81526001600160a01b0385169063150b7a029061472b903390899088908890600401615e69565b6020604051808303816000875af1925050508015614766575060408051601f3d908101601f1916820190925261476391810190615ea5565b60015b61480e573d808015614794576040519150601f19603f3d011682016040523d82523d6000602084013e614799565b606091505b5080516000036148065760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b6064820152608401610cdf565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610f8e565b506001610f8e565b600054610100900460ff1661489b5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401610cdf565b611ace8282614d2b565b600054610100900460ff166110be5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401610cdf565b600054610100900460ff1661497b5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401610cdf565b815161498f906101399060208501906153e4565b5080516149a49061013a9060208401906153e4565b505060006101378190556101385550565b60408051606081018252600080825260208201819052918101829052819083546000819003614a2557600080614a19604080516060808201835260008083526020808401829052928401819052835191820184528082529181018290529182015290565b93509350935050614aa0565b6000614a318682614dbd565b604080518082018252825465ffffffffffff1681528151606081018352600180850154600f81810b8452700100000000000000000000000000000000909104810b602084810191909152600290960154900b938201939093529281018390525190965094509250614aa0915050565b9193909250565b60006001600160e01b031982167f80ac58cd000000000000000000000000000000000000000000000000000000001480614b0a57506001600160e01b031982167f5b5e139f00000000000000000000000000000000000000000000000000000000145b80610be957507f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b0319831614610be9565b614b4c8383614de7565b614b5960008484846146e7565b610f635760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b6064820152608401610cdf565b614bcc84848484614f80565b60005b818110156145cb576000614be38285615c95565b90506001600160a01b038616151580614c0e5750846001600160a01b0316866001600160a01b031614155b15614c3057600081815260ca6020526040902080546001600160a01b03191690555b5080614c3b81615cda565b915050614bcf565b6000614c98826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166150b59092919063ffffffff16565b9050805160001480614cb9575080806020019051810190614cb99190615ec2565b610f635760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610cdf565b600054610100900460ff16614d965760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401610cdf565b8151614da99060659060208501906153e4565b508051610f639060669060208401906153e4565b6000828281548110614dd157614dd1615cad565b9060005260206000209060030201905092915050565b6001600160a01b038216614e3d5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610cdf565b6000818152606760205260409020546001600160a01b031615614ea25760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610cdf565b614eb060008383600161443c565b6000818152606760205260409020546001600160a01b031615614f155760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610cdf565b6001600160a01b038216600081815260686020908152604080832080546001019055848352606790915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6001811115614ff75760405162461bcd60e51b815260206004820152603560248201527f455243373231456e756d657261626c653a20636f6e736563757469766520747260448201527f616e7366657273206e6f7420737570706f7274656400000000000000000000006064820152608401610cdf565b816001600160a01b0385166150535761504e81609980546000838152609a60205260408120829055600182018355919091527f72a152ddfb8e864297c917af52ea6c1c68aead0fee1a62673fcc7e0c94979d000155565b615076565b836001600160a01b0316856001600160a01b0316146150765761507685826150c4565b6001600160a01b0384166150925761508d81615161565b6145cb565b846001600160a01b0316846001600160a01b0316146145cb576145cb8482615210565b6060610f8e8484600085615254565b600060016150d184611e94565b6150db9190615cc3565b60008381526098602052604090205490915080821461512e576001600160a01b03841660009081526097602090815260408083208584528252808320548484528184208190558352609890915290208190555b5060009182526098602090815260408084208490556001600160a01b039094168352609781528383209183525290812055565b60995460009061517390600190615cc3565b6000838152609a60205260408120546099805493945090928490811061519b5761519b615cad565b9060005260206000200154905080609983815481106151bc576151bc615cad565b6000918252602080832090910192909255828152609a909152604080822084905585825281205560998054806151f4576151f4615edf565b6001900381819060005260206000200160009055905550505050565b600061521b83611e94565b6001600160a01b039093166000908152609760209081526040808320868452825280832085905593825260989052919091209190915550565b6060824710156152cc5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610cdf565b600080866001600160a01b031685876040516152e89190615ef5565b60006040518083038185875af1925050503d8060008114615325576040519150601f19603f3d011682016040523d82523d6000602084013e61532a565b606091505b509150915061533b87838387615346565b979650505050505050565b606083156153b55782516000036153ae576001600160a01b0385163b6153ae5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610cdf565b5081610f8e565b610f8e83838151156153ca5781518083602001fd5b8060405162461bcd60e51b8152600401610cdf91906154ff565b8280546153f090615be0565b90600052602060002090601f0160209004810192826154125760008555615458565b82601f1061542b57805160ff1916838001178555615458565b82800160010185558215615458579182015b8281111561545857825182559160200191906001019061543d565b506135679291505b808211156135675760008155600101615460565b6001600160e01b03198116811461151f57600080fd5b60006020828403121561549c57600080fd5b81356111e781615474565b60005b838110156154c25781810151838201526020016154aa565b8381111561265d5750506000910152565b600081518084526154eb8160208601602086016154a7565b601f01601f19169290920160200192915050565b6020815260006111e760208301846154d3565b60006020828403121561552457600080fd5b5035919050565b6001600160a01b038116811461151f57600080fd5b6000806040838503121561555357600080fd5b8235915060208301356155658161552b565b809150509250929050565b6000806040838503121561558357600080fd5b823561558e8161552b565b946020939093013593505050565b801515811461151f57600080fd5b600080600080608085870312156155c057600080fd5b843593506020850135925060408501356155d98161552b565b915060608501356155e98161559c565b939692955090935050565b60008060006060848603121561560957600080fd5b83356156148161552b565b925060208401356156248161552b565b929592945050506040919091013590565b65ffffffffffff8116811461151f57600080fd5b6000806040838503121561565c57600080fd5b82359150602083013561556581615635565b6000806040838503121561568157600080fd5b823561568c8161552b565b915060208301356155658161559c565b600080600080600060a086880312156156b457600080fd5b853594506020860135935060408601356156cd8161552b565b925060608601356156dd8161552b565b915060808601356156ed8161559c565b809150509295509295909350565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561573a5761573a6156fb565b604052919050565b6000806040838503121561575557600080fd5b823567ffffffffffffffff8082111561576d57600080fd5b818501915085601f83011261578157600080fd5b8135602082821115615795576157956156fb565b8160051b92506157a6818401615711565b82815292840181019281810190898511156157c057600080fd5b948201945b848610156157de578535825294820194908201906157c5565b9997909101359750505050505050565b60006020828403121561580057600080fd5b81356111e78161552b565b6000806040838503121561581e57600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b8381101561585d57815187529582019590820190600101615841565b509495945050505050565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e0602082015260006158a360e08301896154d3565b82810360408401526158b581896154d3565b90508660608401526001600160a01b03861660808401528460a084015282810360c08401526158e4818561582d565b9a9950505050505050505050565b60008060006060848603121561590757600080fd5b833592506020840135915060408401356159208161559c565b809150509250925092565b6000806040838503121561593e57600080fd5b82356159498161552b565b915060208301356155658161552b565b600067ffffffffffffffff821115615973576159736156fb565b50601f01601f191660200190565b600061599461598f84615959565b615711565b90508281528383830111156159a857600080fd5b828260208301376000602084830101529392505050565b600080600080608085870312156159d557600080fd5b84356159e08161552b565b935060208501356159f08161552b565b925060408501359150606085013567ffffffffffffffff811115615a1357600080fd5b8501601f81018713615a2457600080fd5b615a3387823560208401615981565b91505092959194509250565b60008060008060008060c08789031215615a5857600080fd5b8635615a638161552b565b95506020870135945060408701359350606087013560ff81168114615a8757600080fd5b9598949750929560808101359460a0909101359350915050565b600080600060608486031215615ab657600080fd5b8335615ac18161552b565b925060208401356156248161559c565b600082601f830112615ae257600080fd5b6111e783833560208501615981565b600080600080600060a08688031215615b0957600080fd5b853567ffffffffffffffff80821115615b2157600080fd5b615b2d89838a01615ad1565b96506020880135915080821115615b4357600080fd5b615b4f89838a01615ad1565b95506040880135915080821115615b6557600080fd5b50615b7288828901615ad1565b9350506060860135615b838161552b565b915060808601356156ed8161552b565b6020808252825182820181905260009190848201906040850190845b81811015615bd45783516001600160a01b031683529284019291840191600101615baf565b50909695505050505050565b600181811c90821680615bf457607f821691505b602082108103615c1457634e487b7160e01b600052602260045260246000fd5b50919050565b60008060408385031215615c2d57600080fd5b8251615c388161552b565b60208401519092506155658161552b565b600060208284031215615c5b57600080fd5b5051919050565b600060208284031215615c7457600080fd5b81516111e78161552b565b634e487b7160e01b600052601160045260246000fd5b60008219821115615ca857615ca8615c7f565b500190565b634e487b7160e01b600052603260045260246000fd5b600082821015615cd557615cd5615c7f565b500390565b600060018201615cec57615cec615c7f565b5060010190565b6000816000190483118215151615615d0d57615d0d615c7f565b500290565b600082615d2f57634e487b7160e01b600052601260045260246000fd5b500490565b6020815260006111e7602083018461582d565b8051600f81900b8114615d5957600080fd5b919050565b6000808284036080811215615d7257600080fd5b6060811215615d8057600080fd5b506040516060810181811067ffffffffffffffff82111715615da457615da46156fb565b604052615db084615d47565b8152615dbe60208501615d47565b6020820152615dcf60408501615d47565b6040820152606084015190925061556581615635565b600060208284031215615df757600080fd5b815167ffffffffffffffff811115615e0e57600080fd5b8201601f81018413615e1f57600080fd5b8051615e2d61598f82615959565b818152856020838501011115615e4257600080fd5b6115f78260208301602086016154a7565b634e487b7160e01b600052602160045260246000fd5b60006001600160a01b03808716835280861660208401525083604083015260806060830152615e9b60808301846154d3565b9695505050505050565b600060208284031215615eb757600080fd5b81516111e781615474565b600060208284031215615ed457600080fd5b81516111e78161559c565b634e487b7160e01b600052603160045260246000fd5b60008251615f078184602087016154a7565b919091019291505056fea26469706673582212207c41d260adabcc3ca693a2f348ba09e3532b0220fd8c1572f4a3ac5e149ce61a64736f6c634300080d0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
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.