Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
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 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {IERC5725Upgradeable} from "./erc5725/IERC5725Upgradeable.sol";
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 {IVotingEscrowV2, IVotes} from "./IVotingEscrowV2.sol";
import {IVeArtProxy} from "../../VeArtProxy/IVeArtProxy.sol";
import {SafeCastLibrary} from "./libraries/SafeCastLibrary.sol";
import {EscrowDelegateCheckpoints, Checkpoints} from "./libraries/EscrowDelegateCheckpoints.sol";
import {EscrowDelegateStorage} from "./libraries/EscrowDelegateStorage.sol";
/**
* @title VotingEscrowV2Upgradeable
* @dev This contract is used for locking tokens and voting.
* - The storage layout for all version 2.x contracts MUST remain compatible for upgradeability.
* - tokenIds always have a delegatee, with the owner being the default (see createLock)
* - On transfers, delegation is reset. (See _update)
*
* @custom:limitations
* - DOES NOT support feeOnTransfer for lock token: token
* - EscrowDelegateCheckpoints.checkpoint() creates a minimum token decimal limitation
* - Because point.slope = (amount) / MAX_TIME, if amount is less than 63,072,000 (two years), the slope will be divided to 0.
* - This limitation means that the minimum token decimal should be 8, but more is recommended.
* - getAccountDelegates() may run out of gas if the account has too many tokens.
* - ReentrancyGuard is used over ReentrancyGuardUpgradeable for upgradeability.
*/
contract VotingEscrowV2Upgradeable is
Initializable,
IVotingEscrowV2,
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 current version of the contract
string public constant override VERSION = "2.2.0";
/// @notice The token being locked
IERC20Upgradeable public token;
/// @notice Total locked supply
uint256 public supply;
uint8 public constant decimals = 18;
/// @notice Immutable address for the art proxy contract
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;
/// @notice maps the vesting data with tokenIds
mapping(uint256 => LockDetails) public _lockDetails;
/// @notice tracker of current NFT id
uint256 public totalNftsMinted = 0;
/// @notice The last time a lock was modified. Prevents front-running sale attacks.
mapping(uint256 => uint256) public lockModifiedAt;
/// @notice Reserved storage slots for future upgrades
uint256[50] private __gap;
/// -----------------------------------------------------------------------
/// Constructor
/// -----------------------------------------------------------------------
/**
* @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);
// Validate and set the main token address
token = mainToken;
token.totalSupply(); // Validate token address
// Validate and set the art proxy address
artProxy = _artProxy;
_tokenURI(0); // Validate art proxy address
// 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) {
return _tokenURI(_tokenId);
}
/// @dev Returns current token URI metadata
/// @param _tokenId Token ID to fetch URI for.
function _tokenURI(uint _tokenId) internal view returns (string memory) {
LockDetails memory _locked = _lockDetails[_tokenId];
return
IVeArtProxy(artProxy)._tokenURI(
_tokenId,
balanceOfNFT(_tokenId),
_locked.endTime,
uint256(int256(_locked.amount))
);
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(
bytes4 interfaceId
) public view virtual override(ERC5725Upgradeable, IERC165Upgradeable) returns (bool supported) {
return interfaceId == type(IVotingEscrowV2).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;
/// @dev Frontrun protection can be added here
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);
}
}
}
/**
* @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
* @param delegatee The delegatee of the lock
* @param permanent Whether the lock is permanent or not
* @param depositType The type of the deposit
* @return The id of the newly created token
*/
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; // 1 indexed
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();
}
_mint(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 with a specified delegatee
* @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 delegatee 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();
}
/**
* @notice Alias for 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
/// @param isPermanent Indicates if the lock is permanent
/// @param depositType The type of the deposit
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) {
/// @dev lock.endTime MUST be set to 0 for permanent locks
/// EscrowDelegateCheckpoints.checkpoint() expects this
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)
/// @dev Prevent DOS by malicious users
bool markLockModified = !(depositType == DepositType.CREATE_LOCK_TYPE ||
depositType == DepositType.INCREASE_LOCK_AMOUNT);
_checkpointLock(_tokenId, _oldLocked, newLocked, markLockModified);
if (_increasedValue != 0 && depositType != DepositType.SPLIT_TYPE) {
token.safeTransferFrom(_msgSender(), address(this), _increasedValue);
emit SupplyUpdated(supplyBefore, supply);
}
}
/// @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
/// @param _markLockModified Marking lock as modified prevents transfers for a short time delay
function _checkpointLock(
uint256 _tokenId,
IVotingEscrowV2.LockDetails memory _oldLocked,
IVotingEscrowV2.LockDetails memory _newLocked,
bool _markLockModified
) internal {
if (_markLockModified) {
/// @dev Log modifications to help prevent marketplace front-running
lockModifiedAt[_tokenId] = block.timestamp;
}
/// @dev EscrowDelegateCheckpoints.checkpoint() expects permanent locks to have an end time of 0 for slope calculations
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();
IVotingEscrowV2.LockDetails memory oldLocked = _lockDetails[_tokenId];
if (_ownerOf(_tokenId) == address(0)) revert ERC721NonexistentToken(_tokenId);
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 non-permanent 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();
if (oldLocked.endTime <= block.timestamp) revert LockExpired();
if (oldLocked.amount == 0) revert ZeroAmount();
uint256 unlockTime;
if (!_permanent) {
unlockTime = toGlobalClock(block.timestamp + _lockDuration);
// Locktime is rounded down to global clock (days)
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, false);
_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) checkAuthorized(_tokenId) {
IVotingEscrowV2.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] = IVotingEscrowV2.LockDetails(0, 0, 0, false);
// Update the total supply
uint256 supplyBefore = supply;
supply -= amountClaimed;
// Update the lock details
_checkpointLock(_tokenId, oldLocked, _lockDetails[_tokenId], true);
/// @notice ERC-5725 event
emit PayoutClaimed(_tokenId, _msgSender(), amountClaimed);
// IERC5725 - Update the total amount claimed
_payoutClaimed[_tokenId] += amountClaimed;
// Transfer the claimed amount to the sender
IERC20Upgradeable(_payoutToken(_tokenId)).safeTransfer(_msgSender(), 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(IERC5725Upgradeable, ERC5725Upgradeable) nonReentrant {
_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();
IVotingEscrowV2.LockDetails memory oldLockedTo = _lockDetails[_to];
if (oldLockedTo.amount == 0) revert ZeroAmount();
if (oldLockedTo.endTime <= block.timestamp && !oldLockedTo.isPermanent) revert LockExpired();
IVotingEscrowV2.LockDetails memory oldLockedFrom = _lockDetails[_from];
if (oldLockedFrom.amount == 0) revert ZeroAmount();
if (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], true);
// Calculate the new lock details
LockDetails memory newLockedTo;
newLockedTo.amount = oldLockedTo.amount + oldLockedFrom.amount;
newLockedTo.startTime = oldLockedTo.startTime;
newLockedTo.isPermanent = oldLockedTo.isPermanent;
if (!newLockedTo.isPermanent) {
newLockedTo.endTime = end;
}
// Update the lock details
_checkpointLock(_to, oldLockedTo, newLockedTo, true);
_lockDetails[_to] = newLockedTo;
emit LockMerged(_from, _to, newLockedTo.amount, end, newLockedTo.isPermanent);
}
/**
* @notice Splits a token into multiple tokens
* @dev WARN split locks will reset delegation to the owner
* @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;
if (locked.endTime <= currentTime && !locked.isPermanent) revert LockExpired();
if (locked.amount == 0 || _weights.length < 2) revert ZeroAmount();
// reset supply
supply -= 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;
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;
/// @dev supply is reduced by total above
supply += value;
_checkpointLock(_tokenId, locked, lockedStorage, true);
} else {
/// @dev supply is increased in _createLock
_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 nonReentrant {
if (_ownerOf(_tokenId) != _msgSender()) revert NotLockOwner();
if (_lockDetails[_tokenId].amount > 0) revert LockHoldsValue();
_burn(_tokenId);
}
/*///////////////////////////////////////////////////////////////
GAUGE REWARDS LOGIC
//////////////////////////////////////////////////////////////*/
/**
* @notice Gets the balance of a lock token
* @param _tokenId The ID of the token
* @return The balance of the lock token
*/
function balanceOfNFT(uint256 _tokenId) public view returns (uint256) {
return edStore.getAdjustedEscrowBias(_tokenId, block.timestamp);
}
/**
* @notice Gets the balance of a lock token at a specific timestamp
* @param _tokenId The ID of the token
* @param _timestamp The timestamp to get the balance at
* @return The balance of the lock token at the specified timestamp
*/
function balanceOfNFTAt(uint256 _tokenId, uint256 _timestamp) external view returns (uint256) {
return edStore.getAdjustedEscrowBias(_tokenId, _timestamp);
}
/**
* @notice Gets the past escrow point for a token at a specific timestamp
* @param _tokenId The ID of the token
* @param _timestamp The timestamp to get the past escrow point at
* @return The escrow point and timestamp of the past escrow point
*/
function getPastEscrowPoint(
uint256 _tokenId,
uint256 _timestamp
) external view override returns (Checkpoints.Point memory, uint48) {
return edStore.getAdjustedEscrow(_tokenId, _timestamp);
}
/**
* @notice Gets the first escrow point for a token
* @param _tokenId The ID of the token
* @return The escrow point and timestamp of the first escrow point
*/
function getFirstEscrowPoint(uint256 _tokenId) external view override returns (Checkpoints.Point memory, uint48) {
return edStore.getFirstEscrowPoint(_tokenId);
}
/**
* @notice Gets the total supply of the lock tokens
* @return The total supply of the lock tokens
*/
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) nonReentrant {
_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 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 {
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);
}
/*///////////////////////////////////////////////////////////////
ERC5725Upgradeable
//////////////////////////////////////////////////////////////*/
/// @inheritdoc ERC5725Upgradeable
function vestedPayoutAtTime(
uint256 tokenId,
uint256 timestamp
) public view override(IERC5725Upgradeable, ERC5725Upgradeable) validToken(tokenId) returns (uint256 payout) {
if (timestamp >= _endTime(tokenId)) {
return _payout(tokenId);
}
return 0;
}
/// @dev All vested tokens are claimable
function claimablePayout(
uint256 tokenId
) public view override(IERC5725Upgradeable, ERC5725Upgradeable) validToken(tokenId) returns (uint256) {
return vestedPayout(tokenId);
}
/// @inheritdoc ERC5725Upgradeable
function _payoutToken(uint256 /*tokenId*/) internal view override(ERC5725Upgradeable) returns (address) {
return address(token);
}
/// @inheritdoc ERC5725Upgradeable
function _payout(uint256 tokenId) internal view override(ERC5725Upgradeable) returns (uint256) {
return _lockDetails[tokenId].amount;
}
/// @inheritdoc ERC5725Upgradeable
function _startTime(uint256 tokenId) internal view override(ERC5725Upgradeable) returns (uint256) {
return _lockDetails[tokenId].startTime;
}
/// @inheritdoc ERC5725Upgradeable
function _endTime(uint256 tokenId) internal view override(ERC5725Upgradeable) returns (uint256) {
LockDetails memory currentLock = _lockDetails[tokenId];
if(currentLock.isPermanent) {
return type(uint48).max;
}
return currentLock.endTime;
}
/**
* @notice Gets the lock details of a token
* @param _tokenId The ID of the token
* @return The lock details of the token
*/
function lockDetails(uint256 _tokenId) external view returns (LockDetails memory) {
return _lockDetails[_tokenId];
}
/**
* @notice Checks if a user is approved or the owner of a token
* @param user The address of the user
* @param tokenId The ID of the token
* @return True if the user is approved or the owner, false otherwise
*/
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) (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;
library Constants {
uint48 constant EPOCH = 1 weeks;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IVersionable {
function VERSION() external view returns (string memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IVeArtProxy {
function tokenURI(
uint256 _tokenId,
uint256 _balanceOf,
uint256 _locked_end,
uint256 _value
) external view returns (string memory output);
/// @dev Leaving for backwards compatibility
function _tokenURI(
uint256 _tokenId,
uint256 _balanceOf,
uint256 _locked_end,
uint256 _value
) external view 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 {IERC5725_ExtendedApproval, 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,
IERC5725_ExtendedApproval,
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();
}
/**
* @notice Initializes the contract with the given name and symbol.
* @param name_ The name of the token.
* @param symbol_ The symbol of the token.
*/
function __ERC5725_init(string memory name_, string memory symbol_) internal onlyInitializing {
__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 virtual 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 (operator == owner || _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 override(IERC5725_ExtendedApproval) 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);
}
interface IERC5725_ExtendedApproval is IERC5725Upgradeable {
/**
* @notice Returns true if `operator` is allowed to transfer the `tokenId` NFT.
* @param operator The address to check if it is approved for the transfer
* @param tokenId The token id to check if the operator is approved for
*/
function isApprovedOrOwner(address operator, uint tokenId) external view returns (bool);
/**
* @notice Returns true if `operator` is allowed to claim for the provided tokenId
* @param operator The address to check if it is approved for the claim or owner of the token
* @param tokenId The token id to check if the operator is approved for
*/
function isApprovedClaimOrOwner(address operator, uint256 tokenId) external view returns (bool);
}// SPDX-License-Identifier: MIT 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
// OpenZeppelin Contracts (last updated v4.9.0) (governance/utils/IVotes.sol)
pragma solidity ^0.8.0;
/**
* @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
*
* _Available since v4.5._
*/
interface IVotes {
/**
* @dev Emitted when an account changes their delegate.
*/
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
/**
* @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
*/
event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);
/**
* @dev Returns the current amount of votes that `account` has.
*/
function getVotes(address account) external view returns (uint256);
/**
* @dev Returns the amount of votes that `account` had at a specific moment in the past. If the `clock()` is
* configured to use block numbers, this will return the value at the end of the corresponding block.
*/
function getPastVotes(address account, uint256 timepoint) external view returns (uint256);
/**
* @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is
* configured to use block numbers, this will return the value at the end of the corresponding block.
*
* NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
* Votes that have not been delegated are still part of total supply, even though they would not participate in a
* vote.
*/
function getPastTotalSupply(uint256 timepoint) external view returns (uint256);
/**
* @dev Returns the delegate that `account` has chosen.
*/
function delegates(address account) external view returns (address);
/**
* @dev Delegates votes from the sender to `delegatee`.
*/
function delegate(address delegatee) external;
/**
* @notice Removed from the interface to avoid signature conflicts.
* @dev Delegates votes from signer to `delegatee`.
*/
// function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {IVotes} from "./interfaces/IVotes.sol";
import {Checkpoints} from "./libraries/Checkpoints.sol";
import {IERC5725_ExtendedApproval} from "./erc5725/IERC5725Upgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {IERC721EnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol";
import {IVersionable} from "../../interfaces/IVersionable.sol";
/**
* @title Voting Escrow V2 Interface for Upgrades
*/
interface IVotingEscrowV2 is IVotes, IERC5725_ExtendedApproval, IERC721EnumerableUpgradeable, IVersionable {
struct LockDetails {
uint256 amount; /// @dev amount of tokens locked
uint256 startTime; /// @dev when locking started
uint256 endTime; /// @dev when locking ends
bool isPermanent; /// @dev if its a permanent lock
}
/// -----------------------------------------------------------------------
/// Events
/// -----------------------------------------------------------------------
event SupplyUpdated(uint256 oldSupply, uint256 newSupply);
/// @notice Lock events
event LockCreated(uint256 indexed tokenId, address indexed to, uint256 value, uint256 unlockTime, bool isPermanent);
event LockUpdated(uint256 indexed tokenId, uint256 value, uint256 unlockTime, bool isPermanent);
event LockMerged(
uint256 indexed fromTokenId,
uint256 indexed toTokenId,
uint256 totalValue,
uint256 unlockTime,
bool isPermanent
);
event LockSplit(uint256[] splitWeights, uint256 indexed _tokenId);
event LockDurationExtended(uint256 indexed tokenId, uint256 newUnlockTime, bool isPermanent);
event LockAmountIncreased(uint256 indexed tokenId, uint256 value);
event UnlockPermanent(uint256 indexed tokenId, address indexed sender, uint256 unlockTime);
/// @notice Delegate events
event LockDelegateChanged(
uint256 indexed tokenId,
address indexed delegator,
address fromDelegate,
address indexed toDelegate
);
/// -----------------------------------------------------------------------
/// Errors
/// -----------------------------------------------------------------------
error AlreadyVoted();
error InvalidNonce();
error InvalidDelegatee();
error InvalidSignature();
error InvalidSignatureS();
error InvalidWeights();
error LockDurationNotInFuture();
error LockDurationTooLong();
error LockExpired();
error LockNotExpired();
error LockHoldsValue();
error LockModifiedDelay();
error NotPermanentLock();
error PermanentLock();
error PermanentLockMismatch();
error SameNFT();
error SignatureExpired();
error ZeroAmount();
error NotLockOwner();
function supply() external view returns (uint);
function token() external view returns (IERC20Upgradeable);
function totalNftsMinted() external view returns (uint256);
function balanceOfNFT(uint256 _tokenId) external view returns (uint256);
function balanceOfNFTAt(uint256 _tokenId, uint256 _timestamp) external view returns (uint256);
function delegates(uint256 tokenId, uint48 timestamp) external view returns (address);
function lockDetails(uint256 tokenId) external view returns (LockDetails calldata);
function getPastEscrowPoint(
uint256 _tokenId,
uint256 _timePoint
) external view returns (Checkpoints.Point memory, uint48);
function getFirstEscrowPoint(uint256 _tokenId) external view returns (Checkpoints.Point memory, uint48);
function checkpoint() external;
function increaseAmount(uint256 _tokenId, uint256 _value) external;
function createLockFor(
uint256 _value,
uint256 _lockDuration,
address _to,
bool _permanent
) external returns (uint256);
function createDelegatedLockFor(
uint256 _value,
uint256 _lockDuration,
address _to,
address _delegatee,
bool _permanent
) external returns (uint256);
function split(uint256[] memory _weights, uint256 _tokenId) external;
function merge(uint256 _from, uint256 _to) external;
function burn(uint256 _tokenId) external;
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// This file was derived from OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/Checkpoints.sol)
pragma solidity 0.8.13;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
/**
* @dev This library defines the `Trace*` struct, for checkpointing values as they change at different points in
* time, and later looking up past values by block number. See {Votes} as an example.
*
* To create a history of checkpoints define a variable type `Checkpoints.Trace*` in your contract, and store a new
* checkpoint for the current transaction block using the {push} function.
*/
library Checkpoints {
struct Trace {
Checkpoint[] _checkpoints;
}
/**
* @dev Struct to keep track of the voting power over time.
*/
struct Point {
/// @dev The voting power at a specific time
/// - MUST never be negative.
int128 bias;
/// @dev The rate at which the voting power decreases over time.
int128 slope;
/// @dev The value of tokens which do not decrease over time, representing permanent voting power
/// - MUST never be negative.
int128 permanent;
}
struct Checkpoint {
uint48 _key;
Point _value;
}
/**
* @dev A value was attempted to be inserted on a past checkpoint.
*/
error CheckpointUnorderedInsertions();
/**
* @dev Pushes a (`key`, `value`) pair into a Trace so that it is stored as the checkpoint.
*
* Returns previous value and new value.
*
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint48).max` key set will disable the
* library.
*/
function push(Trace storage self, uint48 key, Point memory value) internal returns (Point memory, Point memory) {
return _insert(self._checkpoints, key, value);
}
/**
* @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
* there is none.
*/
function lowerLookup(Trace storage self, uint48 key) internal view returns (Point memory) {
uint256 len = self._checkpoints.length;
uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return pos == len ? blankPoint() : _unsafeAccess(self._checkpoints, pos)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*/
function upperLookup(
Trace storage self,
uint48 key
) internal view returns (bool exists, uint48 _key, Point memory _value) {
uint256 len = self._checkpoints.length;
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
exists = pos != 0;
_value = exists ? _unsafeAccess(self._checkpoints, pos - 1)._value : blankPoint();
_key = exists ? _unsafeAccess(self._checkpoints, pos - 1)._key : 0;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*
* NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
* keys).
*/
function upperLookupRecent(
Trace storage self,
uint48 key
) internal view returns (bool exists, uint48 _key, Point memory _value) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
exists = pos != 0;
_value = exists ? _unsafeAccess(self._checkpoints, pos - 1)._value : blankPoint();
_key = exists ? _unsafeAccess(self._checkpoints, pos - 1)._key : 0;
}
/**
* @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
*/
function latest(Trace storage self) internal view returns (Point memory) {
uint256 pos = self._checkpoints.length;
return pos == 0 ? blankPoint() : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
* in the most recent checkpoint.
*/
function latestCheckpoint(
Trace storage self
) internal view returns (bool exists, uint48 _key, Point memory _value) {
uint256 pos = self._checkpoints.length;
if (pos == 0) {
return (false, 0, blankPoint());
} else {
Checkpoint memory ckpt = _unsafeAccess(self._checkpoints, pos - 1);
return (true, ckpt._key, ckpt._value);
}
}
/**
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
* in the most recent checkpoint.
*/
function firstCheckpoint(
Trace storage self
) internal view returns (bool exists, uint48 _key, Point memory _value) {
uint256 pos = self._checkpoints.length;
if (pos == 0) {
return (false, 0, blankPoint());
} else {
Checkpoint memory ckpt = _unsafeAccess(self._checkpoints, 0);
return (true, ckpt._key, ckpt._value);
}
}
/**
* @dev Returns the number of checkpoint.
*/
function length(Trace storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
/**
* @dev Returns checkpoint at given position.
*/
function at(Trace storage self, uint48 pos) internal view returns (Checkpoint memory) {
return self._checkpoints[pos];
}
/**
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
* or by updating the last one.
*/
function _insert(
Checkpoint[] storage self,
uint48 key,
Point memory value
) private returns (Point memory, Point memory) {
uint256 pos = self.length;
if (pos > 0) {
// Copying to memory is important here.
Checkpoint memory last = _unsafeAccess(self, pos - 1);
// Checkpoint keys must be non-decreasing.
if (last._key > key) {
revert CheckpointUnorderedInsertions();
}
// Update or push new checkpoint
if (last._key == key) {
_unsafeAccess(self, pos - 1)._value = value;
} else {
self.push(Checkpoint({_key: key, _value: value}));
}
return (last._value, value);
} else {
self.push(Checkpoint({_key: key, _value: value}));
return (blankPoint(), value);
}
}
/**
* @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _upperBinaryLookup(
Checkpoint[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
/**
* @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or
* `high` if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and
* exclusive `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _lowerBinaryLookup(
Checkpoint[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
/**
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
*/
function _unsafeAccess(Checkpoint[] storage self, uint256 pos) private view returns (Checkpoint storage result) {
return self[pos];
}
/**
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
*/
function _realUnsafeAccess(
Checkpoint[] storage self,
uint256 pos
) private pure returns (Checkpoint storage result) {
assembly {
mstore(0, self.slot)
result.slot := add(keccak256(0, 0x20), pos)
}
}
function blankPoint() internal pure returns (Point memory) {
return Point({bias: 0, slope: 0, permanent: 0});
}
struct TraceAddress {
CheckpointAddress[] _checkpoints;
}
struct CheckpointAddress {
uint48 _key;
address _value;
}
/**
* @dev Pushes a (`key`, `value`) pair into a TraceAddress so that it is stored as the checkpoint.
*
* Returns previous value and new value.
*
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint48).max` key set will disable the
* library.
*/
function push(TraceAddress storage self, uint48 key, address value) internal returns (address, address) {
return _insert(self._checkpoints, key, value);
}
/**
* @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
* there is none.
*/
function lowerLookup(TraceAddress storage self, uint48 key) internal view returns (address) {
uint256 len = self._checkpoints.length;
uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
return pos == len ? address(0) : _unsafeAccess(self._checkpoints, pos)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*/
function upperLookup(TraceAddress storage self, uint48 key) internal view returns (address) {
uint256 len = self._checkpoints.length;
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
return pos == 0 ? address(0) : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
* if there is none.
*
* NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
* keys).
*/
function upperLookupRecent(TraceAddress storage self, uint48 key) internal view returns (address) {
uint256 len = self._checkpoints.length;
uint256 low = 0;
uint256 high = len;
if (len > 5) {
uint256 mid = len - Math.sqrt(len);
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
high = mid;
} else {
low = mid + 1;
}
}
uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
return pos == 0 ? address(0) : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
*/
function latest(TraceAddress storage self) internal view returns (address) {
uint256 pos = self._checkpoints.length;
return pos == 0 ? address(0) : _unsafeAccess(self._checkpoints, pos - 1)._value;
}
/**
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
* in the most recent checkpoint.
*/
function latestCheckpoint(
TraceAddress storage self
) internal view returns (bool exists, uint48 _key, address _value) {
uint256 pos = self._checkpoints.length;
if (pos == 0) {
return (false, 0, address(0));
} else {
CheckpointAddress memory ckpt = _unsafeAccess(self._checkpoints, pos - 1);
return (true, ckpt._key, ckpt._value);
}
}
/**
* @dev Returns the number of checkpoint.
*/
function length(TraceAddress storage self) internal view returns (uint256) {
return self._checkpoints.length;
}
/**
* @dev Returns checkpoint at given position.
*/
function at(TraceAddress storage self, uint48 pos) internal view returns (CheckpointAddress memory) {
return self._checkpoints[pos];
}
/**
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
* or by updating the last one.
*/
function _insert(CheckpointAddress[] storage self, uint48 key, address value) private returns (address, address) {
uint256 pos = self.length;
if (pos > 0) {
// Copying to memory is important here.
CheckpointAddress memory last = _unsafeAccess(self, pos - 1);
// Checkpoint keys must be non-decreasing.
if (last._key > key) {
revert CheckpointUnorderedInsertions();
}
// Update or push new checkpoint
if (last._key == key) {
_unsafeAccess(self, pos - 1)._value = value;
} else {
self.push(CheckpointAddress({_key: key, _value: value}));
}
return (last._value, value);
} else {
self.push(CheckpointAddress({_key: key, _value: value}));
return (address(0), value);
}
}
/**
* @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high`
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
* `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _upperBinaryLookup(
CheckpointAddress[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key > key) {
high = mid;
} else {
low = mid + 1;
}
}
return high;
}
/**
* @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or
* `high` if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and
* exclusive `high`.
*
* WARNING: `high` should not be greater than the array's length.
*/
function _lowerBinaryLookup(
CheckpointAddress[] storage self,
uint48 key,
uint256 low,
uint256 high
) private view returns (uint256) {
while (low < high) {
uint256 mid = Math.average(low, high);
if (_unsafeAccess(self, mid)._key < key) {
low = mid + 1;
} else {
high = mid;
}
}
return high;
}
/**
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
*/
function _unsafeAccess(
CheckpointAddress[] storage self,
uint256 pos
) private pure returns (CheckpointAddress storage result) {
assembly {
mstore(0, self.slot)
result.slot := add(keccak256(0, 0x20), pos)
}
}
}// 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";
import {Constants} from "../../../Constants.sol";
/**
* @title EscrowDelegateCheckpoints
* @dev This library is used to manage checkpoints of escrow locks and delegatees.
*/
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;
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;
}
/// @notice Emitted when a global checkpoint is created
/// @param timestamp The timestamp of the checkpoint
/// @param slope The slope of the checkpoint
/// @param bias The bias of the checkpoint
/// @param permanent The permanent value of the checkpoint
event CheckpointGlobal(uint48 timestamp, int128 slope, int128 bias, int128 permanent);
/// @notice Emitted when a delegate checkpoint is created
/// @param delegatee The address of the delegatee
/// @param timestamp The timestamp of the checkpoint
/// @param slope The slope of the checkpoint
/// @param bias The bias of the checkpoint
/// @param permanent The permanent value of the checkpoint
event CheckpointDelegate(address delegatee, uint48 timestamp, int128 slope, int128 bias, int128 permanent);
/// @notice Emitted when an escrow checkpoint is created
/// @param escrowId The ID of the escrow
/// @param timestamp The timestamp of the checkpoint
/// @param slope The slope of the checkpoint
/// @param bias The bias of the checkpoint
/// @param permanent The permanent value of the checkpoint
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 / Constants.EPOCH) * Constants.EPOCH);
}
/**
* @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
* @param store_ The EscrowDelegateStore struct containing all the storage mappings
*/
function globalCheckpoint(EscrowDelegateStore storage store_) external {
globalCheckpoint(store_, 0, Checkpoints.blankPoint(), Checkpoints.blankPoint());
}
/**
* @dev Function to update global checkpoint with new points
* @param store_ The EscrowDelegateStore struct containing all the storage mappings
* @param escrowId The ID of the escrow lock
* @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 > testTime
uint256 maxTime = testTime + MAX_TIME.toUint256();
while (testTime < block.timestamp) {
testTime += Constants.EPOCH;
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 store_ The EscrowDelegateStore struct containing all the storage mappings
* @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 store_ The EscrowDelegateStore struct containing all the storage mappings
* @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 > testTime
uint256 maxTime = testTime + MAX_TIME.toUint256();
while (testTime < timestamp) {
testTime += Constants.EPOCH;
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 store_ The EscrowDelegateStore struct containing all the storage mappings
* @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 store_ The EscrowDelegateStore struct containing all the storage mappings
* @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 store_ The EscrowDelegateStore struct containing all the storage mappings
* @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 != 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 store_ The EscrowDelegateStore struct containing all the storage mappings
* @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 store_ The EscrowDelegateStore struct containing all the storage mappings
* @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 > testTime
uint256 maxTime = testTime + MAX_TIME.toUint256();
// Iterate over time until current block timestamp or maxtime
while (testTime < block.timestamp) {
testTime += Constants.EPOCH;
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 store_ The EscrowDelegateStore struct containing all the storage mappings
* @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 store_ The EscrowDelegateStore struct containing all the storage mappings
* @param timestamp Time to calculate the total voting power at
* @return The adjusted checkpoint at the provided timestamp
*/
function _getAdjustedCheckpoint(
EscrowDelegateStore storage store_,
uint48 timestamp
) internal view returns (Checkpoints.Point memory) {
(bool exists, uint48 lastCheckpointTs, Checkpoints.Point memory lastGlobal) = store_
._globalCheckpoints
.upperLookupRecent(timestamp);
if (!exists) return lastGlobal;
uint48 testTime = toGlobalClock(lastCheckpointTs); /// @dev lastCheckpointTs > testTime
uint256 maxTime = testTime + MAX_TIME.toUint256();
// Iterate over time until the specified timestamp or maxtime is reached
while (testTime < timestamp) {
testTime += Constants.EPOCH;
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(timestamp - 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 store_ The EscrowDelegateStore struct containing all the storage mappings
* @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 store_ The EscrowDelegateStore struct containing all the storage mappings
* @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);
}
/**
* @notice Get the first escrow point for `escrowId`
* @param store_ The EscrowDelegateStore struct containing all the storage mappings
* @param escrowId The ID of the escrow lock
* @return The first point and the timestamp of the first checkpoint
*/
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": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {
"contracts/VoterV5/VotingEscrow/libraries/EscrowDelegateCheckpoints.sol": {
"EscrowDelegateCheckpoints": "0xfd3d17d25c05456caa4a9d6e9feaf71aa6047102"
}
}
}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":"LockModifiedDelay","type":"error"},{"inputs":[],"name":"LockNotExpired","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":[],"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":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"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":[{"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":"","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":"_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 IVotingEscrowV2.LockDetails","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"lockModifiedAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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
60806040526303c26700610104556000610171553480156200002057600080fd5b506200002b62000041565b600161016b556200003b62000041565b62000102565b600054610100900460ff1615620000ae5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff9081161462000100576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b61584680620001126000396000f3fe608060405234801561001057600080fd5b506004361061041d5760003560e01c8063753c73281161022b578063c7d7d90f11610130578063db900b9d116100b8578063e7e242d411610087578063e7e242d414610a98578063e985e9c514610aab578063f778e0a314610abe578063fc0c546a14610ad1578063ffa1ad7414610ae557600080fd5b8063db900b9d14610a38578063e0514aba14610a4b578063e58ee17d14610a5e578063e7a324dc14610a7157600080fd5b8063d1c2babb116100ff578063d1c2babb146109cc578063d60371a7146109df578063d6d0faee146109f2578063d744515f14610a05578063d93aafb514610a1857600080fd5b8063c7d7d90f14610911578063c87b56dd1461096c578063cae57c891461097f578063d113cfba146109b957600080fd5b80639ab24eb0116101b3578063a2e8a50c11610182578063a2e8a50c1461089a578063b2383e55146108ad578063b88d4fde146108c0578063bb7cfce2146108d3578063c2c4c5c11461090957600080fd5b80639ab24eb01461084e5780639e0bd808146108615780639f19442214610874578063a22cb4651461088757600080fd5b806384b0196e116101fa57806384b0196e146107f257806384e968e61461080d5780638b9cb90b146108205780638e539e8c1461083357806395d89b411461084657600080fd5b8063753c7328146107825780637ecebe0014610795578063807c3531146107b657806381d0526d146107df57600080fd5b80633a46b1a811610331578063576561d2116102b95780636352211e116102885780636352211e146106f35780636bec7397146107065780636c423c101461071957806370a082311461076557806372fbaacb1461077857600080fd5b8063576561d21461065d578063587cde1e146106855780635c19a95c1461069857806363185237146106ab57600080fd5b8063430c208111610300578063430c2081146105ef5780634f6ccce714610602578063519a459d146106155780635594a0451461063657806356afe7441461064a57600080fd5b80633a46b1a8146105a35780633c340b32146105b657806342842e0e146105c957806342966c68146105dc57600080fd5b80631ac40e66116103b45780632f745c59116103835780632f745c591461053d578063313ce56714610550578063323418c11461056a57806335b0f6bd1461057d578063379607f51461059057600080fd5b80631ac40e66146104e5578063212d18c9146104ed57806323b872dd146105175780632b3b09ec1461052a57600080fd5b806308bbb824116103f057806308bbb824146104a2578063095ea7b3146104b75780630a2abdb3146104ca57806318160ddd146104dd57600080fd5b806301ffc9a714610422578063047fc9aa1461044a57806306fdde0314610462578063081812fc14610477575b600080fd5b610435610430366004614c91565b610b09565b60405190151581526020015b60405180910390f35b61045461016d5481565b604051908152602001610441565b61046a610b34565b6040516104419190614d06565b61048a610485366004614d19565b610bc6565b6040516001600160a01b039091168152602001610441565b6104b56104b0366004614d47565b610bed565b005b6104b56104c5366004614d77565b610d53565b6104546104d8366004614db1565b610e68565b610454610e96565b6104b5610f2f565b6105006104fb366004614d19565b610fa7565b60405165ffffffffffff9091168152602001610441565b6104b5610525366004614dfb565b610fb2565b61048a610538366004614e50565b610fe3565b61045461054b366004614d77565b611076565b610558601281565b60405160ff9091168152602001610441565b6104b5610578366004614e75565b61110c565b6104b561058b366004614d19565b611181565b6104b561059e366004614d19565b611379565b6104546105b1366004614d77565b611395565b6104546105c4366004614ea3565b61143b565b6104b56105d7366004614dfb565b61146a565b6104b56105ea366004614d19565b611485565b6104356105fd366004614d77565b6114f5565b610454610610366004614d19565b61150a565b610454610623366004614d19565b6101726020526000908152604090205481565b61016e5461048a906001600160a01b031681565b6104b5610658366004614f49565b61159d565b61067061066b366004614d19565b6118c5565b60408051928352602083019190915201610441565b61048a610693366004614ff5565b611927565b6104b56106a6366004614ff5565b611a41565b6106be6106b9366004614d19565b611a53565b604051610441919081518152602080830151908201526040808301519082015260609182015115159181019190915260800190565b61048a610701366004614d19565b611acb565b610435610714366004614d77565b611b2a565b61072c610727366004615012565b611b92565b604080518351600f90810b8252602080860151820b908301529382015190930b9083015265ffffffffffff166060820152608001610441565b610454610773366004614ff5565b611c3f565b6104546101715481565b6104b5610790366004614ff5565b611cc5565b6104546107a3366004614ff5565b61016f6020526000908152604090205481565b61048a6107c4366004614d19565b600090815260ca60205260409020546001600160a01b031690565b6104546107ed366004614d19565b611d5d565b6107fa611dba565b604051610441979695949392919061506f565b61045461081b366004614d19565b611e5a565b61048a61082e366004614d19565b611ea9565b610454610841366004614d19565b611ef5565b61046a611f8a565b61045461085c366004614ff5565b611f99565b61045461086f366004614d19565b612002565b6104b56108823660046150df565b612045565b6104b5610895366004614e75565b612237565b6104356108a8366004615118565b612242565b6104b56108bb366004615012565b61228e565b6104b56108ce3660046151ac565b612383565b6108f66108e1366004614d19565b600090815260ff6020526040902054600f0b90565b604051600f9190910b8152602001610441565b6104b56123bb565b61094a61091f366004614d19565b6101706020526000908152604090208054600182015460028301546003909301549192909160ff1684565b6040805194855260208501939093529183015215156060820152608001610441565b61046a61097a366004614d19565b6123c3565b6108f661098d366004614d77565b6001600160a01b03919091166000908152610103602090815260408083209383529290522054600f0b90565b6104b56109c736600461522c565b612406565b6104b56109da366004615012565b61249f565b6104546109ed3660046150df565b612857565b6104b5610a0036600461527c565b61287d565b610454610a13366004615012565b612a4e565b610a2b610a26366004614ff5565b612abb565b604051610441919061531e565b610454610a46366004614d19565b612beb565b610454610a59366004615012565b612bf7565b61048a610a6c366004614d19565b612c40565b6104547fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf81565b610454610aa6366004614d19565b612cbe565b610435610ab9366004615118565b612d06565b61072c610acc366004614d19565b612d34565b61016c5461048a906001600160a01b031681565b61046a604051806040016040528060058152602001640322e322e360dc1b81525081565b60006001600160e01b03198216631d80e43d60e31b1480610b2e5750610b2e82612d65565b92915050565b606060658054610b439061536b565b80601f0160208091040260200160405190810160405280929190818152602001828054610b6f9061536b565b8015610bbc5780601f10610b9157610100808354040283529160200191610bbc565b820191906000526020600020905b815481529060010190602001808311610b9f57829003601f168201915b5050505050905090565b6000610bd182612d8a565b506000908152606960205260409020546001600160a01b031690565b816000610bf982612dda565b90506001600160a01b038116610c2a57604051637e27328960e01b8152600481018390526024015b60405180910390fd5b33610c36828285612df5565b610c655760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610c21565b60008581526101706020526040808220600201549051639bdd2e4d60e01b8152829173fd3d17d25c05456caa4a9d6e9feaf71aa604710291639bdd2e4d91610cb79160fe918c918c91906004016153a5565b6040805180830381865af4158015610cd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cf791906153c9565b91509150806001600160a01b0316610d0c3390565b6001600160a01b0316886000805160206157f183398151915285604051610d4291906001600160a01b0391909116815260200190565b60405180910390a450505050505050565b6000610d5e82611acb565b9050806001600160a01b0316836001600160a01b031603610dcb5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610c21565b336001600160a01b0382161480610de75750610de78133612d06565b610e595760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c0000006064820152608401610c21565b610e638383612e48565b505050565b6000610e72612eb6565b610e8185858586866001612f11565b9050610e8e600161016b55565b949350505050565b600073fd3d17d25c05456caa4a9d6e9feaf71aa60471026334e78a9e60fe610ebd42613163565b6040516001600160e01b031960e085901b168152600481019290925265ffffffffffff166024820152604401602060405180830381865af4158015610f06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f2a91906153f8565b905090565b610f37612eb6565b604051636977e3a960e01b815260fe600482015273fd3d17d25c05456caa4a9d6e9feaf71aa604710290636977e3a99060240160006040518083038186803b158015610f8257600080fd5b505af4158015610f96573d6000803e3d6000fd5b50505050610fa5600161016b55565b565b6000610b2e82613190565b610fbc33826131aa565b610fd85760405162461bcd60e51b8152600401610c2190615411565b610e638383836131f6565b604051639511aa5360e01b815260fe60048201526024810183905265ffffffffffff8216604482015260009073fd3d17d25c05456caa4a9d6e9feaf71aa604710290639511aa5390606401602060405180830381865af415801561104b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106f919061545e565b9392505050565b600061108183611c3f565b82106110e35760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b6064820152608401610c21565b506001600160a01b03919091166000908152609760209081526040808320938352929052205490565b33600090815260cb602090815260408083206001600160a01b03861684529091529020805460ff191682151517905560405181151581526001600160a01b0383169033907f199acb87f54d9aa34d4c071192d94f5ec4e0b152f52b81209d89085dd7dade7e9060200160405180910390a35050565b611189612eb6565b80600061119582612dda565b90506001600160a01b0381166111c157604051637e27328960e01b815260048101839052602401610c21565b336111cd828285612df5565b6111fc5760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610c21565b6000848152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff1615156060820181905261126257604051632188f8ab60e01b815260040160405180910390fd5b61127461010454426104fb9190615491565b65ffffffffffff1660408083019190915260006060808401829052878252610170602090815283832084516080810186528154815260018201549281019290925260028101549482019490945260039093015460ff161515908301526112dd9187918490613367565b600085815261017060209081526040918290208351815590830151600182015590820151600282015560608201516003909101805460ff1916911515919091179055336001600160a01b0316857f7fbde6b5a06f47dab128f45cea79f339e710e4f5d9e5cda522585afe7b30539b836040015160405161135f91815260200190565b60405180910390a350505050611376600161016b55565b50565b611381612eb6565b61138a8161343b565b611376600161016b55565b600073fd3d17d25c05456caa4a9d6e9feaf71aa604710263f8183ecf60fe856113bd86613163565b6040516001600160e01b031960e086901b16815260048101939093526001600160a01b03909116602483015265ffffffffffff1660448201526064015b602060405180830381865af4158015611417573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106f91906153f8565b6000611445612eb6565b61145486868686866001612f11565b9050611461600161016b55565b95945050505050565b610e6383838360405180602001604052806000815250612383565b61148d612eb6565b3361149782612dda565b6001600160a01b0316146114be57604051630472b49d60e51b815260040160405180910390fd5b60008181526101706020526040902054156114ec576040516330a2172f60e11b815260040160405180910390fd5b61138a81613709565b600061106f61150383611acb565b8484612df5565b600061151560995490565b82106115785760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b6064820152608401610c21565b6099828154811061158b5761158b6154a9565b90600052602060002001549050919050565b6115a5612eb6565b8060006115b182612dda565b90506001600160a01b0381166115dd57604051637e27328960e01b815260048101839052602401610c21565b336115e9828285612df5565b6116185760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610c21565b6000848152610170602081815260408084208151608081018352815481526001820154818501526002820154928101928352600382015460ff16151560608201529489905292909152514290811080159061167557508260600151155b15611693576040516307b7d7dd60e51b815260040160405180910390fd5b825115806116a2575060028851105b156116c057604051631f2a200560e01b815260040160405180910390fd5b825161016d80546000906116d59084906154bf565b90915550600090506116e688612dda565b90506000805b8a5181101561172e578a8181518110611707576117076154a9565b60200260200101518261171a9190615491565b915080611726816154d6565b9150506116ec565b50806000036117505760405163108cef9d60e31b815260040160405180910390fd5b600085606001516117705783866040015161176b91906154bf565b611773565b60005b865190915060005b8c51811015611873576000848e8381518110611799576117996154a9565b60200260200101518a600001516117b091906154ef565b6117ba919061550e565b905060018e516117ca91906154bf565b82036117d35750815b6117dd81846154bf565b92508160000361184b5780885561016d8054829190600090611800908490615491565b909155505060408051608081018252895481526001808b0154602083015260028b01549282019290925260038a015460ff1615156060820152611846918f918c91613367565b611860565b61185e818588898d606001516005612f11565b505b508061186b816154d6565b91505061177b565b508a7fef2d6e0b68eee68a522999443f2c077264967f8dec5d69a61161668da51663f58d6040516118a49190615530565b60405180910390a2505050505050505050506118c1600161016b55565b5050565b6000808260006118d482612dda565b90506001600160a01b03811661190057604051637e27328960e01b815260048101839052602401610c21565b6000858152610170602052604090206001015461191c866137ac565b935093505050915091565b6000808061193484611c3f565b9050600081603211611947576032611949565b815b905060005b81811015611a375760006119628783611076565b60405163f83bb31360e01b815260fe60048201526024810182905290915060009073fd3d17d25c05456caa4a9d6e9feaf71aa60471029063f83bb31390604401602060405180830381865af41580156119bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119e3919061545e565b90506001600160a01b0386166119fb57809550611a22565b806001600160a01b0316866001600160a01b031614611a2257506001979650505050505050565b50508080611a2f906154d6565b91505061194e565b5091949350505050565b611a49612eb6565b61138a338261380d565b611a8060405180608001604052806000815260200160008152602001600081526020016000151581525090565b506000908152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff161515606082015290565b600080611ad783612dda565b90506001600160a01b038116610b2e5760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b6044820152606401610c21565b600080611b3683612dda565b9050806001600160a01b0316846001600160a01b03161480611b5d5750611b5d8185612242565b80610e8e5750600083815260ca60205260409020546001600160a01b0380861691165b6001600160a01b031614949350505050565b6040805160608101825260008082526020820181905291810191909152604051630e680a3160e41b815260fe6004820152602481018490526044810183905260009073fd3d17d25c05456caa4a9d6e9feaf71aa60471029063e680a31090606401608060405180830381865af4158015611c10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c34919061555a565b915091509250929050565b60006001600160a01b038216611ca95760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b6064820152608401610c21565b506001600160a01b031660009081526068602052604090205490565b611ccd612eb6565b604051636bd6136d60e11b815260fe60048201526001600160a01b038216602482015273fd3d17d25c05456caa4a9d6e9feaf71aa60471029063d7ac26da90604401608060405180830381865af4158015611d2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d50919061555a565b5050611376600161016b55565b6000816000611d6b82612dda565b90506001600160a01b038116611d9757604051637e27328960e01b815260048101839052602401610c21565b611da084612beb565b60008581526101706020526040902054610e8e91906154bf565b600060608060008060006060610137546000801b148015611ddc575061013854155b611e205760405162461bcd60e51b81526020600482015260156024820152741152540dcc4c8e88155b9a5b9a5d1a585b1a5e9959605a1b6044820152606401610c21565b611e286139c2565b611e306139d2565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b6000816000611e6882612dda565b90506001600160a01b038116611e9457604051637e27328960e01b815260048101839052602401610c21565b505050600090815260c9602052604090205490565b6000816000611eb782612dda565b90506001600160a01b038116611ee357604051637e27328960e01b815260048101839052602401610c21565b61016c546001600160a01b0316610e8e565b600073fd3d17d25c05456caa4a9d6e9feaf71aa60471026334e78a9e60fe611f1c85613163565b6040516001600160e01b031960e085901b168152600481019290925265ffffffffffff1660248201526044015b602060405180830381865af4158015611f66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b2e91906153f8565b606060668054610b439061536b565b600073fd3d17d25c05456caa4a9d6e9feaf71aa604710263f8183ecf60fe84611fc142613163565b6040516001600160e01b031960e086901b16815260048101939093526001600160a01b03909116602483015265ffffffffffff166044820152606401611f49565b600081600061201082612dda565b90506001600160a01b03811661203c57604051637e27328960e01b815260048101839052602401610c21565b610e8e84612beb565b61204d612eb6565b82600061205982612dda565b90506001600160a01b03811661208557604051637e27328960e01b815260048101839052602401610c21565b33612091828285612df5565b6120c05760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610c21565b6000868152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff161580156060830152612125576040516334d10f9560e11b815260040160405180910390fd5b42816040015111612149576040516307b7d7dd60e51b815260040160405180910390fd5b805160000361216b57604051631f2a200560e01b815260040160405180910390fd5b6000856121db5761217f6104fb8842615491565b65ffffffffffff169050816040015181116121ad57604051638e6b5b6760e01b815260040160405180910390fd5b610104546121bb9042615491565b8111156121db5760405163f761f1cd60e01b815260040160405180910390fd5b6121eb88600083858a60036139e2565b60408051828152871515602082015289917fa239317f70ee4cfafe56d2bc53cac05379afea1286f6088d963d7a02da99a75d910160405180910390a25050505050610e63600161016b55565b6118c1338383613bc8565b6000826001600160a01b0316826001600160a01b0316148061106f5750506001600160a01b03918216600090815260cb6020908152604080832093909416825291909152205460ff1690565b612296612eb6565b806000036122b757604051631f2a200560e01b815260040160405180910390fd5b6000828152610170602090815260408083208151608081018352815481526001820154938101939093526002810154918301919091526003015460ff16151560608201529061230584612dda565b6001600160a01b03160361232f57604051637e27328960e01b815260048101849052602401610c21565b4281604001511115801561234557508060600151155b15612363576040516307b7d7dd60e51b815260040160405180910390fd5b6123778383600084856060015160026139e2565b506118c1600161016b55565b61238d33836131aa565b6123a95760405162461bcd60e51b8152600401610c2190615411565b6123b584848484613c96565b50505050565b610fa5610f2f565b60608160006123d182612dda565b90506001600160a01b0381166123fd57604051637e27328960e01b815260048101839052602401610c21565b610e8e84613cc9565b80600061241282612dda565b90506001600160a01b03811661243e57604051637e27328960e01b815260048101839052602401610c21565b6124488584613db8565b82856001600160a01b0316336001600160a01b03167fb688daf266707251b810cde26b1bedfbbc6aa98cfc5667ea84fc354483e2ac8e87604051612490911515815260200190565b60405180910390a45050505050565b6124a7612eb6565b8160006124b382612dda565b90506001600160a01b0381166124df57604051637e27328960e01b815260048101839052602401610c21565b336124eb828285612df5565b61251a5760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610c21565b83600061252682612dda565b90506001600160a01b03811661255257604051637e27328960e01b815260048101839052602401610c21565b3361255e828285612df5565b61258d5760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610c21565b8688036125ad576040516349da877960e11b815260040160405180910390fd5b600087815261017060209081526040808320815160808101835281548082526001830154948201949094526002820154928101929092526003015460ff1615156060820152910361261157604051631f2a200560e01b815260040160405180910390fd5b4281604001511115801561262757508060600151155b15612645576040516307b7d7dd60e51b815260040160405180910390fd5b600089815261017060209081526040808320815160808101835281548082526001830154948201949094526002820154928101929092526003015460ff161515606082015291036126a957604051631f2a200560e01b815260040160405180910390fd5b806060015180156126bc57508160600151155b156126da576040516367fd3ad960e01b815260040160405180910390fd5b60008260400151826040015110156126f65782604001516126fc565b81604001515b60008c8152610170602090815260408083208381558151608081018352938452600180820154938501939093526002810154918401919091526003015460ff1615156060830152919250612753918d918591613367565b61278060405180608001604052806000815260200160008152602001600081526020016000151581525090565b8251845161278e9190615491565b81526020808501519082015260608085015115159082018190526127b457604081018290525b6127c18b85836001613367565b60008b8152610170602090815260409182902083518082558483015160018301558484015160028301556060808601516003909301805460ff191693151593841790558451918252928101869052928301528c918e917fec3b82c6fa54397270e9474a17cd38c92b766ae2b2541f87715357ac7ddd48e0910160405180910390a3505050505050505050506118c1600161016b55565b6000612861612eb6565b61287084843333866001612f11565b905061106f600161016b55565b600054610100900460ff161580801561289d5750600054600160ff909116105b806128b75750303b1580156128b7575060005460ff166001145b61291a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610c21565b6000805460ff19166001179055801561293d576000805461ff0019166101001790555b6129478686613e3e565b6129518685613e77565b61016c80546001600160a01b0319166001600160a01b038516908117909155604080516318160ddd60e01b815290516318160ddd916004808201926020929091908290030181865afa1580156129ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129cf91906153f8565b5061016e80546001600160a01b0319166001600160a01b0384161790556129f66000613cc9565b506303c26700610104558015612a46576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b6000826000612a5c82612dda565b90506001600160a01b038116612a8857604051637e27328960e01b815260048101839052602401610c21565b612a91856137ac565b8410612aae57600085815261017060205260409020549250612ab3565b600092505b505092915050565b60606000612ac883611c3f565b905060008167ffffffffffffffff811115612ae557612ae5614f02565b604051908082528060200260200182016040528015612b0e578160200160208202803683370190505b50905060005b82811015612be3576000612b288683611076565b60405163f83bb31360e01b815260fe60048201526024810182905290915073fd3d17d25c05456caa4a9d6e9feaf71aa60471029063f83bb31390604401602060405180830381865af4158015612b82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba6919061545e565b838381518110612bb857612bb86154a9565b6001600160a01b03909216602092830291909101909101525080612bdb816154d6565b915050612b14565b509392505050565b6000610b2e8242612a4e565b604051633d003cf160e21b815260fe6004820152602481018390526044810182905260009073fd3d17d25c05456caa4a9d6e9feaf71aa60471029063f400f3c4906064016113fa565b60405163f83bb31360e01b815260fe60048201526024810182905260009073fd3d17d25c05456caa4a9d6e9feaf71aa60471029063f83bb31390604401602060405180830381865af4158015612c9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b2e919061545e565b604051633d003cf160e21b815260fe60048201526024810182905242604482015260009073fd3d17d25c05456caa4a9d6e9feaf71aa60471029063f400f3c490606401611f49565b6001600160a01b039182166000908152606a6020908152604080832093909416825291909152205460ff1690565b6040805160608101825260008082526020820181905291810182905290612d5c60fe84613ea8565b91509150915091565b60006001600160e01b0319821663bd3a202b60e01b1480610b2e5750610b2e82613eef565b612d9381613f14565b6113765760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b6044820152606401610c21565b6000908152606760205260409020546001600160a01b031690565b60006001600160a01b03831615801590610e8e5750826001600160a01b0316846001600160a01b03161480612e2f5750612e2f8484612d06565b80610e8e5750826001600160a01b0316611b8083610bc6565b600081815260696020526040902080546001600160a01b0319166001600160a01b0384169081179091558190612e7d82611acb565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600261016b5403612f095760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610c21565b600261016b55565b600086600003612f3457604051631f2a200560e01b815260040160405180910390fd5b610171805460009182612f46836154d6565b90915550506101715484612fb957612f616104fb8942615491565b65ffffffffffff169150428211612f8b57604051638e6b5b6760e01b815260040160405180910390fd5b61010454612f999042615491565b821115612fb95760405163f761f1cd60e01b815260040160405180910390fd5b612fc38782613f31565b6000818152610170602090815260409182902042600182018190558351608081018552825481529283015260028101549282019290925260039091015460ff161515606082015261301b9082908b90859089896139e2565b604051639bdd2e4d60e01b815273fd3d17d25c05456caa4a9d6e9feaf71aa604710290639bdd2e4d906130599060fe9085908b9088906004016153a5565b6040805180830381865af4158015613075573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061309991906153c9565b5050604080518a8152602081018490528615158183015290516001600160a01b0388169183917f551141c76a3b7e9a59ed1e32ccc940d667b98cfd98931126e89f1a17838f43519181900360600190a36040516001600160a01b03808816916000918a16907f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f908390a4604051600081526001600160a01b03808816919089169083906000805160206157f18339815191529060200160405180910390a498975050505050505050565b600065ffffffffffff82111561318c576040516393dafdf160e01b815260040160405180910390fd5b5090565b600062093a806131a0818461550e565b610b2e91906154ef565b6000806131b683611acb565b9050806001600160a01b0316846001600160a01b031614806131dd57506131dd8185612d06565b80610e8e5750836001600160a01b0316611b8084610bc6565b826001600160a01b031661320982611acb565b6001600160a01b03161461322f5760405162461bcd60e51b8152600401610c21906155e1565b6001600160a01b0382166132915760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610c21565b61329e83838360016140ac565b826001600160a01b03166132b182611acb565b6001600160a01b0316146132d75760405162461bcd60e51b8152600401610c21906155e1565b600081815260696020908152604080832080546001600160a01b03199081169091556001600160a01b0387811680865260688552838620805460001901905590871680865283862080546001019055868652606790945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b8015613380576000848152610172602052604090204290555b60fe73fd3d17d25c05456caa4a9d6e9feaf71aa6047102634cd260569091866133ac876000015161421c565b86516133b79061421c565b6040808a01518982015191516001600160e01b031960e089901b16815260048101969096526024860194909452600f92830b6044860152910b6064840152608483019190915260a482015260c40160006040518083038186803b15801561341d57600080fd5b505af4158015613431573d6000803e3d6000fd5b5050505050505050565b80600061344782612dda565b90506001600160a01b03811661347357604051637e27328960e01b815260048101839052602401610c21565b82600061347f82612dda565b90506001600160a01b0381166134ab57604051637e27328960e01b815260048101839052602401610c21565b336134b7828285612df5565b6134e65760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610c21565b6000868152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff16158015606083015261354b576040516334d10f9560e11b815260040160405180910390fd5b600061355688612002565b9050806000036135795760405163342ad40160e11b815260040160405180910390fd5b6040805160808101825260008082526020808301828152838501838152606085018481528e8552610170909352948320935184555160018401559251600283015591516003909101805460ff191691151591909117905561016d8054918391906135e383856154bf565b909155505060008981526101706020908152604091829020825160808101845281548152600180830154938201939093526002820154938101939093526003015460ff161515606083015261363c918b91869190613367565b60405182815233908a907fe97cee5a4c0549d3fdc81e322b718ddf0aeb3418ec87dce4f9a7fb28d117c3129060200160405180910390a3600089815260c9602052604081208054849290613691908490615491565b909155506136bf905033836136af61016c546001600160a01b031690565b6001600160a01b0316919061424f565b61016d546040805183815260208101929092527f313edf9a07fc8449830af260b03eb5d8ffd3e7fc45a0c71cc26775972a4669a391015b60405180910390a1505050505050505050565b600061371482611acb565b90506137248160008460016140ac565b61372d82611acb565b600083815260696020908152604080832080546001600160a01b03199081169091556001600160a01b0385168085526068845282852080546000190190558785526067909352818420805490911690555192935084927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6000818152610170602090815260408083208151608081018352815481526001820154938101939093526002810154918301919091526003015460ff161580156060830152613803575065ffffffffffff92915050565b6040015192915050565b600061381883611c3f565b90506000805b828110156139715760006138328683611076565b60008181526101706020526040808220600201549051639bdd2e4d60e01b81529293509091829173fd3d17d25c05456caa4a9d6e9feaf71aa604710291639bdd2e4d916138899160fe9188918d91906004016153a5565b6040805180830381865af41580156138a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138c991906153c9565b91509150806001600160a01b0316886001600160a01b0316846000805160206157f18339815191528560405161390e91906001600160a01b0391909116815260200190565b60405180910390a46001600160a01b03851661392c5781945061395b565b6001600160a01b03851660011461395b57816001600160a01b0316856001600160a01b03161461395b57600194505b5050508080613969906154d6565b91505061381e565b50826001600160a01b0316816001600160a01b0316856001600160a01b03167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f60405160405180910390a450505050565b60606101398054610b439061536b565b606061013a8054610b439061536b565b61016d805490869060006139f68385615491565b9091555050604080516080810182526000808252602080830182815283850183815260608086019485528a51938b0151968b0151908b01511515909452929092529290528181529087908290613a4d908390615491565b9052508515801590613a5d575083155b15613a6a57604081018690525b8315613a7f5760006040820152600160608201525b60008881526101706020908152604091829020835181558382015160018201558383015160028201556060808501516003909201805460ff19169215159290921790915582518a81529182018990528615158284015291518a927fca0c517aab9ec63932670fd23484369811c8412142cc2535c9f78f008ecd0ce1928290030190a260006001846005811115613b1757613b17615626565b1480613b3457506002846005811115613b3257613b32615626565b145b159050613b4389878484613367565b8715801590613b6457506005846005811115613b6157613b61615626565b14155b15613bbd57613b823361016c546001600160a01b031690308b6142b2565b61016d546040805185815260208101929092527f313edf9a07fc8449830af260b03eb5d8ffd3e7fc45a0c71cc26775972a4669a391016136f6565b505050505050505050565b816001600160a01b0316836001600160a01b031603613c295760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610c21565b6001600160a01b038381166000818152606a6020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b613ca18484846131f6565b613cad848484846142ea565b6123b55760405162461bcd60e51b8152600401610c219061563c565b6000818152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff16151560608281019190915261016e549091906001600160a01b031663dd9ec14984613d3381612cbe565b604080860151865191516001600160e01b031960e087901b168152613d739493929060040193845260208401929092526040830152606082015260800190565b600060405180830381865afa158015613d90573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261106f919081019061568e565b33613dc282612dda565b6001600160a01b031614613e10573381613ddb83612dda565b6040516364283d7b60e01b81526001600160a01b03938416600482015260248101929092529091166044820152606401610c21565b600090815260ca6020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b600054610100900460ff16613e655760405162461bcd60e51b8152600401610c21906156fc565b613e6f82826143e8565b6118c1614419565b600054610100900460ff16613e9e5760405162461bcd60e51b8152600401610c21906156fc565b6118c18282614440565b60408051606081018252600080825260208201819052918101919091526000828152600284016020526040812081908190613ee2906144a1565b9890975095505050505050565b60006001600160e01b0319821663780e9d6360e01b1480610b2e5750610b2e82614586565b600080613f2083612dda565b6001600160a01b0316141592915050565b6001600160a01b038216613f875760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610c21565b613f9081613f14565b15613fdd5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610c21565b613feb6000838360016140ac565b613ff481613f14565b156140415760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610c21565b6001600160a01b038216600081815260686020908152604080832080546001019055848352606790915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6140b8848484846145d6565b60005b818110156142155760006140cf8285615491565b9050846001600160a01b0316866001600160a01b0316146142025760008181526101706020526040808220600201549051639bdd2e4d60e01b8152829173fd3d17d25c05456caa4a9d6e9feaf71aa604710291639bdd2e4d9161413c9160fe9188918d91906004016153a5565b6040805180830381865af4158015614158573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061417c91906153c9565b91509150806001600160a01b0316826001600160a01b0316886001600160a01b03167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f60405160405180910390a46040516001600160a01b038381168252808316919089169085906000805160206157f18339815191529060200160405180910390a450505b508061420d816154d6565b9150506140bb565b5050505050565b60006f7fffffffffffffffffffffffffffffff82111561318c576040516393dafdf160e01b815260040160405180910390fd5b6040516001600160a01b038316602482015260448101829052610e6390849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614659565b6040516001600160a01b03808516602483015283166044820152606481018290526123b59085906323b872dd60e01b9060840161427b565b60006001600160a01b0384163b156143e057604051630a85bd0160e11b81526001600160a01b0385169063150b7a029061432e903390899088908890600401615747565b6020604051808303816000875af1925050508015614369575060408051601f3d908101601f1916820190925261436691810190615784565b60015b6143c6573d808015614397576040519150601f19603f3d011682016040523d82523d6000602084013e61439c565b606091505b5080516000036143be5760405162461bcd60e51b8152600401610c219061563c565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610e8e565b506001610e8e565b600054610100900460ff1661440f5760405162461bcd60e51b8152600401610c21906156fc565b6118c1828261472e565b600054610100900460ff16610fa55760405162461bcd60e51b8152600401610c21906156fc565b600054610100900460ff166144675760405162461bcd60e51b8152600401610c21906156fc565b815161447b90610139906020850190614beb565b5080516144909061013a906020840190614beb565b505060006101378190556101385550565b6040805160608101825260008082526020820181905291810182905281908354600081900361451157600080614505604080516060808201835260008083526020808401829052928401819052835191820184528082529181018290529182015290565b9350935093505061457f565b600061451d868261477c565b604080518082018252825465ffffffffffff1681528151606081018352600180850154600f81810b8452600160801b909104810b602084810191909152600290960154900b93820193909352928101839052519096509450925061457f915050565b9193909250565b60006001600160e01b031982166380ac58cd60e01b14806145b757506001600160e01b03198216635b5e139f60e01b145b80610b2e57506301ffc9a760e01b6001600160e01b0319831614610b2e565b6145e2848484846147a6565b60005b818110156142155760006145f98285615491565b90506001600160a01b0386161515806146245750846001600160a01b0316866001600160a01b031614155b1561464657600081815260ca6020526040902080546001600160a01b03191690555b5080614651816154d6565b9150506145e5565b60006146ae826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166148d39092919063ffffffff16565b90508051600014806146cf5750808060200190518101906146cf91906157a1565b610e635760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610c21565b600054610100900460ff166147555760405162461bcd60e51b8152600401610c21906156fc565b8151614768906065906020850190614beb565b508051610e63906066906020840190614beb565b6000828281548110614790576147906154a9565b9060005260206000209060030201905092915050565b60018111156148155760405162461bcd60e51b815260206004820152603560248201527f455243373231456e756d657261626c653a20636f6e7365637574697665207472604482015274185b9cd9995c9cc81b9bdd081cdd5c1c1bdc9d1959605a1b6064820152608401610c21565b816001600160a01b0385166148715761486c81609980546000838152609a60205260408120829055600182018355919091527f72a152ddfb8e864297c917af52ea6c1c68aead0fee1a62673fcc7e0c94979d000155565b614894565b836001600160a01b0316856001600160a01b0316146148945761489485826148e2565b6001600160a01b0384166148b0576148ab8161497f565b614215565b846001600160a01b0316846001600160a01b031614614215576142158482614a2e565b6060610e8e8484600085614a72565b600060016148ef84611c3f565b6148f991906154bf565b60008381526098602052604090205490915080821461494c576001600160a01b03841660009081526097602090815260408083208584528252808320548484528184208190558352609890915290208190555b5060009182526098602090815260408084208490556001600160a01b039094168352609781528383209183525290812055565b609954600090614991906001906154bf565b6000838152609a6020526040812054609980549394509092849081106149b9576149b96154a9565b9060005260206000200154905080609983815481106149da576149da6154a9565b6000918252602080832090910192909255828152609a90915260408082208490558582528120556099805480614a1257614a126157be565b6001900381819060005260206000200160009055905550505050565b6000614a3983611c3f565b6001600160a01b039093166000908152609760209081526040808320868452825280832085905593825260989052919091209190915550565b606082471015614ad35760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610c21565b600080866001600160a01b03168587604051614aef91906157d4565b60006040518083038185875af1925050503d8060008114614b2c576040519150601f19603f3d011682016040523d82523d6000602084013e614b31565b606091505b5091509150614b4287838387614b4d565b979650505050505050565b60608315614bbc578251600003614bb5576001600160a01b0385163b614bb55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610c21565b5081610e8e565b610e8e8383815115614bd15781518083602001fd5b8060405162461bcd60e51b8152600401610c219190614d06565b828054614bf79061536b565b90600052602060002090601f016020900481019282614c195760008555614c5f565b82601f10614c3257805160ff1916838001178555614c5f565b82800160010185558215614c5f579182015b82811115614c5f578251825591602001919060010190614c44565b5061318c9291505b8082111561318c5760008155600101614c67565b6001600160e01b03198116811461137657600080fd5b600060208284031215614ca357600080fd5b813561106f81614c7b565b60005b83811015614cc9578181015183820152602001614cb1565b838111156123b55750506000910152565b60008151808452614cf2816020860160208601614cae565b601f01601f19169290920160200192915050565b60208152600061106f6020830184614cda565b600060208284031215614d2b57600080fd5b5035919050565b6001600160a01b038116811461137657600080fd5b60008060408385031215614d5a57600080fd5b823591506020830135614d6c81614d32565b809150509250929050565b60008060408385031215614d8a57600080fd5b8235614d9581614d32565b946020939093013593505050565b801515811461137657600080fd5b60008060008060808587031215614dc757600080fd5b84359350602085013592506040850135614de081614d32565b91506060850135614df081614da3565b939692955090935050565b600080600060608486031215614e1057600080fd5b8335614e1b81614d32565b92506020840135614e2b81614d32565b929592945050506040919091013590565b65ffffffffffff8116811461137657600080fd5b60008060408385031215614e6357600080fd5b823591506020830135614d6c81614e3c565b60008060408385031215614e8857600080fd5b8235614e9381614d32565b91506020830135614d6c81614da3565b600080600080600060a08688031215614ebb57600080fd5b85359450602086013593506040860135614ed481614d32565b92506060860135614ee481614d32565b91506080860135614ef481614da3565b809150509295509295909350565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715614f4157614f41614f02565b604052919050565b60008060408385031215614f5c57600080fd5b823567ffffffffffffffff80821115614f7457600080fd5b818501915085601f830112614f8857600080fd5b8135602082821115614f9c57614f9c614f02565b8160051b9250614fad818401614f18565b8281529284018101928181019089851115614fc757600080fd5b948201945b84861015614fe557853582529482019490820190614fcc565b9997909101359750505050505050565b60006020828403121561500757600080fd5b813561106f81614d32565b6000806040838503121561502557600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b8381101561506457815187529582019590820190600101615048565b509495945050505050565b60ff60f81b8816815260e06020820152600061508e60e0830189614cda565b82810360408401526150a08189614cda565b606084018890526001600160a01b038716608085015260a0840186905283810360c085015290506150d18185615034565b9a9950505050505050505050565b6000806000606084860312156150f457600080fd5b8335925060208401359150604084013561510d81614da3565b809150509250925092565b6000806040838503121561512b57600080fd5b823561513681614d32565b91506020830135614d6c81614d32565b600067ffffffffffffffff82111561516057615160614f02565b50601f01601f191660200190565b600061518161517c84615146565b614f18565b905082815283838301111561519557600080fd5b828260208301376000602084830101529392505050565b600080600080608085870312156151c257600080fd5b84356151cd81614d32565b935060208501356151dd81614d32565b925060408501359150606085013567ffffffffffffffff81111561520057600080fd5b8501601f8101871361521157600080fd5b6152208782356020840161516e565b91505092959194509250565b60008060006060848603121561524157600080fd5b833561524c81614d32565b92506020840135614e2b81614da3565b600082601f83011261526d57600080fd5b61106f8383356020850161516e565b600080600080600060a0868803121561529457600080fd5b853567ffffffffffffffff808211156152ac57600080fd5b6152b889838a0161525c565b965060208801359150808211156152ce57600080fd5b6152da89838a0161525c565b955060408801359150808211156152f057600080fd5b506152fd8882890161525c565b935050606086013561530e81614d32565b91506080860135614ef481614d32565b6020808252825182820181905260009190848201906040850190845b8181101561535f5783516001600160a01b03168352928401929184019160010161533a565b50909695505050505050565b600181811c9082168061537f57607f821691505b60208210810361539f57634e487b7160e01b600052602260045260246000fd5b50919050565b93845260208401929092526001600160a01b03166040830152606082015260800190565b600080604083850312156153dc57600080fd5b82516153e781614d32565b6020840151909250614d6c81614d32565b60006020828403121561540a57600080fd5b5051919050565b6020808252602d908201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560408201526c1c881bdc88185c1c1c9bdd9959609a1b606082015260800190565b60006020828403121561547057600080fd5b815161106f81614d32565b634e487b7160e01b600052601160045260246000fd5b600082198211156154a4576154a461547b565b500190565b634e487b7160e01b600052603260045260246000fd5b6000828210156154d1576154d161547b565b500390565b6000600182016154e8576154e861547b565b5060010190565b60008160001904831182151516156155095761550961547b565b500290565b60008261552b57634e487b7160e01b600052601260045260246000fd5b500490565b60208152600061106f6020830184615034565b8051600f81900b811461555557600080fd5b919050565b600080828403608081121561556e57600080fd5b606081121561557c57600080fd5b506040516060810181811067ffffffffffffffff821117156155a0576155a0614f02565b6040526155ac84615543565b81526155ba60208501615543565b60208201526155cb60408501615543565b60408201526060840151909250614d6c81614e3c565b60208082526025908201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060408201526437bbb732b960d91b606082015260800190565b634e487b7160e01b600052602160045260246000fd5b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b6000602082840312156156a057600080fd5b815167ffffffffffffffff8111156156b757600080fd5b8201601f810184136156c857600080fd5b80516156d661517c82615146565b8181528560208385010111156156eb57600080fd5b611461826020830160208601614cae565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061577a90830184614cda565b9695505050505050565b60006020828403121561579657600080fd5b815161106f81614c7b565b6000602082840312156157b357600080fd5b815161106f81614da3565b634e487b7160e01b600052603160045260246000fd5b600082516157e6818460208701614cae565b919091019291505056fe39d7dc46c82aa27d4484a57e21232e5c6f22fe6c7ffc01795304084ba872811ea2646970667358221220c4074109288fbd2d6dbc54597cd95e2a16ebe8798f70999b72aec6c8b9cffa2464736f6c634300080d0033
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061041d5760003560e01c8063753c73281161022b578063c7d7d90f11610130578063db900b9d116100b8578063e7e242d411610087578063e7e242d414610a98578063e985e9c514610aab578063f778e0a314610abe578063fc0c546a14610ad1578063ffa1ad7414610ae557600080fd5b8063db900b9d14610a38578063e0514aba14610a4b578063e58ee17d14610a5e578063e7a324dc14610a7157600080fd5b8063d1c2babb116100ff578063d1c2babb146109cc578063d60371a7146109df578063d6d0faee146109f2578063d744515f14610a05578063d93aafb514610a1857600080fd5b8063c7d7d90f14610911578063c87b56dd1461096c578063cae57c891461097f578063d113cfba146109b957600080fd5b80639ab24eb0116101b3578063a2e8a50c11610182578063a2e8a50c1461089a578063b2383e55146108ad578063b88d4fde146108c0578063bb7cfce2146108d3578063c2c4c5c11461090957600080fd5b80639ab24eb01461084e5780639e0bd808146108615780639f19442214610874578063a22cb4651461088757600080fd5b806384b0196e116101fa57806384b0196e146107f257806384e968e61461080d5780638b9cb90b146108205780638e539e8c1461083357806395d89b411461084657600080fd5b8063753c7328146107825780637ecebe0014610795578063807c3531146107b657806381d0526d146107df57600080fd5b80633a46b1a811610331578063576561d2116102b95780636352211e116102885780636352211e146106f35780636bec7397146107065780636c423c101461071957806370a082311461076557806372fbaacb1461077857600080fd5b8063576561d21461065d578063587cde1e146106855780635c19a95c1461069857806363185237146106ab57600080fd5b8063430c208111610300578063430c2081146105ef5780634f6ccce714610602578063519a459d146106155780635594a0451461063657806356afe7441461064a57600080fd5b80633a46b1a8146105a35780633c340b32146105b657806342842e0e146105c957806342966c68146105dc57600080fd5b80631ac40e66116103b45780632f745c59116103835780632f745c591461053d578063313ce56714610550578063323418c11461056a57806335b0f6bd1461057d578063379607f51461059057600080fd5b80631ac40e66146104e5578063212d18c9146104ed57806323b872dd146105175780632b3b09ec1461052a57600080fd5b806308bbb824116103f057806308bbb824146104a2578063095ea7b3146104b75780630a2abdb3146104ca57806318160ddd146104dd57600080fd5b806301ffc9a714610422578063047fc9aa1461044a57806306fdde0314610462578063081812fc14610477575b600080fd5b610435610430366004614c91565b610b09565b60405190151581526020015b60405180910390f35b61045461016d5481565b604051908152602001610441565b61046a610b34565b6040516104419190614d06565b61048a610485366004614d19565b610bc6565b6040516001600160a01b039091168152602001610441565b6104b56104b0366004614d47565b610bed565b005b6104b56104c5366004614d77565b610d53565b6104546104d8366004614db1565b610e68565b610454610e96565b6104b5610f2f565b6105006104fb366004614d19565b610fa7565b60405165ffffffffffff9091168152602001610441565b6104b5610525366004614dfb565b610fb2565b61048a610538366004614e50565b610fe3565b61045461054b366004614d77565b611076565b610558601281565b60405160ff9091168152602001610441565b6104b5610578366004614e75565b61110c565b6104b561058b366004614d19565b611181565b6104b561059e366004614d19565b611379565b6104546105b1366004614d77565b611395565b6104546105c4366004614ea3565b61143b565b6104b56105d7366004614dfb565b61146a565b6104b56105ea366004614d19565b611485565b6104356105fd366004614d77565b6114f5565b610454610610366004614d19565b61150a565b610454610623366004614d19565b6101726020526000908152604090205481565b61016e5461048a906001600160a01b031681565b6104b5610658366004614f49565b61159d565b61067061066b366004614d19565b6118c5565b60408051928352602083019190915201610441565b61048a610693366004614ff5565b611927565b6104b56106a6366004614ff5565b611a41565b6106be6106b9366004614d19565b611a53565b604051610441919081518152602080830151908201526040808301519082015260609182015115159181019190915260800190565b61048a610701366004614d19565b611acb565b610435610714366004614d77565b611b2a565b61072c610727366004615012565b611b92565b604080518351600f90810b8252602080860151820b908301529382015190930b9083015265ffffffffffff166060820152608001610441565b610454610773366004614ff5565b611c3f565b6104546101715481565b6104b5610790366004614ff5565b611cc5565b6104546107a3366004614ff5565b61016f6020526000908152604090205481565b61048a6107c4366004614d19565b600090815260ca60205260409020546001600160a01b031690565b6104546107ed366004614d19565b611d5d565b6107fa611dba565b604051610441979695949392919061506f565b61045461081b366004614d19565b611e5a565b61048a61082e366004614d19565b611ea9565b610454610841366004614d19565b611ef5565b61046a611f8a565b61045461085c366004614ff5565b611f99565b61045461086f366004614d19565b612002565b6104b56108823660046150df565b612045565b6104b5610895366004614e75565b612237565b6104356108a8366004615118565b612242565b6104b56108bb366004615012565b61228e565b6104b56108ce3660046151ac565b612383565b6108f66108e1366004614d19565b600090815260ff6020526040902054600f0b90565b604051600f9190910b8152602001610441565b6104b56123bb565b61094a61091f366004614d19565b6101706020526000908152604090208054600182015460028301546003909301549192909160ff1684565b6040805194855260208501939093529183015215156060820152608001610441565b61046a61097a366004614d19565b6123c3565b6108f661098d366004614d77565b6001600160a01b03919091166000908152610103602090815260408083209383529290522054600f0b90565b6104b56109c736600461522c565b612406565b6104b56109da366004615012565b61249f565b6104546109ed3660046150df565b612857565b6104b5610a0036600461527c565b61287d565b610454610a13366004615012565b612a4e565b610a2b610a26366004614ff5565b612abb565b604051610441919061531e565b610454610a46366004614d19565b612beb565b610454610a59366004615012565b612bf7565b61048a610a6c366004614d19565b612c40565b6104547fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf81565b610454610aa6366004614d19565b612cbe565b610435610ab9366004615118565b612d06565b61072c610acc366004614d19565b612d34565b61016c5461048a906001600160a01b031681565b61046a604051806040016040528060058152602001640322e322e360dc1b81525081565b60006001600160e01b03198216631d80e43d60e31b1480610b2e5750610b2e82612d65565b92915050565b606060658054610b439061536b565b80601f0160208091040260200160405190810160405280929190818152602001828054610b6f9061536b565b8015610bbc5780601f10610b9157610100808354040283529160200191610bbc565b820191906000526020600020905b815481529060010190602001808311610b9f57829003601f168201915b5050505050905090565b6000610bd182612d8a565b506000908152606960205260409020546001600160a01b031690565b816000610bf982612dda565b90506001600160a01b038116610c2a57604051637e27328960e01b8152600481018390526024015b60405180910390fd5b33610c36828285612df5565b610c655760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610c21565b60008581526101706020526040808220600201549051639bdd2e4d60e01b8152829173fd3d17d25c05456caa4a9d6e9feaf71aa604710291639bdd2e4d91610cb79160fe918c918c91906004016153a5565b6040805180830381865af4158015610cd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cf791906153c9565b91509150806001600160a01b0316610d0c3390565b6001600160a01b0316886000805160206157f183398151915285604051610d4291906001600160a01b0391909116815260200190565b60405180910390a450505050505050565b6000610d5e82611acb565b9050806001600160a01b0316836001600160a01b031603610dcb5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610c21565b336001600160a01b0382161480610de75750610de78133612d06565b610e595760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c0000006064820152608401610c21565b610e638383612e48565b505050565b6000610e72612eb6565b610e8185858586866001612f11565b9050610e8e600161016b55565b949350505050565b600073fd3d17d25c05456caa4a9d6e9feaf71aa60471026334e78a9e60fe610ebd42613163565b6040516001600160e01b031960e085901b168152600481019290925265ffffffffffff166024820152604401602060405180830381865af4158015610f06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f2a91906153f8565b905090565b610f37612eb6565b604051636977e3a960e01b815260fe600482015273fd3d17d25c05456caa4a9d6e9feaf71aa604710290636977e3a99060240160006040518083038186803b158015610f8257600080fd5b505af4158015610f96573d6000803e3d6000fd5b50505050610fa5600161016b55565b565b6000610b2e82613190565b610fbc33826131aa565b610fd85760405162461bcd60e51b8152600401610c2190615411565b610e638383836131f6565b604051639511aa5360e01b815260fe60048201526024810183905265ffffffffffff8216604482015260009073fd3d17d25c05456caa4a9d6e9feaf71aa604710290639511aa5390606401602060405180830381865af415801561104b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106f919061545e565b9392505050565b600061108183611c3f565b82106110e35760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b6064820152608401610c21565b506001600160a01b03919091166000908152609760209081526040808320938352929052205490565b33600090815260cb602090815260408083206001600160a01b03861684529091529020805460ff191682151517905560405181151581526001600160a01b0383169033907f199acb87f54d9aa34d4c071192d94f5ec4e0b152f52b81209d89085dd7dade7e9060200160405180910390a35050565b611189612eb6565b80600061119582612dda565b90506001600160a01b0381166111c157604051637e27328960e01b815260048101839052602401610c21565b336111cd828285612df5565b6111fc5760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610c21565b6000848152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff1615156060820181905261126257604051632188f8ab60e01b815260040160405180910390fd5b61127461010454426104fb9190615491565b65ffffffffffff1660408083019190915260006060808401829052878252610170602090815283832084516080810186528154815260018201549281019290925260028101549482019490945260039093015460ff161515908301526112dd9187918490613367565b600085815261017060209081526040918290208351815590830151600182015590820151600282015560608201516003909101805460ff1916911515919091179055336001600160a01b0316857f7fbde6b5a06f47dab128f45cea79f339e710e4f5d9e5cda522585afe7b30539b836040015160405161135f91815260200190565b60405180910390a350505050611376600161016b55565b50565b611381612eb6565b61138a8161343b565b611376600161016b55565b600073fd3d17d25c05456caa4a9d6e9feaf71aa604710263f8183ecf60fe856113bd86613163565b6040516001600160e01b031960e086901b16815260048101939093526001600160a01b03909116602483015265ffffffffffff1660448201526064015b602060405180830381865af4158015611417573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106f91906153f8565b6000611445612eb6565b61145486868686866001612f11565b9050611461600161016b55565b95945050505050565b610e6383838360405180602001604052806000815250612383565b61148d612eb6565b3361149782612dda565b6001600160a01b0316146114be57604051630472b49d60e51b815260040160405180910390fd5b60008181526101706020526040902054156114ec576040516330a2172f60e11b815260040160405180910390fd5b61138a81613709565b600061106f61150383611acb565b8484612df5565b600061151560995490565b82106115785760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b6064820152608401610c21565b6099828154811061158b5761158b6154a9565b90600052602060002001549050919050565b6115a5612eb6565b8060006115b182612dda565b90506001600160a01b0381166115dd57604051637e27328960e01b815260048101839052602401610c21565b336115e9828285612df5565b6116185760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610c21565b6000848152610170602081815260408084208151608081018352815481526001820154818501526002820154928101928352600382015460ff16151560608201529489905292909152514290811080159061167557508260600151155b15611693576040516307b7d7dd60e51b815260040160405180910390fd5b825115806116a2575060028851105b156116c057604051631f2a200560e01b815260040160405180910390fd5b825161016d80546000906116d59084906154bf565b90915550600090506116e688612dda565b90506000805b8a5181101561172e578a8181518110611707576117076154a9565b60200260200101518261171a9190615491565b915080611726816154d6565b9150506116ec565b50806000036117505760405163108cef9d60e31b815260040160405180910390fd5b600085606001516117705783866040015161176b91906154bf565b611773565b60005b865190915060005b8c51811015611873576000848e8381518110611799576117996154a9565b60200260200101518a600001516117b091906154ef565b6117ba919061550e565b905060018e516117ca91906154bf565b82036117d35750815b6117dd81846154bf565b92508160000361184b5780885561016d8054829190600090611800908490615491565b909155505060408051608081018252895481526001808b0154602083015260028b01549282019290925260038a015460ff1615156060820152611846918f918c91613367565b611860565b61185e818588898d606001516005612f11565b505b508061186b816154d6565b91505061177b565b508a7fef2d6e0b68eee68a522999443f2c077264967f8dec5d69a61161668da51663f58d6040516118a49190615530565b60405180910390a2505050505050505050506118c1600161016b55565b5050565b6000808260006118d482612dda565b90506001600160a01b03811661190057604051637e27328960e01b815260048101839052602401610c21565b6000858152610170602052604090206001015461191c866137ac565b935093505050915091565b6000808061193484611c3f565b9050600081603211611947576032611949565b815b905060005b81811015611a375760006119628783611076565b60405163f83bb31360e01b815260fe60048201526024810182905290915060009073fd3d17d25c05456caa4a9d6e9feaf71aa60471029063f83bb31390604401602060405180830381865af41580156119bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119e3919061545e565b90506001600160a01b0386166119fb57809550611a22565b806001600160a01b0316866001600160a01b031614611a2257506001979650505050505050565b50508080611a2f906154d6565b91505061194e565b5091949350505050565b611a49612eb6565b61138a338261380d565b611a8060405180608001604052806000815260200160008152602001600081526020016000151581525090565b506000908152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff161515606082015290565b600080611ad783612dda565b90506001600160a01b038116610b2e5760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b6044820152606401610c21565b600080611b3683612dda565b9050806001600160a01b0316846001600160a01b03161480611b5d5750611b5d8185612242565b80610e8e5750600083815260ca60205260409020546001600160a01b0380861691165b6001600160a01b031614949350505050565b6040805160608101825260008082526020820181905291810191909152604051630e680a3160e41b815260fe6004820152602481018490526044810183905260009073fd3d17d25c05456caa4a9d6e9feaf71aa60471029063e680a31090606401608060405180830381865af4158015611c10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c34919061555a565b915091509250929050565b60006001600160a01b038216611ca95760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b6064820152608401610c21565b506001600160a01b031660009081526068602052604090205490565b611ccd612eb6565b604051636bd6136d60e11b815260fe60048201526001600160a01b038216602482015273fd3d17d25c05456caa4a9d6e9feaf71aa60471029063d7ac26da90604401608060405180830381865af4158015611d2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d50919061555a565b5050611376600161016b55565b6000816000611d6b82612dda565b90506001600160a01b038116611d9757604051637e27328960e01b815260048101839052602401610c21565b611da084612beb565b60008581526101706020526040902054610e8e91906154bf565b600060608060008060006060610137546000801b148015611ddc575061013854155b611e205760405162461bcd60e51b81526020600482015260156024820152741152540dcc4c8e88155b9a5b9a5d1a585b1a5e9959605a1b6044820152606401610c21565b611e286139c2565b611e306139d2565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b6000816000611e6882612dda565b90506001600160a01b038116611e9457604051637e27328960e01b815260048101839052602401610c21565b505050600090815260c9602052604090205490565b6000816000611eb782612dda565b90506001600160a01b038116611ee357604051637e27328960e01b815260048101839052602401610c21565b61016c546001600160a01b0316610e8e565b600073fd3d17d25c05456caa4a9d6e9feaf71aa60471026334e78a9e60fe611f1c85613163565b6040516001600160e01b031960e085901b168152600481019290925265ffffffffffff1660248201526044015b602060405180830381865af4158015611f66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b2e91906153f8565b606060668054610b439061536b565b600073fd3d17d25c05456caa4a9d6e9feaf71aa604710263f8183ecf60fe84611fc142613163565b6040516001600160e01b031960e086901b16815260048101939093526001600160a01b03909116602483015265ffffffffffff166044820152606401611f49565b600081600061201082612dda565b90506001600160a01b03811661203c57604051637e27328960e01b815260048101839052602401610c21565b610e8e84612beb565b61204d612eb6565b82600061205982612dda565b90506001600160a01b03811661208557604051637e27328960e01b815260048101839052602401610c21565b33612091828285612df5565b6120c05760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610c21565b6000868152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff161580156060830152612125576040516334d10f9560e11b815260040160405180910390fd5b42816040015111612149576040516307b7d7dd60e51b815260040160405180910390fd5b805160000361216b57604051631f2a200560e01b815260040160405180910390fd5b6000856121db5761217f6104fb8842615491565b65ffffffffffff169050816040015181116121ad57604051638e6b5b6760e01b815260040160405180910390fd5b610104546121bb9042615491565b8111156121db5760405163f761f1cd60e01b815260040160405180910390fd5b6121eb88600083858a60036139e2565b60408051828152871515602082015289917fa239317f70ee4cfafe56d2bc53cac05379afea1286f6088d963d7a02da99a75d910160405180910390a25050505050610e63600161016b55565b6118c1338383613bc8565b6000826001600160a01b0316826001600160a01b0316148061106f5750506001600160a01b03918216600090815260cb6020908152604080832093909416825291909152205460ff1690565b612296612eb6565b806000036122b757604051631f2a200560e01b815260040160405180910390fd5b6000828152610170602090815260408083208151608081018352815481526001820154938101939093526002810154918301919091526003015460ff16151560608201529061230584612dda565b6001600160a01b03160361232f57604051637e27328960e01b815260048101849052602401610c21565b4281604001511115801561234557508060600151155b15612363576040516307b7d7dd60e51b815260040160405180910390fd5b6123778383600084856060015160026139e2565b506118c1600161016b55565b61238d33836131aa565b6123a95760405162461bcd60e51b8152600401610c2190615411565b6123b584848484613c96565b50505050565b610fa5610f2f565b60608160006123d182612dda565b90506001600160a01b0381166123fd57604051637e27328960e01b815260048101839052602401610c21565b610e8e84613cc9565b80600061241282612dda565b90506001600160a01b03811661243e57604051637e27328960e01b815260048101839052602401610c21565b6124488584613db8565b82856001600160a01b0316336001600160a01b03167fb688daf266707251b810cde26b1bedfbbc6aa98cfc5667ea84fc354483e2ac8e87604051612490911515815260200190565b60405180910390a45050505050565b6124a7612eb6565b8160006124b382612dda565b90506001600160a01b0381166124df57604051637e27328960e01b815260048101839052602401610c21565b336124eb828285612df5565b61251a5760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610c21565b83600061252682612dda565b90506001600160a01b03811661255257604051637e27328960e01b815260048101839052602401610c21565b3361255e828285612df5565b61258d5760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610c21565b8688036125ad576040516349da877960e11b815260040160405180910390fd5b600087815261017060209081526040808320815160808101835281548082526001830154948201949094526002820154928101929092526003015460ff1615156060820152910361261157604051631f2a200560e01b815260040160405180910390fd5b4281604001511115801561262757508060600151155b15612645576040516307b7d7dd60e51b815260040160405180910390fd5b600089815261017060209081526040808320815160808101835281548082526001830154948201949094526002820154928101929092526003015460ff161515606082015291036126a957604051631f2a200560e01b815260040160405180910390fd5b806060015180156126bc57508160600151155b156126da576040516367fd3ad960e01b815260040160405180910390fd5b60008260400151826040015110156126f65782604001516126fc565b81604001515b60008c8152610170602090815260408083208381558151608081018352938452600180820154938501939093526002810154918401919091526003015460ff1615156060830152919250612753918d918591613367565b61278060405180608001604052806000815260200160008152602001600081526020016000151581525090565b8251845161278e9190615491565b81526020808501519082015260608085015115159082018190526127b457604081018290525b6127c18b85836001613367565b60008b8152610170602090815260409182902083518082558483015160018301558484015160028301556060808601516003909301805460ff191693151593841790558451918252928101869052928301528c918e917fec3b82c6fa54397270e9474a17cd38c92b766ae2b2541f87715357ac7ddd48e0910160405180910390a3505050505050505050506118c1600161016b55565b6000612861612eb6565b61287084843333866001612f11565b905061106f600161016b55565b600054610100900460ff161580801561289d5750600054600160ff909116105b806128b75750303b1580156128b7575060005460ff166001145b61291a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610c21565b6000805460ff19166001179055801561293d576000805461ff0019166101001790555b6129478686613e3e565b6129518685613e77565b61016c80546001600160a01b0319166001600160a01b038516908117909155604080516318160ddd60e01b815290516318160ddd916004808201926020929091908290030181865afa1580156129ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129cf91906153f8565b5061016e80546001600160a01b0319166001600160a01b0384161790556129f66000613cc9565b506303c26700610104558015612a46576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b6000826000612a5c82612dda565b90506001600160a01b038116612a8857604051637e27328960e01b815260048101839052602401610c21565b612a91856137ac565b8410612aae57600085815261017060205260409020549250612ab3565b600092505b505092915050565b60606000612ac883611c3f565b905060008167ffffffffffffffff811115612ae557612ae5614f02565b604051908082528060200260200182016040528015612b0e578160200160208202803683370190505b50905060005b82811015612be3576000612b288683611076565b60405163f83bb31360e01b815260fe60048201526024810182905290915073fd3d17d25c05456caa4a9d6e9feaf71aa60471029063f83bb31390604401602060405180830381865af4158015612b82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba6919061545e565b838381518110612bb857612bb86154a9565b6001600160a01b03909216602092830291909101909101525080612bdb816154d6565b915050612b14565b509392505050565b6000610b2e8242612a4e565b604051633d003cf160e21b815260fe6004820152602481018390526044810182905260009073fd3d17d25c05456caa4a9d6e9feaf71aa60471029063f400f3c4906064016113fa565b60405163f83bb31360e01b815260fe60048201526024810182905260009073fd3d17d25c05456caa4a9d6e9feaf71aa60471029063f83bb31390604401602060405180830381865af4158015612c9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b2e919061545e565b604051633d003cf160e21b815260fe60048201526024810182905242604482015260009073fd3d17d25c05456caa4a9d6e9feaf71aa60471029063f400f3c490606401611f49565b6001600160a01b039182166000908152606a6020908152604080832093909416825291909152205460ff1690565b6040805160608101825260008082526020820181905291810182905290612d5c60fe84613ea8565b91509150915091565b60006001600160e01b0319821663bd3a202b60e01b1480610b2e5750610b2e82613eef565b612d9381613f14565b6113765760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b6044820152606401610c21565b6000908152606760205260409020546001600160a01b031690565b60006001600160a01b03831615801590610e8e5750826001600160a01b0316846001600160a01b03161480612e2f5750612e2f8484612d06565b80610e8e5750826001600160a01b0316611b8083610bc6565b600081815260696020526040902080546001600160a01b0319166001600160a01b0384169081179091558190612e7d82611acb565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600261016b5403612f095760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610c21565b600261016b55565b600086600003612f3457604051631f2a200560e01b815260040160405180910390fd5b610171805460009182612f46836154d6565b90915550506101715484612fb957612f616104fb8942615491565b65ffffffffffff169150428211612f8b57604051638e6b5b6760e01b815260040160405180910390fd5b61010454612f999042615491565b821115612fb95760405163f761f1cd60e01b815260040160405180910390fd5b612fc38782613f31565b6000818152610170602090815260409182902042600182018190558351608081018552825481529283015260028101549282019290925260039091015460ff161515606082015261301b9082908b90859089896139e2565b604051639bdd2e4d60e01b815273fd3d17d25c05456caa4a9d6e9feaf71aa604710290639bdd2e4d906130599060fe9085908b9088906004016153a5565b6040805180830381865af4158015613075573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061309991906153c9565b5050604080518a8152602081018490528615158183015290516001600160a01b0388169183917f551141c76a3b7e9a59ed1e32ccc940d667b98cfd98931126e89f1a17838f43519181900360600190a36040516001600160a01b03808816916000918a16907f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f908390a4604051600081526001600160a01b03808816919089169083906000805160206157f18339815191529060200160405180910390a498975050505050505050565b600065ffffffffffff82111561318c576040516393dafdf160e01b815260040160405180910390fd5b5090565b600062093a806131a0818461550e565b610b2e91906154ef565b6000806131b683611acb565b9050806001600160a01b0316846001600160a01b031614806131dd57506131dd8185612d06565b80610e8e5750836001600160a01b0316611b8084610bc6565b826001600160a01b031661320982611acb565b6001600160a01b03161461322f5760405162461bcd60e51b8152600401610c21906155e1565b6001600160a01b0382166132915760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610c21565b61329e83838360016140ac565b826001600160a01b03166132b182611acb565b6001600160a01b0316146132d75760405162461bcd60e51b8152600401610c21906155e1565b600081815260696020908152604080832080546001600160a01b03199081169091556001600160a01b0387811680865260688552838620805460001901905590871680865283862080546001019055868652606790945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b8015613380576000848152610172602052604090204290555b60fe73fd3d17d25c05456caa4a9d6e9feaf71aa6047102634cd260569091866133ac876000015161421c565b86516133b79061421c565b6040808a01518982015191516001600160e01b031960e089901b16815260048101969096526024860194909452600f92830b6044860152910b6064840152608483019190915260a482015260c40160006040518083038186803b15801561341d57600080fd5b505af4158015613431573d6000803e3d6000fd5b5050505050505050565b80600061344782612dda565b90506001600160a01b03811661347357604051637e27328960e01b815260048101839052602401610c21565b82600061347f82612dda565b90506001600160a01b0381166134ab57604051637e27328960e01b815260048101839052602401610c21565b336134b7828285612df5565b6134e65760405163177e802f60e01b81526001600160a01b038216600482015260248101849052604401610c21565b6000868152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff16158015606083015261354b576040516334d10f9560e11b815260040160405180910390fd5b600061355688612002565b9050806000036135795760405163342ad40160e11b815260040160405180910390fd5b6040805160808101825260008082526020808301828152838501838152606085018481528e8552610170909352948320935184555160018401559251600283015591516003909101805460ff191691151591909117905561016d8054918391906135e383856154bf565b909155505060008981526101706020908152604091829020825160808101845281548152600180830154938201939093526002820154938101939093526003015460ff161515606083015261363c918b91869190613367565b60405182815233908a907fe97cee5a4c0549d3fdc81e322b718ddf0aeb3418ec87dce4f9a7fb28d117c3129060200160405180910390a3600089815260c9602052604081208054849290613691908490615491565b909155506136bf905033836136af61016c546001600160a01b031690565b6001600160a01b0316919061424f565b61016d546040805183815260208101929092527f313edf9a07fc8449830af260b03eb5d8ffd3e7fc45a0c71cc26775972a4669a391015b60405180910390a1505050505050505050565b600061371482611acb565b90506137248160008460016140ac565b61372d82611acb565b600083815260696020908152604080832080546001600160a01b03199081169091556001600160a01b0385168085526068845282852080546000190190558785526067909352818420805490911690555192935084927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6000818152610170602090815260408083208151608081018352815481526001820154938101939093526002810154918301919091526003015460ff161580156060830152613803575065ffffffffffff92915050565b6040015192915050565b600061381883611c3f565b90506000805b828110156139715760006138328683611076565b60008181526101706020526040808220600201549051639bdd2e4d60e01b81529293509091829173fd3d17d25c05456caa4a9d6e9feaf71aa604710291639bdd2e4d916138899160fe9188918d91906004016153a5565b6040805180830381865af41580156138a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138c991906153c9565b91509150806001600160a01b0316886001600160a01b0316846000805160206157f18339815191528560405161390e91906001600160a01b0391909116815260200190565b60405180910390a46001600160a01b03851661392c5781945061395b565b6001600160a01b03851660011461395b57816001600160a01b0316856001600160a01b03161461395b57600194505b5050508080613969906154d6565b91505061381e565b50826001600160a01b0316816001600160a01b0316856001600160a01b03167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f60405160405180910390a450505050565b60606101398054610b439061536b565b606061013a8054610b439061536b565b61016d805490869060006139f68385615491565b9091555050604080516080810182526000808252602080830182815283850183815260608086019485528a51938b0151968b0151908b01511515909452929092529290528181529087908290613a4d908390615491565b9052508515801590613a5d575083155b15613a6a57604081018690525b8315613a7f5760006040820152600160608201525b60008881526101706020908152604091829020835181558382015160018201558383015160028201556060808501516003909201805460ff19169215159290921790915582518a81529182018990528615158284015291518a927fca0c517aab9ec63932670fd23484369811c8412142cc2535c9f78f008ecd0ce1928290030190a260006001846005811115613b1757613b17615626565b1480613b3457506002846005811115613b3257613b32615626565b145b159050613b4389878484613367565b8715801590613b6457506005846005811115613b6157613b61615626565b14155b15613bbd57613b823361016c546001600160a01b031690308b6142b2565b61016d546040805185815260208101929092527f313edf9a07fc8449830af260b03eb5d8ffd3e7fc45a0c71cc26775972a4669a391016136f6565b505050505050505050565b816001600160a01b0316836001600160a01b031603613c295760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610c21565b6001600160a01b038381166000818152606a6020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b613ca18484846131f6565b613cad848484846142ea565b6123b55760405162461bcd60e51b8152600401610c219061563c565b6000818152610170602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460ff16151560608281019190915261016e549091906001600160a01b031663dd9ec14984613d3381612cbe565b604080860151865191516001600160e01b031960e087901b168152613d739493929060040193845260208401929092526040830152606082015260800190565b600060405180830381865afa158015613d90573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261106f919081019061568e565b33613dc282612dda565b6001600160a01b031614613e10573381613ddb83612dda565b6040516364283d7b60e01b81526001600160a01b03938416600482015260248101929092529091166044820152606401610c21565b600090815260ca6020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b600054610100900460ff16613e655760405162461bcd60e51b8152600401610c21906156fc565b613e6f82826143e8565b6118c1614419565b600054610100900460ff16613e9e5760405162461bcd60e51b8152600401610c21906156fc565b6118c18282614440565b60408051606081018252600080825260208201819052918101919091526000828152600284016020526040812081908190613ee2906144a1565b9890975095505050505050565b60006001600160e01b0319821663780e9d6360e01b1480610b2e5750610b2e82614586565b600080613f2083612dda565b6001600160a01b0316141592915050565b6001600160a01b038216613f875760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610c21565b613f9081613f14565b15613fdd5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610c21565b613feb6000838360016140ac565b613ff481613f14565b156140415760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610c21565b6001600160a01b038216600081815260686020908152604080832080546001019055848352606790915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6140b8848484846145d6565b60005b818110156142155760006140cf8285615491565b9050846001600160a01b0316866001600160a01b0316146142025760008181526101706020526040808220600201549051639bdd2e4d60e01b8152829173fd3d17d25c05456caa4a9d6e9feaf71aa604710291639bdd2e4d9161413c9160fe9188918d91906004016153a5565b6040805180830381865af4158015614158573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061417c91906153c9565b91509150806001600160a01b0316826001600160a01b0316886001600160a01b03167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f60405160405180910390a46040516001600160a01b038381168252808316919089169085906000805160206157f18339815191529060200160405180910390a450505b508061420d816154d6565b9150506140bb565b5050505050565b60006f7fffffffffffffffffffffffffffffff82111561318c576040516393dafdf160e01b815260040160405180910390fd5b6040516001600160a01b038316602482015260448101829052610e6390849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614659565b6040516001600160a01b03808516602483015283166044820152606481018290526123b59085906323b872dd60e01b9060840161427b565b60006001600160a01b0384163b156143e057604051630a85bd0160e11b81526001600160a01b0385169063150b7a029061432e903390899088908890600401615747565b6020604051808303816000875af1925050508015614369575060408051601f3d908101601f1916820190925261436691810190615784565b60015b6143c6573d808015614397576040519150601f19603f3d011682016040523d82523d6000602084013e61439c565b606091505b5080516000036143be5760405162461bcd60e51b8152600401610c219061563c565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610e8e565b506001610e8e565b600054610100900460ff1661440f5760405162461bcd60e51b8152600401610c21906156fc565b6118c1828261472e565b600054610100900460ff16610fa55760405162461bcd60e51b8152600401610c21906156fc565b600054610100900460ff166144675760405162461bcd60e51b8152600401610c21906156fc565b815161447b90610139906020850190614beb565b5080516144909061013a906020840190614beb565b505060006101378190556101385550565b6040805160608101825260008082526020820181905291810182905281908354600081900361451157600080614505604080516060808201835260008083526020808401829052928401819052835191820184528082529181018290529182015290565b9350935093505061457f565b600061451d868261477c565b604080518082018252825465ffffffffffff1681528151606081018352600180850154600f81810b8452600160801b909104810b602084810191909152600290960154900b93820193909352928101839052519096509450925061457f915050565b9193909250565b60006001600160e01b031982166380ac58cd60e01b14806145b757506001600160e01b03198216635b5e139f60e01b145b80610b2e57506301ffc9a760e01b6001600160e01b0319831614610b2e565b6145e2848484846147a6565b60005b818110156142155760006145f98285615491565b90506001600160a01b0386161515806146245750846001600160a01b0316866001600160a01b031614155b1561464657600081815260ca6020526040902080546001600160a01b03191690555b5080614651816154d6565b9150506145e5565b60006146ae826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166148d39092919063ffffffff16565b90508051600014806146cf5750808060200190518101906146cf91906157a1565b610e635760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610c21565b600054610100900460ff166147555760405162461bcd60e51b8152600401610c21906156fc565b8151614768906065906020850190614beb565b508051610e63906066906020840190614beb565b6000828281548110614790576147906154a9565b9060005260206000209060030201905092915050565b60018111156148155760405162461bcd60e51b815260206004820152603560248201527f455243373231456e756d657261626c653a20636f6e7365637574697665207472604482015274185b9cd9995c9cc81b9bdd081cdd5c1c1bdc9d1959605a1b6064820152608401610c21565b816001600160a01b0385166148715761486c81609980546000838152609a60205260408120829055600182018355919091527f72a152ddfb8e864297c917af52ea6c1c68aead0fee1a62673fcc7e0c94979d000155565b614894565b836001600160a01b0316856001600160a01b0316146148945761489485826148e2565b6001600160a01b0384166148b0576148ab8161497f565b614215565b846001600160a01b0316846001600160a01b031614614215576142158482614a2e565b6060610e8e8484600085614a72565b600060016148ef84611c3f565b6148f991906154bf565b60008381526098602052604090205490915080821461494c576001600160a01b03841660009081526097602090815260408083208584528252808320548484528184208190558352609890915290208190555b5060009182526098602090815260408084208490556001600160a01b039094168352609781528383209183525290812055565b609954600090614991906001906154bf565b6000838152609a6020526040812054609980549394509092849081106149b9576149b96154a9565b9060005260206000200154905080609983815481106149da576149da6154a9565b6000918252602080832090910192909255828152609a90915260408082208490558582528120556099805480614a1257614a126157be565b6001900381819060005260206000200160009055905550505050565b6000614a3983611c3f565b6001600160a01b039093166000908152609760209081526040808320868452825280832085905593825260989052919091209190915550565b606082471015614ad35760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610c21565b600080866001600160a01b03168587604051614aef91906157d4565b60006040518083038185875af1925050503d8060008114614b2c576040519150601f19603f3d011682016040523d82523d6000602084013e614b31565b606091505b5091509150614b4287838387614b4d565b979650505050505050565b60608315614bbc578251600003614bb5576001600160a01b0385163b614bb55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610c21565b5081610e8e565b610e8e8383815115614bd15781518083602001fd5b8060405162461bcd60e51b8152600401610c219190614d06565b828054614bf79061536b565b90600052602060002090601f016020900481019282614c195760008555614c5f565b82601f10614c3257805160ff1916838001178555614c5f565b82800160010185558215614c5f579182015b82811115614c5f578251825591602001919060010190614c44565b5061318c9291505b8082111561318c5760008155600101614c67565b6001600160e01b03198116811461137657600080fd5b600060208284031215614ca357600080fd5b813561106f81614c7b565b60005b83811015614cc9578181015183820152602001614cb1565b838111156123b55750506000910152565b60008151808452614cf2816020860160208601614cae565b601f01601f19169290920160200192915050565b60208152600061106f6020830184614cda565b600060208284031215614d2b57600080fd5b5035919050565b6001600160a01b038116811461137657600080fd5b60008060408385031215614d5a57600080fd5b823591506020830135614d6c81614d32565b809150509250929050565b60008060408385031215614d8a57600080fd5b8235614d9581614d32565b946020939093013593505050565b801515811461137657600080fd5b60008060008060808587031215614dc757600080fd5b84359350602085013592506040850135614de081614d32565b91506060850135614df081614da3565b939692955090935050565b600080600060608486031215614e1057600080fd5b8335614e1b81614d32565b92506020840135614e2b81614d32565b929592945050506040919091013590565b65ffffffffffff8116811461137657600080fd5b60008060408385031215614e6357600080fd5b823591506020830135614d6c81614e3c565b60008060408385031215614e8857600080fd5b8235614e9381614d32565b91506020830135614d6c81614da3565b600080600080600060a08688031215614ebb57600080fd5b85359450602086013593506040860135614ed481614d32565b92506060860135614ee481614d32565b91506080860135614ef481614da3565b809150509295509295909350565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715614f4157614f41614f02565b604052919050565b60008060408385031215614f5c57600080fd5b823567ffffffffffffffff80821115614f7457600080fd5b818501915085601f830112614f8857600080fd5b8135602082821115614f9c57614f9c614f02565b8160051b9250614fad818401614f18565b8281529284018101928181019089851115614fc757600080fd5b948201945b84861015614fe557853582529482019490820190614fcc565b9997909101359750505050505050565b60006020828403121561500757600080fd5b813561106f81614d32565b6000806040838503121561502557600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b8381101561506457815187529582019590820190600101615048565b509495945050505050565b60ff60f81b8816815260e06020820152600061508e60e0830189614cda565b82810360408401526150a08189614cda565b606084018890526001600160a01b038716608085015260a0840186905283810360c085015290506150d18185615034565b9a9950505050505050505050565b6000806000606084860312156150f457600080fd5b8335925060208401359150604084013561510d81614da3565b809150509250925092565b6000806040838503121561512b57600080fd5b823561513681614d32565b91506020830135614d6c81614d32565b600067ffffffffffffffff82111561516057615160614f02565b50601f01601f191660200190565b600061518161517c84615146565b614f18565b905082815283838301111561519557600080fd5b828260208301376000602084830101529392505050565b600080600080608085870312156151c257600080fd5b84356151cd81614d32565b935060208501356151dd81614d32565b925060408501359150606085013567ffffffffffffffff81111561520057600080fd5b8501601f8101871361521157600080fd5b6152208782356020840161516e565b91505092959194509250565b60008060006060848603121561524157600080fd5b833561524c81614d32565b92506020840135614e2b81614da3565b600082601f83011261526d57600080fd5b61106f8383356020850161516e565b600080600080600060a0868803121561529457600080fd5b853567ffffffffffffffff808211156152ac57600080fd5b6152b889838a0161525c565b965060208801359150808211156152ce57600080fd5b6152da89838a0161525c565b955060408801359150808211156152f057600080fd5b506152fd8882890161525c565b935050606086013561530e81614d32565b91506080860135614ef481614d32565b6020808252825182820181905260009190848201906040850190845b8181101561535f5783516001600160a01b03168352928401929184019160010161533a565b50909695505050505050565b600181811c9082168061537f57607f821691505b60208210810361539f57634e487b7160e01b600052602260045260246000fd5b50919050565b93845260208401929092526001600160a01b03166040830152606082015260800190565b600080604083850312156153dc57600080fd5b82516153e781614d32565b6020840151909250614d6c81614d32565b60006020828403121561540a57600080fd5b5051919050565b6020808252602d908201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560408201526c1c881bdc88185c1c1c9bdd9959609a1b606082015260800190565b60006020828403121561547057600080fd5b815161106f81614d32565b634e487b7160e01b600052601160045260246000fd5b600082198211156154a4576154a461547b565b500190565b634e487b7160e01b600052603260045260246000fd5b6000828210156154d1576154d161547b565b500390565b6000600182016154e8576154e861547b565b5060010190565b60008160001904831182151516156155095761550961547b565b500290565b60008261552b57634e487b7160e01b600052601260045260246000fd5b500490565b60208152600061106f6020830184615034565b8051600f81900b811461555557600080fd5b919050565b600080828403608081121561556e57600080fd5b606081121561557c57600080fd5b506040516060810181811067ffffffffffffffff821117156155a0576155a0614f02565b6040526155ac84615543565b81526155ba60208501615543565b60208201526155cb60408501615543565b60408201526060840151909250614d6c81614e3c565b60208082526025908201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060408201526437bbb732b960d91b606082015260800190565b634e487b7160e01b600052602160045260246000fd5b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b6000602082840312156156a057600080fd5b815167ffffffffffffffff8111156156b757600080fd5b8201601f810184136156c857600080fd5b80516156d661517c82615146565b8181528560208385010111156156eb57600080fd5b611461826020830160208601614cae565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061577a90830184614cda565b9695505050505050565b60006020828403121561579657600080fd5b815161106f81614c7b565b6000602082840312156157b357600080fd5b815161106f81614da3565b634e487b7160e01b600052603160045260246000fd5b600082516157e6818460208701614cae565b919091019291505056fe39d7dc46c82aa27d4484a57e21232e5c6f22fe6c7ffc01795304084ba872811ea2646970667358221220c4074109288fbd2d6dbc54597cd95e2a16ebe8798f70999b72aec6c8b9cffa2464736f6c634300080d0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.