More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 734 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Increase_amount | 23744746 | 1 hr ago | IN | 0 ETH | 0.00042089 | ||||
| Infinite_lock_to... | 23743217 | 6 hrs ago | IN | 0 ETH | 0.00004798 | ||||
| Increase_amount | 23743213 | 6 hrs ago | IN | 0 ETH | 0.00006213 | ||||
| Infinite_lock_to... | 23743116 | 6 hrs ago | IN | 0 ETH | 0.00006167 | ||||
| Create_lock | 23743113 | 6 hrs ago | IN | 0 ETH | 0.00012465 | ||||
| Increase_amount | 23741633 | 11 hrs ago | IN | 0 ETH | 0.00052094 | ||||
| Increase_amount | 23737195 | 26 hrs ago | IN | 0 ETH | 0.0000696 | ||||
| Create_lock | 23736540 | 28 hrs ago | IN | 0 ETH | 0.00018002 | ||||
| Infinite_lock_to... | 23736158 | 29 hrs ago | IN | 0 ETH | 0.00004198 | ||||
| Create_lock | 23736151 | 29 hrs ago | IN | 0 ETH | 0.00011236 | ||||
| Increase_amount | 23735966 | 30 hrs ago | IN | 0 ETH | 0.00007352 | ||||
| Increase_amount | 23735945 | 30 hrs ago | IN | 0 ETH | 0.00007751 | ||||
| Increase_amount | 23735788 | 31 hrs ago | IN | 0 ETH | 0.00040798 | ||||
| Increase_amount | 23735340 | 32 hrs ago | IN | 0 ETH | 0.00008198 | ||||
| Increase_amount | 23735323 | 32 hrs ago | IN | 0 ETH | 0.00041407 | ||||
| Increase_amount | 23735228 | 33 hrs ago | IN | 0 ETH | 0.00010872 | ||||
| Increase_amount | 23734752 | 34 hrs ago | IN | 0 ETH | 0.00017653 | ||||
| Create_lock | 23734346 | 35 hrs ago | IN | 0 ETH | 0.00043669 | ||||
| Safe Transfer Fr... | 23734283 | 36 hrs ago | IN | 0 ETH | 0.00033766 | ||||
| Infinite_lock_to... | 23734271 | 36 hrs ago | IN | 0 ETH | 0.00021617 | ||||
| Increase_amount | 23733505 | 38 hrs ago | IN | 0 ETH | 0.00017734 | ||||
| Create_lock | 23732912 | 40 hrs ago | IN | 0 ETH | 0.00017479 | ||||
| Create_lock | 23732829 | 41 hrs ago | IN | 0 ETH | 0.00014551 | ||||
| Create_lock | 23732692 | 41 hrs ago | IN | 0 ETH | 0.00015114 | ||||
| Increase_amount | 23732653 | 41 hrs ago | IN | 0 ETH | 0.00007703 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x4712D2CB...59e3840F1 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
Voting Escrow
Compiler Version
vyper:0.4.3
Contract Source Code (Vyper Json-Input format)
# @version 0.4.3
"""
@title Voting Escrow
@author Yield Basis
@license GNU Affero General Public License v3.0
@notice Votes have a weight depending on time, so that users are
committed to the future of (whatever they are voting for)
@dev Vote weight decays linearly over time. Lock time cannot be
more than `MAXTIME` (4 years).
"""
# Voting escrow to have time-weighted votes
# Votes have a weight depending on time, so that users are committed
# to the future of (whatever they are voting for).
# The weight in this implementation is linear, and lock cannot be more than maxtime:
# w ^
# 1 + /
# | /
# | /
# | /
# |/
# 0 +--------+------> time
# maxtime (4 years)
from ethereum.ercs import IERC20
from ethereum.ercs import IERC721
from snekmate.auth import ownable
from snekmate.tokens import erc721
from interfaces import IVotes
implements: IVotes
initializes: ownable
initializes: erc721[ownable := ownable]
exports: (
erc721.balanceOf,
erc721.ownerOf,
erc721.approve,
erc721.setApprovalForAll,
erc721.getApproved,
erc721.isApprovedForAll,
erc721.name,
erc721.symbol,
erc721.totalSupply,
erc721.tokenByIndex,
erc721.tokenOfOwnerByIndex,
erc721.tokenURI,
ownable.transfer_ownership,
ownable.owner
)
### ve-specific
TOKEN: public(immutable(IERC20))
struct Point:
bias: int256
slope: int256 # - dweight / dt
ts: uint256
struct UntimedPoint:
bias: uint256
slope: uint256
struct LockedBalance:
amount: int256
end: uint256
flag LockActions:
DEPOSIT_FOR
CREATE_LOCK
INCREASE_AMOUNT
INCREASE_TIME
interface TransferClearanceChecker:
def ve_transfer_allowed(user: address) -> bool: view
event Deposit:
_from: indexed(address)
_for: indexed(address)
value: uint256
locktime: indexed(uint256)
type: LockActions
ts: uint256
event Withdraw:
_from: indexed(address)
_for: indexed(address)
value: uint256
ts: uint256
event Supply:
prevSupply: uint256
supply: uint256
event SetTransferClearanceChecker:
clearance_checker: address
WEEK: constant(uint256) = 7 * 86400 # all future times are rounded by week
MAXTIME: constant(int256) = 4 * 365 * 86400 # 4 years
UMAXTIME: constant(uint256) = 4 * 365 * 86400 # 4 years
WAD: constant(uint256) = 10**18
supply: public(uint256)
locked: public(HashMap[address, LockedBalance])
epoch: public(uint256)
point_history: public(Point[10**18]) # epoch -> unsigned point
user_point_history: public(HashMap[address, Point[10**18]]) # user -> Point[user_epoch]
user_point_epoch: public(HashMap[address, uint256])
slope_changes: public(HashMap[uint256, int256]) # time -> signed slope change
transfer_clearance_checker: public(TransferClearanceChecker)
_SUPPORTED_INTERFACES: constant(bytes4[6]) = [
0x01FFC9A7, # The ERC-165 identifier for ERC-165.
0x80AC58CD, # The ERC-165 identifier for ERC-721.
0x5B5E139F, # The ERC-165 identifier for the ERC-721 metadata extension.
0x780E9D63, # The ERC-165 identifier for the ERC-721 enumeration extension.
0x49064906, # The ERC-165 identifier for ERC-4906.
0xE90FB3F6 # IVotes
]
CLOCK_MODE: public(constant(String[14])) = "mode=timestamp"
@deploy
def __init__(token: IERC20, name: String[25], symbol: String[5], base_uri: String[80]):
ownable.__init__()
erc721.__init__(name, symbol, base_uri, "Just say no", "to EIP712")
TOKEN = token
self.point_history[0].ts = block.timestamp
@external
@view
def supportsInterface(interface_id: bytes4) -> bool:
"""
@dev Returns `True` if this contract implements the
interface defined by `interface_id`.
@param interface_id The 4-byte interface identifier.
@return bool The verification whether the contract
implements the interface or not.
"""
return interface_id in _SUPPORTED_INTERFACES
@external
@view
def delegates(account: address) -> address:
"""
@dev Returns the delegate that `account` has chosen (it's a stub because this value cannot be changed)
"""
return account
@external
def delegate(delegatee: address):
"""
@dev Delegates votes from the sender to `delegatee`. This reverts because functionality is not supported
"""
raise "Not supported"
@external
def delegateBySig(delegatee: address, nonce: uint256, expiry: uint256, v: uint8, r: bytes32, s: bytes32):
"""
@dev Delegates votes from signer to `delegatee`. This reverts because functionality is not supported
"""
raise "Not supported"
@external
@view
def clock() -> uint48:
"""
EIP-6372 clock
"""
return convert(block.timestamp, uint48)
@internal
def _checkpoint(addr: address, old_locked: LockedBalance, new_locked: LockedBalance):
"""
@notice Record global and per-user data to checkpoint
@param addr User's wallet address. No user checkpoint if 0x0
@param old_locked Pevious locked amount / end lock time for the user
@param new_locked New locked amount / end lock time for the user
"""
u_old: Point = empty(Point)
u_new: Point = empty(Point)
old_dslope: int256 = 0
new_dslope: int256 = 0
_epoch: uint256 = self.epoch
if addr != empty(address):
# Calculate slopes and biases
# Kept at zero when they have to
if old_locked.end > block.timestamp and old_locked.amount > 0:
if old_locked.end == max_value(uint256):
u_old.slope = 0
u_old.bias = old_locked.amount
else:
u_old.slope = old_locked.amount // MAXTIME
u_old.bias = u_old.slope * convert(old_locked.end - block.timestamp, int256)
if new_locked.end > block.timestamp and new_locked.amount > 0:
if new_locked.end == max_value(uint256):
u_new.slope = 0
u_new.bias = new_locked.amount
else:
u_new.slope = new_locked.amount // MAXTIME
u_new.bias = u_new.slope * convert(new_locked.end - block.timestamp, int256)
# Read values of scheduled changes in the slope
# old_locked.end can be in the past and in the future
# new_locked.end can ONLY by in the FUTURE unless everything expired: than zeros
old_dslope = self.slope_changes[old_locked.end]
if new_locked.end != 0:
if new_locked.end == old_locked.end:
new_dslope = old_dslope
else:
new_dslope = self.slope_changes[new_locked.end]
last_point: Point = Point(bias=0, slope=0, ts=block.timestamp)
if _epoch > 0:
last_point = self.point_history[_epoch]
last_checkpoint: uint256 = last_point.ts
# Go over weeks to fill history and calculate what the current point is
t_i: uint256 = (last_checkpoint // WEEK) * WEEK
for i: uint256 in range(255):
# Hopefully it won't happen that this won't get used in 5 years!
# If it does, users will be able to withdraw but vote weight will be broken
t_i += WEEK
d_slope: int256 = 0
if t_i > block.timestamp:
t_i = block.timestamp
else:
d_slope = self.slope_changes[t_i]
last_point.bias -= last_point.slope * convert(t_i - last_checkpoint, int256)
last_point.slope += d_slope
if last_point.bias < 0: # This can happen
last_point.bias = 0
if last_point.slope < 0: # This cannot happen - just in case
last_point.slope = 0
last_checkpoint = t_i
last_point.ts = t_i
_epoch += 1
if t_i == block.timestamp:
break
else:
self.point_history[_epoch] = last_point
self.epoch = _epoch
# Now point_history is filled until t=now
if addr != empty(address):
# If last point was in this block, the slope change has been applied already
# But in such case we have 0 slope(s)
last_point.slope += (u_new.slope - u_old.slope)
last_point.bias += (u_new.bias - u_old.bias)
if last_point.slope < 0:
last_point.slope = 0
if last_point.bias < 0:
last_point.bias = 0
# Record the changed point into history
self.point_history[_epoch] = last_point
if addr != empty(address):
# Schedule the slope changes (slope is going down)
# We subtract new_user_slope from [new_locked.end]
# and add old_user_slope to [old_locked.end]
if old_locked.end > block.timestamp:
# old_dslope was <something> - u_old.slope, so we cancel that
old_dslope += u_old.slope
if new_locked.end == old_locked.end:
old_dslope -= u_new.slope # It was a new deposit, not extension
self.slope_changes[old_locked.end] = old_dslope
if new_locked.end > block.timestamp:
if new_locked.end != old_locked.end:
new_dslope -= u_new.slope # old slope disappeared at this point
self.slope_changes[new_locked.end] = new_dslope
# else: we recorded it already in old_dslope
# Now handle user history
user_epoch: uint256 = self.user_point_epoch[addr] + 1
self.user_point_epoch[addr] = user_epoch
u_new.ts = block.timestamp
self.user_point_history[addr][user_epoch] = u_new
@external
def checkpoint():
"""
@notice Record global data to checkpoint
"""
self._checkpoint(empty(address), empty(LockedBalance), empty(LockedBalance))
@internal
def _deposit_for(_addr: address, _value: uint256, unlock_time: uint256, locked_balance: LockedBalance, type: LockActions):
"""
@notice Deposit and lock tokens for a user
@param _addr User's wallet address
@param _value Amount to deposit
@param unlock_time New time when to unlock the tokens, or 0 if unchanged
@param locked_balance Previous locked amount / timestamp
"""
_locked: LockedBalance = locked_balance
supply_before: uint256 = self.supply
new_supply: uint256 = (supply_before + _value) // UMAXTIME * UMAXTIME
rounded_value: uint256 = new_supply - supply_before
self.supply = new_supply
old_locked: LockedBalance = _locked
# Adding to existing lock, or if a lock is expired - creating a new one
_locked.amount += convert(rounded_value, int256)
if unlock_time != 0:
_locked.end = unlock_time
self.locked[_addr] = _locked
# Possibilities:
# Both old_locked.end could be current or expired (>/< block.timestamp)
# value == 0 (extend lock) or value > 0 (add to lock or extend lock)
# _locked.end > block.timestamp (always)
self._checkpoint(_addr, old_locked, _locked)
if rounded_value != 0:
assert extcall TOKEN.transferFrom(msg.sender, self, rounded_value)
log Deposit(_from=msg.sender, _for=_addr, value=rounded_value, locktime=_locked.end, type=type, ts=block.timestamp)
log Supply(prevSupply=supply_before, supply=new_supply)
@external
@nonreentrant
def create_lock(_value: uint256, _unlock_time: uint256):
"""
@notice Deposit `_value` tokens for `msg.sender` and lock until `_unlock_time`
@param _value Amount to deposit
@param _unlock_time Epoch time when tokens unlock, rounded down to whole weeks
"""
unlock_time: uint256 = (_unlock_time // WEEK) * WEEK # Locktime is rounded down to weeks
_locked: LockedBalance = self.locked[msg.sender]
assert _value >= UMAXTIME, "Min value"
assert _locked.amount == 0, "Withdraw old tokens first"
assert unlock_time > block.timestamp, "Can only lock until time in the future"
assert unlock_time <= block.timestamp + UMAXTIME, "Voting lock can be 4 years max"
self._deposit_for(msg.sender, _value, unlock_time, _locked, LockActions.CREATE_LOCK)
erc721._mint(msg.sender, convert(msg.sender, uint256))
@external
@nonreentrant
def increase_amount(_value: uint256, _for: address = msg.sender):
"""
@notice Deposit `_value` additional tokens for `_for` which is `msg.sender` by default
without modifying the unlock time
@param _value Amount of tokens to deposit and add to the lock
@param _for Lock to increase for
"""
_locked: LockedBalance = self.locked[_for]
assert _value >= UMAXTIME # dev: need non-zero value
assert _locked.amount > 0, "No existing lock found"
assert _locked.end > block.timestamp, "Cannot add to expired lock. Withdraw"
self._deposit_for(_for, _value, 0, _locked, LockActions.INCREASE_AMOUNT)
@external
@nonreentrant
def increase_unlock_time(_unlock_time: uint256):
"""
@notice Extend the unlock time for `msg.sender` to `_unlock_time`
@param _unlock_time New epoch time for unlocking
"""
_locked: LockedBalance = self.locked[msg.sender]
unlock_time: uint256 = (_unlock_time // WEEK) * WEEK # Locktime is rounded down to weeks
assert _locked.amount > 0, "Nothing is locked"
assert _locked.end > block.timestamp, "Lock expired"
assert unlock_time > _locked.end, "Can only increase lock duration"
assert unlock_time <= block.timestamp + UMAXTIME, "Voting lock can be 4 years max"
self._deposit_for(msg.sender, 0, unlock_time, _locked, LockActions.INCREASE_TIME)
@external
@nonreentrant
def infinite_lock_toggle():
"""
@notice Make ever-extending lock or cancel it
"""
_locked: LockedBalance = self.locked[msg.sender]
assert _locked.end > block.timestamp, "Lock expired"
assert _locked.amount > 0, "Nothing is locked"
unlock_time: uint256 = 0
if _locked.end == max_value(uint256):
checker: TransferClearanceChecker = self.transfer_clearance_checker
if checker.address != empty(address):
# The check is whether the source (owner) has 0 votes.
# Destination address can STILL have votes, that's fine
assert staticcall checker.ve_transfer_allowed(msg.sender), "Not allowed"
unlock_time = ((block.timestamp + UMAXTIME) // WEEK) * WEEK
else:
unlock_time = max_value(uint256)
self._deposit_for(msg.sender, 0, unlock_time, _locked, LockActions.INCREASE_TIME)
@external
@nonreentrant
def withdraw(_for: address = msg.sender):
"""
@notice Withdraw all tokens for `msg.sender`
@dev Only possible if the lock has expired
"""
_locked: LockedBalance = self.locked[msg.sender]
assert block.timestamp >= _locked.end, "The lock didn't expire"
value: uint256 = convert(_locked.amount, uint256)
old_locked: LockedBalance = _locked
_locked.end = 0
_locked.amount = 0
self.locked[msg.sender] = _locked
supply_before: uint256 = self.supply
new_supply: uint256 = supply_before - value
self.supply = new_supply
# old_locked can have either expired <= timestamp or zero end
# _locked has only 0 end
# Both can have >= 0 amount
self._checkpoint(msg.sender, old_locked, _locked)
erc721._burn(convert(msg.sender, uint256))
assert extcall TOKEN.transfer(_for, value)
log Withdraw(_from=msg.sender, _for=_for, value=value, ts=block.timestamp)
log Supply(prevSupply=supply_before, supply=new_supply)
@external
@view
def getVotes(account: address) -> uint256:
"""
@dev Returns the current amount of votes that `account` has.
"""
_epoch: uint256 = self.user_point_epoch[account]
if _epoch == 0:
return 0
else:
last_point: Point = self.user_point_history[account][_epoch]
last_point.bias -= last_point.slope * convert(block.timestamp - last_point.ts, int256)
if last_point.bias < 0:
last_point.bias = 0
return convert(last_point.bias, uint256)
@external
@view
def getPastVotes(account: address, timepoint: uint256) -> uint256:
"""
@dev Returns the amount of votes that `account` had at a specific moment in the past
"""
# Binary search
_min: uint256 = 0
_max: uint256 = self.user_point_epoch[account]
if timepoint < self.user_point_history[account][0].ts:
return 0
for i: uint256 in range(128): # Will be always enough for 128-bit numbers
if _min >= _max:
break
_mid: uint256 = (_min + _max + 1) // 2
if self.user_point_history[account][_mid].ts <= timepoint:
_min = _mid
else:
_max = _mid - 1
upoint: Point = self.user_point_history[account][_min]
upoint.bias -= upoint.slope * convert(timepoint - upoint.ts, int256)
if upoint.bias >= 0:
return convert(upoint.bias, uint256)
else:
return 0
@internal
@view
def total_supply_at(timepoint: uint256) -> uint256:
_epoch: uint256 = self.epoch
if _epoch == 0:
return 0
else:
if timepoint < self.point_history[0].ts:
return 0
# Past total supply binary search
_min: uint256 = 0
for i: uint256 in range(128): # Will be always enough for 128-bit numbers
if _min >= _epoch:
break
_mid: uint256 = (_min + _epoch + 1) // 2
if self.point_history[_mid].ts <= timepoint:
_min = _mid
else:
_epoch = _mid - 1
point: Point = self.point_history[_min]
if _min == _epoch:
# Future total supply search -> iterate over all slope changes
t_i: uint256 = point.ts # Already rounded to whole weeks
for i: uint256 in range(255):
t_i += WEEK
d_slope: int256 = 0
if t_i > timepoint:
t_i = timepoint
else:
d_slope = self.slope_changes[t_i]
point.bias -= point.slope * convert(t_i - point.ts, int256)
if t_i == timepoint:
break
point.slope += d_slope
point.ts = t_i
if point.bias < 0:
point.bias = 0
else:
point.bias -= point.slope * convert(timepoint - point.ts, int256)
return convert(point.bias, uint256)
@external
@view
def totalVotes() -> uint256:
"""
@notice Returns current total supply of votes
"""
return self.total_supply_at(block.timestamp)
@external
@view
def getPastTotalSupply(timepoint: uint256) -> uint256:
"""
@dev Returns the total supply of votes available at a specific moment in the past.
@notice 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.
Unlike the original method, this one ALSO works with the future
"""
return self.total_supply_at(timepoint)
@internal
@view
def _ve_transfer_allowed(owner: address, to: address) -> bool:
checker: TransferClearanceChecker = self.transfer_clearance_checker
if checker.address != empty(address):
# The check is whether the source (owner) has 0 votes.
# Destination address can STILL have votes, that's fine
assert staticcall checker.ve_transfer_allowed(owner), "Not allowed"
assert owner != to
sender_max: bool = False
receiver_max: bool = False
max_time: uint256 = (block.timestamp + UMAXTIME) // WEEK * WEEK
owner_time: uint256 = self.locked[owner].end
if owner_time == max_value(uint256) or owner_time // WEEK * WEEK == max_time:
sender_max = True
to_time: uint256 = self.locked[to].end
if to_time == max_value(uint256) or to_time // WEEK * WEEK == max_time:
receiver_max = True
# the end slope should be the same, that's why the last condition is needed
return sender_max and receiver_max and (owner_time // WEEK * WEEK == to_time // WEEK * WEEK)
@internal
def _merge_positions(owner: address, to: address):
"""
@dev Merge veLocked positions of `owner` with `to`, giving it to `to`.
"""
locked: LockedBalance = self.locked[owner]
self.locked[owner] = empty(LockedBalance)
new_locked: LockedBalance = self.locked[to]
new_locked.amount += locked.amount
self.locked[to].amount = new_locked.amount
user_epoch: uint256 = self.user_point_epoch[owner] + 1
self.user_point_epoch[owner] = user_epoch
self.user_point_history[owner][user_epoch] = Point(bias=0, slope=0, ts=block.timestamp)
user_epoch = self.user_point_epoch[to] + 1
self.user_point_epoch[to] = user_epoch
slope: int256 = 0
bias: int256 = 0
if new_locked.end == max_value(uint256):
bias = new_locked.amount
else:
slope = new_locked.amount // MAXTIME
bias = slope * convert(new_locked.end - block.timestamp, int256)
self.user_point_history[to][user_epoch] = Point(
bias=bias,
slope=slope,
ts=block.timestamp
)
# Total should not change because we transfer between users
self._checkpoint(empty(address), empty(LockedBalance), empty(LockedBalance))
@external
def set_transfer_clearance_checker(transfer_clearance_checker: TransferClearanceChecker):
"""
@notice Set checker for when the transfer os the ve-token is allowed (usually when all votes are removed)
"""
ownable._check_owner()
self.transfer_clearance_checker = transfer_clearance_checker
log SetTransferClearanceChecker(clearance_checker=transfer_clearance_checker.address)
@external
def transferFrom(owner: address, to: address, token_id: uint256):
"""
@notice Transfer ve-NFT
"""
assert erc721._is_approved_or_owner(msg.sender, token_id), "erc721: caller is not token owner or approved"
assert token_id == convert(owner, uint256), "Wrong token ID"
assert self._ve_transfer_allowed(owner, to), "Need max veLock"
self._merge_positions(owner, to)
erc721._burn(token_id)
@external
def safeTransferFrom(owner: address, to: address, token_id: uint256, data: Bytes[1_024] = b""):
"""
@notice Transfer ve-NFT and use a callback. Keep in mind that NFT gets destructed before the callback is hit
"""
assert erc721._is_approved_or_owner(msg.sender, token_id), "erc721: caller is not token owner or approved"
assert token_id == convert(owner, uint256), "Wrong token ID"
assert self._ve_transfer_allowed(owner, to), "Need max veLock"
self._merge_positions(owner, to)
erc721._burn(token_id)
assert erc721._check_on_erc721_received(owner, to, token_id, data), "erc721: transfer to non-IERC721Receiver implementer"
@external
@view
def get_last_user_slope(addr: address) -> int256:
"""
@notice Get the most recently recorded rate of voting power decrease for `addr`
@param addr Address of the user wallet
@return Value of the slope
"""
uepoch: uint256 = self.user_point_epoch[addr]
return self.user_point_history[addr][uepoch].slope
@external
@view
def get_last_user_point(addr: address) -> UntimedPoint:
"""
@notice Get the most recently recorded point of voting power decrease for `addr`
@param addr Address of the user wallet
"""
uepoch: uint256 = self.user_point_epoch[addr]
return UntimedPoint(
bias=convert(self.user_point_history[addr][uepoch].bias, uint256),
slope=convert(self.user_point_history[addr][uepoch].slope, uint256)
)
@external
@view
def locked__end(_addr: address) -> uint256:
"""
@notice Get timestamp when `_addr`'s lock finishes
@param _addr User wallet
@return Epoch time of the lock end
"""
return self.locked[_addr].end# pragma version ~=0.4.3
# pragma nonreentrancy off
"""
@title Owner-Based Access Control Functions
@custom:contract-name ownable
@license GNU Affero General Public License v3.0 only
@author pcaversaccio
@notice These functions can be used to implement a basic access
control mechanism, where there is an account (an owner)
that can be granted exclusive access to specific functions.
By default, the owner account will be the one that deploys
the contract. This can later be changed with `transfer_ownership`.
An exemplary integration can be found in the ERC-20 implementation here:
https://github.com/pcaversaccio/snekmate/blob/main/src/snekmate/tokens/erc20.vy.
The implementation is inspired by OpenZeppelin's implementation here:
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol.
"""
# @dev Returns the address of the current owner.
# @notice If you declare a variable as `public`,
# Vyper automatically generates an `external`
# getter function for the variable.
owner: public(address)
# @dev Emitted when the ownership is transferred
# from `previous_owner` to `new_owner`.
event OwnershipTransferred:
previous_owner: indexed(address)
new_owner: indexed(address)
@deploy
@payable
def __init__():
"""
@dev To omit the opcodes for checking the `msg.value`
in the creation-time EVM bytecode, the constructor
is declared as `payable`.
@notice The `owner` role will be assigned to
the `msg.sender`.
"""
self._transfer_ownership(msg.sender)
@external
def transfer_ownership(new_owner: address):
"""
@dev Transfers the ownership of the contract
to a new account `new_owner`.
@notice Note that this function can only be
called by the current `owner`. Also,
the `new_owner` cannot be the zero address.
@param new_owner The 20-byte address of the new owner.
"""
self._check_owner()
assert new_owner != empty(address), "ownable: new owner is the zero address"
self._transfer_ownership(new_owner)
@external
def renounce_ownership():
"""
@dev Leaves the contract without an owner.
@notice Renouncing ownership will leave the
contract without an owner, thereby
removing any functionality that is
only available to the owner.
"""
self._check_owner()
self._transfer_ownership(empty(address))
@internal
def _check_owner():
"""
@dev Throws if the sender is not the owner.
"""
assert msg.sender == self.owner, "ownable: caller is not the owner"
@internal
def _transfer_ownership(new_owner: address):
"""
@dev Transfers the ownership of the contract
to a new account `new_owner`.
@notice This is an `internal` function without
access restriction.
@param new_owner The 20-byte address of the new owner.
"""
old_owner: address = self.owner
self.owner = new_owner
log OwnershipTransferred(previous_owner=old_owner, new_owner=new_owner)# pragma version ~=0.4.3
"""
@title EIP-721 Optional Metadata Interface Definition
@custom:contract-name IERC721Metadata
@license GNU Affero General Public License v3.0 only
@author pcaversaccio
@notice The metadata extension is optional for an ERC-721
smart contract. This allows a smart contract to
be interrogated for its name and for details about
the asset(s) which a non-fungible token (NFT)
represents. For more details, please refer to:
https://eips.ethereum.org/EIPS/eip-721#specification.
Note that Vyper interfaces that implement functions
with return values that require an upper bound (e.g.
`Bytes`, `DynArray`, or `String`), the upper bound
defined in the interface represents the lower bound
of the implementation:
https://github.com/vyperlang/vyper/pull/3205.
On how to use interfaces in Vyper, please visit:
https://vyper.readthedocs.io/en/latest/interfaces.html#interfaces.
"""
# @dev We import and implement the `IERC165` interface,
# which is a built-in interface of the Vyper compiler.
from ethereum.ercs import IERC165
implements: IERC165
# @dev We import the `IERC721` interface, which is a built-in
# interface of the Vyper compiler, to highlight the association
# of the custom `IERC721Metadata` interface with the built-in
# `IERC721` interface.
# @notice The interface `IERC721Metadata` must be used in conjunction
# with the built-in interface `IERC721` to be EIP-721 compatible.
# If you want to use this interface as a stand-alone interface,
# you must add `implements: IERC721` to this interface and implement
# all required events and functions accordingly.
from ethereum.ercs import IERC721
@external
@view
def supportsInterface(interfaceId: bytes4) -> bool:
"""
@dev Returns `True` if this contract implements the
interface defined by `interfaceId`.
@notice For more details on how these identifiers are
created, please refer to:
https://eips.ethereum.org/EIPS/eip-165.
@param interfaceId The 4-byte interface identifier.
@return bool The verification whether the contract
implements the interface or not.
"""
...
@external
@view
def name() -> String[25]:
"""
@dev Returns the token collection name.
@return String The maximum 25-character
user-readable string name of the
token collection.
"""
...
@external
@view
def symbol() -> String[5]:
"""
@dev Returns the token collection symbol.
@return String The maximum 5-character
user-readable string symbol of the
token collection.
"""
...
@external
@view
def tokenURI(_tokenId: uint256) -> String[512]:
"""
@dev Returns the Uniform Resource Identifier (URI)
for `_tokenId` token.
@notice Throws if `_tokenId` is not a valid ERC-721 token.
@param _tokenId The 32-byte identifier of the token.
@return String The maximum 512-character user-readable
string token URI of the `_tokenId` token.
"""
...# pragma version ~=0.4.3
"""
@title EIP-721 Optional Enumeration Interface Definition
@custom:contract-name IERC721Enumerable
@license GNU Affero General Public License v3.0 only
@author pcaversaccio
@notice The enumeration extension is optional for an ERC-721
smart contract. This allows a contract to publish its
full list of ERC-721 tokens and make them discoverable.
For more details, please refer to:
https://eips.ethereum.org/EIPS/eip-721#specification.
On how to use interfaces in Vyper, please visit:
https://vyper.readthedocs.io/en/latest/interfaces.html#interfaces.
"""
# @dev We import and implement the `IERC165` interface,
# which is a built-in interface of the Vyper compiler.
from ethereum.ercs import IERC165
implements: IERC165
# @dev We import the `IERC721` interface, which is a built-in
# interface of the Vyper compiler, to highlight the association
# of the custom `IERC721Enumerable` interface with the built-in
# `IERC721` interface.
# @notice The interface `IERC721Enumerable` must be used in conjunction
# with the built-in interface `IERC721` to be EIP-721 compatible.
# If you want to use this interface as a stand-alone interface,
# you must add `implements: IERC721` to this interface and implement
# all required events and functions accordingly.
from ethereum.ercs import IERC721
@external
@view
def supportsInterface(interfaceId: bytes4) -> bool:
"""
@dev Returns `True` if this contract implements the
interface defined by `interfaceId`.
@notice For more details on how these identifiers are
created, please refer to:
https://eips.ethereum.org/EIPS/eip-165.
@param interfaceId The 4-byte interface identifier.
@return bool The verification whether the contract
implements the interface or not.
"""
...
@external
@view
def totalSupply() -> uint256:
"""
@dev Returns the amount of tokens in existence.
@return uint256 The 32-byte token supply.
"""
...
@external
@view
def tokenByIndex(_index: uint256) -> uint256:
"""
@dev Returns a token ID at a given `_index` of
all the tokens stored by the contract.
@notice Use along with `totalSupply` to enumerate
all tokens.
@param _index The 32-byte counter (must be less
than `totalSupply()`).
@return uint256 The 32-byte token ID at index
`_index`.
"""
...
@external
@view
def tokenOfOwnerByIndex(_owner: address, _index: uint256) -> uint256:
"""
@dev Returns a token ID owned by `_owner` at a
given `_index` of its token list.
@notice Use along with `balanceOf` to enumerate
all of `_owner`'s tokens.
@param _owner The 20-byte owner address.
@param _index The 32-byte counter (must be less
than `balanceOf(_owner)`).
@return uint256 The 32-byte token ID owned by
`_owner` at index `_index`.
"""
...# pragma version ~=0.4.3
"""
@title EIP-4494 Interface Definition
@custom:contract-name IERC721Permit
@license GNU Affero General Public License v3.0 only
@author pcaversaccio
@notice The `permit` function implements approvals via
EIP-712 secp256k1 signatures for ERC-721 tokens:
https://eips.ethereum.org/EIPS/eip-4494. The
`permit` function allows users to modify the
permission of who can manage a `tokenId` using
a signed message (via secp256k1 signatures),
instead of through `msg.sender`. By not relying
on `approve`, the token holder's account does not
need to send a transaction and therefore does not
need to hold ether, enabling important use cases
such as meta-transactions.
IMPORTANT: Due to sake of consistency, we follow EIP-2612's
pattern (see https://eips.ethereum.org/EIPS/eip-2612) and
implement the `permit` function via the secp256k1 signature
parameters `v`, `r`, and `s` and do not support EIP-2098
signatures (64-byte length, see https://eips.ethereum.org/EIPS/eip-2098).
The ERC-165 identifier for this interface is `0x589C5CE2`.
On how to use interfaces in Vyper, please visit:
https://vyper.readthedocs.io/en/latest/interfaces.html#interfaces.
"""
# @dev We import and implement the `IERC165` interface,
# which is a built-in interface of the Vyper compiler.
from ethereum.ercs import IERC165
implements: IERC165
@external
@view
def supportsInterface(interfaceId: bytes4) -> bool:
"""
@dev Returns `True` if this contract implements the
interface defined by `interfaceId`.
@notice For more details on how these identifiers are
created, please refer to:
https://eips.ethereum.org/EIPS/eip-165.
@param interfaceId The 4-byte interface identifier.
@return bool The verification whether the contract
implements the interface or not.
"""
...
@external
def permit(spender: address, tokenId: uint256, deadline: uint256, v: uint8, r: bytes32, s: bytes32):
"""
@dev Sets permission to `spender` to transfer `tokenId`
token to another account, given `owner`'s signed
approval.
@notice Note that `spender` cannot be the zero address.
Also, `deadline` must be a block timestamp in
the future. `v`, `r`, and `s` must be a valid
secp256k1 signature from `owner` over the
EIP-712-formatted function arguments. Eventually,
the signature must use `tokenId`'s current nonce.
@param spender The 20-byte spender address.
@param tokenId The 32-byte identifier of the token.
@param deadline The 32-byte block timestamp up
which the `spender` is allowed to spend `tokenId`.
@param v The secp256k1 1-byte signature parameter `v`.
@param r The secp256k1 32-byte signature parameter `r`.
@param s The secp256k1 32-byte signature parameter `s`.
"""
...
@external
@view
def nonces(tokenId: uint256) -> uint256:
"""
@dev Returns the current on-chain tracked nonce of `tokenId`.
@param tokenId The 32-byte identifier of the token.
@return uint256 The 32-byte `tokenId` nonce.
"""
...
@external
@view
def DOMAIN_SEPARATOR() -> bytes32:
"""
@dev Returns the domain separator for the current chain.
@return bytes32 The 32-byte domain separator.
"""
...# pragma version ~=0.4.3
"""
@title EIP-4906 Interface Definition
@custom:contract-name IERC4906
@license GNU Affero General Public License v3.0 only
@author pcaversaccio
@notice The ERC-4906 standard is an extension of EIP-721.
It adds a `MetadataUpdate` event to EIP-721 tokens.
The ERC-165 identifier for this interface is `0x49064906`.
For more details, please refer to:
https://eips.ethereum.org/EIPS/eip-4906.
On how to use interfaces in Vyper, please visit:
https://vyper.readthedocs.io/en/latest/interfaces.html#interfaces.
"""
# @dev We import and implement the `IERC165` interface,
# which is a built-in interface of the Vyper compiler.
from ethereum.ercs import IERC165
implements: IERC165
# @dev We import the `IERC721` interface, which is a built-in
# interface of the Vyper compiler, to highlight the association
# of the custom `IERC4906` interface with the built-in `IERC721`
# interface.
# @notice The interface `IERC4906` must be used in conjunction
# with the built-in interface `IERC721` to be EIP-721 compatible.
# If you want to use this interface as a stand-alone interface,
# you must add `implements: IERC721` to this interface and implement
# all required events and functions accordingly.
from ethereum.ercs import IERC721
# @dev Emitted when the metadata of a token is changed.
# Thus, third-party platforms, such as NFT marketplaces,
# can update the images and associated attributes of the
# NFT in a timely manner.
event MetadataUpdate:
_tokenId: uint256
# @dev Emitted when the metadata of a range of tokens is
# changed. Thus, third-party platforms, such as NFT marketplaces,
# can update the images and associated attributes of the
# NFTs in a timely manner.
event BatchMetadataUpdate:
_fromTokenId: uint256
_toTokenId: uint256
@external
@view
def supportsInterface(interfaceId: bytes4) -> bool:
"""
@dev Returns `True` if this contract implements the
interface defined by `interfaceId`.
@notice For more details on how these identifiers are
created, please refer to:
https://eips.ethereum.org/EIPS/eip-165.
@param interfaceId The 4-byte interface identifier.
@return bool The verification whether the contract
implements the interface or not.
"""
...# pragma version ~=0.4.3
"""
@title EIP-5267 Interface Definition
@custom:contract-name IERC5267
@license GNU Affero General Public License v3.0 only
@author pcaversaccio
@notice The ERC-5267 standard complements the EIP-712 standard
by standardising how contracts should publish the fields
and values that describe their domain. This enables
applications to retrieve this description and generate
appropriate domain separators in a general way, and thus
integrate EIP-712 signatures securely and scalably. For
more details, please refer to:
https://eips.ethereum.org/EIPS/eip-5267.
Note that Vyper interfaces that implement functions
with return values that require an upper bound (e.g.
`Bytes`, `DynArray`, or `String`), the upper bound
defined in the interface represents the lower bound
of the implementation:
https://github.com/vyperlang/vyper/pull/3205.
On how to use interfaces in Vyper, please visit:
https://vyper.readthedocs.io/en/latest/interfaces.html#interfaces.
"""
# @dev May be emitted to signal that the domain could
# have changed.
event EIP712DomainChanged:
pass
@external
@view
def eip712Domain() -> (bytes1, String[50], String[20], uint256, address, bytes32, DynArray[uint256, 32]):
"""
@dev Returns the fields and values that describe the domain
separator used by this contract for EIP-712 signatures.
@notice The bits in the 1-byte bit map are read from the least
significant to the most significant, and fields are indexed
in the order that is specified by EIP-712, identical to the
order in which they are listed in the function type.
@return bytes1 The 1-byte bit map where bit `i` is set to `1`
if and only if domain field `i` is present (`0 ≤ i ≤ 4`).
@return String The maximum 50-character user-readable string name
of the signing domain, i.e. the name of the dApp or protocol.
@return String The maximum 20-character current main version of
the signing domain. Signatures from different versions are
not compatible.
@return uint256 The 32-byte EIP-155 chain ID.
@return address The 20-byte address of the verifying contract.
@return bytes32 The 32-byte disambiguation salt for the protocol.
@return DynArray The 32-byte array of EIP-712 extensions.
"""
...# pragma version ~=0.4.3
"""
@title EIP-721 Token Receiver Interface Definition
@custom:contract-name IERC721Receiver
@license GNU Affero General Public License v3.0 only
@author pcaversaccio
@notice The interface definition for any contract
that wants to support safe transfers from
ERC-721 asset contracts. For more details,
please refer to:
https://eips.ethereum.org/EIPS/eip-721#specification.
On how to use interfaces in Vyper, please visit:
https://vyper.readthedocs.io/en/latest/interfaces.html#interfaces.
"""
@external
def onERC721Received(_operator: address, _from: address, _tokenId: uint256, _data: Bytes[1_024]) -> bytes4:
"""
@dev Whenever a `_tokenId` token is transferred to
this contract via `safeTransferFrom` by
`_operator` from `_from`, this function is called.
@notice It must return its function 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.
@param _operator The 20-byte address which called
the `safeTransferFrom` function.
@param _from The 20-byte address which previously
owned the token.
@param _tokenId The 32-byte identifier of the token.
@param _data The maximum 1,024-byte additional data
with no specified format.
@return bytes4 The 4-byte function selector of `onERC721Received`.
"""
...# pragma version ~=0.4.3
# pragma nonreentrancy off
"""
@title Elliptic Curve Digital Signature Algorithm (ECDSA) Secp256k1-Based Functions
@custom:contract-name ecdsa
@license GNU Affero General Public License v3.0 only
@author pcaversaccio
@notice These functions can be used to verify that a message was signed by
the holder of the private key of a given address. All cryptographic
calculations are based on the Ethereum-native secp256k1 elliptic curve
(see https://en.bitcoin.it/wiki/Secp256k1). For verification functions
based on the NIST P-256 elliptic curve (also known as secp256r1), see
the {p256} contract. The implementation is inspired by OpenZeppelin's
implementation here:
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol.
@custom:security Signatures must not be used as unique identifiers since the
`ecrecover` EVM precompile allows for malleable (non-unique)
signatures (see EIP-2: https://eips.ethereum.org/EIPS/eip-2)
or signatures can be malleablised using EIP-2098:
https://eips.ethereum.org/EIPS/eip-2098.
"""
# @dev The malleability threshold used as part of the ECDSA
# verification function.
_MALLEABILITY_THRESHOLD: constant(uint256) = (
57_896_044_618_658_097_711_785_492_504_343_953_926_418_782_139_537_452_191_302_581_570_759_080_747_168
)
@deploy
@payable
def __init__():
"""
@dev To omit the opcodes for checking the `msg.value`
in the creation-time EVM bytecode, the constructor
is declared as `payable`.
"""
pass
@internal
@pure
def _recover_sig(hash: bytes32, signature: Bytes[65]) -> address:
"""
@dev Recovers the signer address from a message digest `hash`
and the signature `signature`.
@notice WARNING: This function is vulnerable to a kind of
signature malleability due to accepting EIP-2098
compact signatures in addition to the traditional
65-byte signature format. The potentially affected
contracts are those that implement signature reuse
or replay protection by marking the signature itself
as used rather than the signed message or a nonce
included in it. A user may take a signature that has
already been submitted, submit it again in a different
form, and bypass this protection. Also, see OpenZeppelin's
security advisory for more information:
https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-4h98-2769-gh6h.
@param hash The 32-byte message digest that was signed.
@param signature The secp256k1 64/65-byte signature of `hash`.
@return address The recovered 20-byte signer address.
"""
sig_length: uint256 = len(signature)
# 65-byte case: `(r,s,v)` standard signature.
if sig_length == 65:
r: uint256 = extract32(signature, empty(uint256), output_type=uint256)
s: uint256 = extract32(signature, 32, output_type=uint256)
v: uint256 = convert(slice(signature, 64, 1), uint256)
return self._try_recover_vrs(hash, v, r, s)
# 64-byte case: `(r,vs)` signature; see: https://eips.ethereum.org/EIPS/eip-2098.
elif sig_length == 64:
r: uint256 = extract32(signature, empty(uint256), output_type=uint256)
vs: uint256 = extract32(signature, 32, output_type=uint256)
return self._try_recover_r_vs(hash, r, vs)
return empty(address)
@internal
@pure
def _recover_vrs(hash: bytes32, v: uint256, r: uint256, s: uint256) -> address:
"""
@dev Recovers the signer address from a message digest `hash`
and the secp256k1 signature parameters `v`, `r`, and `s`.
@param hash The 32-byte message digest that was signed.
@param v The secp256k1 1-byte signature parameter `v`.
@param r The secp256k1 32-byte signature parameter `r`.
@param s The secp256k1 32-byte signature parameter `s`.
@return address The recovered 20-byte signer address.
"""
return self._try_recover_vrs(hash, v, r, s)
@internal
@pure
def _try_recover_r_vs(hash: bytes32, r: uint256, vs: uint256) -> address:
"""
@dev Recovers the signer address from a message digest `hash`
and the secp256k1 short signature fields `r` and `vs`.
@notice See https://eips.ethereum.org/EIPS/eip-2098 for the
compact signature representation.
@param hash The 32-byte message digest that was signed.
@param r The secp256k1 32-byte signature parameter `r`.
@param vs The secp256k1 32-byte short signature field of `v` and `s`.
@return address The recovered 20-byte signer address.
"""
s: uint256 = vs & convert(max_value(int256), uint256)
# We do not check for an overflow here, as the shift operation
# `vs >> 255` results in `0` or `1`.
v: uint256 = unsafe_add(vs >> 255, 27)
return self._try_recover_vrs(hash, v, r, s)
@internal
@pure
def _try_recover_vrs(hash: bytes32, v: uint256, r: uint256, s: uint256) -> address:
"""
@dev Recovers the signer address from a message digest `hash`
and the secp256k1 signature parameters `v`, `r`, and `s`.
@notice All client implementations of the precompile `ecrecover`
check if the value of `v` is `27` or `28`. The references
for the different client implementations can be found here:
https://github.com/ethereum/yellowpaper/pull/860. Thus,
the signature check on the value of `v` is neglected.
@param hash The 32-byte message digest that was signed.
@param v The secp256k1 1-byte signature parameter `v`.
@param r The secp256k1 32-byte signature parameter `r`.
@param s The secp256k1 32-byte signature parameter `s`.
@return address The recovered 20-byte signer address.
"""
assert s <= _MALLEABILITY_THRESHOLD, "ecdsa: invalid signature `s` value"
signer: address = ecrecover(hash, v, r, s)
assert signer != empty(address), "ecdsa: invalid signature"
return signer# pragma version ~=0.4.3
# pragma nonreentrancy off
"""
@title Signature Message Hash Utility Functions
@custom:contract-name message_hash_utils
@license GNU Affero General Public License v3.0 only
@author pcaversaccio
@notice These functions can be used to generate message hashes that conform
to the EIP-191 (https://eips.ethereum.org/EIPS/eip-191) as well as
EIP-712 (https://eips.ethereum.org/EIPS/eip-712) specifications. The
implementation is inspired by OpenZeppelin's implementation here:
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MessageHashUtils.sol.
"""
@deploy
@payable
def __init__():
"""
@dev To omit the opcodes for checking the `msg.value`
in the creation-time EVM bytecode, the constructor
is declared as `payable`.
"""
pass
@internal
@pure
def _to_eth_signed_message_hash(hash: bytes32) -> bytes32:
"""
@dev Returns an Ethereum signed message from a 32-byte
message digest `hash`.
@notice This function returns a 32-byte hash that
corresponds to the one signed with the JSON-RPC method:
https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign.
This method is part of EIP-191:
https://eips.ethereum.org/EIPS/eip-191.
@param hash The 32-byte message digest.
@return bytes32 The 32-byte Ethereum signed message.
"""
return keccak256(concat(b"\x19Ethereum Signed Message:\n32", hash))
@internal
@view
def _to_data_with_intended_validator_hash_self(data: Bytes[1_024]) -> bytes32:
"""
@dev Returns an Ethereum signed data with this contract
as the intended validator and a maximum 1,024-byte
payload `data`.
@notice This function structures the data according to
the version `0x00` of EIP-191:
https://eips.ethereum.org/EIPS/eip-191#version-0x00.
@param data The maximum 1,024-byte data to be signed.
@return bytes32 The 32-byte Ethereum signed data.
"""
return self._to_data_with_intended_validator_hash(self, data)
@internal
@pure
def _to_data_with_intended_validator_hash(validator: address, data: Bytes[1_024]) -> bytes32:
"""
@dev Returns an Ethereum signed data with `validator` as
the intended validator and a maximum 1,024-byte payload
`data`.
@notice This function structures the data according to
the version `0x00` of EIP-191:
https://eips.ethereum.org/EIPS/eip-191#version-0x00.
@param validator The 20-byte intended validator address.
@param data The maximum 1,024-byte data to be signed.
@return bytes32 The 32-byte Ethereum signed data.
"""
return keccak256(concat(x"1900", convert(validator, bytes20), data))
@internal
@pure
def _to_typed_data_hash(domain_separator: bytes32, struct_hash: bytes32) -> bytes32:
"""
@dev Returns an Ethereum signed typed data from a 32-byte
`domain_separator` and a 32-byte `struct_hash`.
@notice This function returns a 32-byte hash that
corresponds to the one signed with the JSON-RPC method:
https://eips.ethereum.org/EIPS/eip-712#specification-of-the-eth_signtypeddata-json-rpc.
This method is part of EIP-712:
https://eips.ethereum.org/EIPS/eip-712.
@param domain_separator The 32-byte domain separator that is
used as part of the EIP-712 encoding scheme.
@param struct_hash The 32-byte struct hash that is used as
part of the EIP-712 encoding scheme. See the definition:
https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
@return bytes32 The 32-byte Ethereum signed typed data.
"""
return keccak256(concat(x"1901", domain_separator, struct_hash))# pragma version ~=0.4.3
# pragma nonreentrancy off
"""
@title EIP-712 Domain Separator
@custom:contract-name eip712_domain_separator
@license GNU Affero General Public License v3.0 only
@author pcaversaccio
@notice These functions are part of EIP-712: https://eips.ethereum.org/EIPS/eip-712.
These functions implement the version of encoding known
as "v4" as implemented by the JSON-RPC method:
https://docs.metamask.io/guide/signing-data.html#sign-typed-data-v4.
In addition, this contract also implements EIP-5267:
https://eips.ethereum.org/EIPS/eip-5267.
The implementation is inspired by OpenZeppelin's implementation here:
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol.
"""
# @dev We import and implement the `IERC5267` interface,
# which is written using standard Vyper syntax.
from .interfaces import IERC5267
implements: IERC5267
# @dev We import the `message_hash_utils` module.
# @notice Please note that the `message_hash_utils`
# module is stateless and therefore does not require
# the `uses` keyword for usage.
from . import message_hash_utils
# @dev The 32-byte type hash for the EIP-712 domain separator.
_TYPE_HASH: constant(bytes32) = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
)
# @dev Caches the domain separator as an `immutable`
# value, but also stores the corresponding chain ID
# to invalidate the cached domain separator if the
# chain ID changes.
_CACHED_DOMAIN_SEPARATOR: immutable(bytes32)
_CACHED_CHAIN_ID: immutable(uint256)
# @dev Caches `self` to `immutable` storage to avoid
# potential issues if a vanilla contract is used in
# a `delegatecall` context.
_CACHED_SELF: immutable(address)
# @dev `immutable` variables to store the (hashed)
# name and (hashed) version during contract creation.
_NAME: immutable(String[50])
_HASHED_NAME: immutable(bytes32)
_VERSION: immutable(String[20])
_HASHED_VERSION: immutable(bytes32)
@deploy
@payable
def __init__(name_: String[50], version_: String[20]):
"""
@dev Initialises the domain separator and the parameter caches.
To omit the opcodes for checking the `msg.value` in the
creation-time EVM bytecode, the constructor is declared as
`payable`.
@notice The definition of the domain separator can be found here:
https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator.
Since the Vyper design requires strings of fixed size,
we arbitrarily set the maximum length for `name` to 50
characters and `version` to 20 characters.
@param name_ The maximum 50-character user-readable string name
of the signing domain, i.e. the name of the dApp or protocol.
@param version_ The maximum 20-character current main version of
the signing domain. Signatures from different versions are
not compatible.
"""
_NAME = name_
_VERSION = version_
_HASHED_NAME = keccak256(name_)
_HASHED_VERSION = keccak256(version_)
_CACHED_DOMAIN_SEPARATOR = self._build_domain_separator()
_CACHED_CHAIN_ID = chain.id
_CACHED_SELF = self
@external
@view
def eip712Domain() -> (bytes1, String[50], String[20], uint256, address, bytes32, DynArray[uint256, 32]):
"""
@dev Returns the fields and values that describe the domain
separator used by this contract for EIP-712 signatures.
@notice The bits in the 1-byte bit map are read from the least
significant to the most significant, and fields are indexed
in the order that is specified by EIP-712, identical to the
order in which they are listed in the function type.
@return bytes1 The 1-byte bit map where bit `i` is set to `1`
if and only if domain field `i` is present (`0 ≤ i ≤ 4`).
@return String The maximum 50-character user-readable string name
of the signing domain, i.e. the name of the dApp or protocol.
@return String The maximum 20-character current main version of
the signing domain. Signatures from different versions are
not compatible.
@return uint256 The 32-byte EIP-155 chain ID.
@return address The 20-byte address of the verifying contract.
@return bytes32 The 32-byte disambiguation salt for the protocol.
@return DynArray The 32-byte array of EIP-712 extensions.
"""
# Note that `0x0f` equals `01111`.
return (0x0f, _NAME, _VERSION, chain.id, self, empty(bytes32), empty(DynArray[uint256, 32]))
@internal
@view
def _domain_separator_v4() -> bytes32:
"""
@dev Returns the domain separator for the current chain.
@return bytes32 The 32-byte domain separator.
"""
if self == _CACHED_SELF and chain.id == _CACHED_CHAIN_ID:
return _CACHED_DOMAIN_SEPARATOR
return self._build_domain_separator()
@internal
@view
def _build_domain_separator() -> bytes32:
"""
@dev Builds the domain separator for the current chain.
@return bytes32 The 32-byte domain separator.
"""
return keccak256(abi_encode(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION, chain.id, self))
@internal
@view
def _hash_typed_data_v4(struct_hash: bytes32) -> bytes32:
"""
@dev Returns the hash of the fully encoded EIP-712
message for this domain.
@notice The definition of the hashed struct can be found here:
https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
@param struct_hash The 32-byte hashed struct.
@return bytes32 The 32-byte fully encoded EIP712
message hash for this domain.
"""
return message_hash_utils._to_typed_data_hash(self._domain_separator_v4(), struct_hash)# pragma version ~=0.4.3
# pragma nonreentrancy off
"""
@title Modern and Gas-Efficient ERC-721 + EIP-4494 Implementation
@custom:contract-name erc721
@license GNU Affero General Public License v3.0 only
@author pcaversaccio
@notice These functions implement the ERC-721
standard interface:
- https://eips.ethereum.org/EIPS/eip-721.
In addition, the following functions have
been added for convenience:
- `name` (`external` `view` function),
- `symbol` (`external` `view` function),
- `tokenURI` (`external` `view` function),
- `totalSupply` (`external` `view` function),
- `tokenByIndex` (`external` `view` function),
- `tokenOfOwnerByIndex` (`external` `view` function),
- `burn` (`external` function),
- `is_minter` (`external` `view` function),
- `safe_mint` (`external` function),
- `set_minter` (`external` function),
- `permit` (`external` function),
- `nonces` (`external` `view` function),
- `DOMAIN_SEPARATOR` (`external` `view` function),
- `eip712Domain` (`external` `view` function),
- `owner` (`external` `view` function),
- `transfer_ownership` (`external` function),
- `renounce_ownership` (`external` function),
- `_check_on_erc721_received` (`internal` function),
- `_before_token_transfer` (`internal` function),
- `_after_token_transfer` (`internal` function).
The `permit` function implements approvals via
EIP-712 secp256k1 signatures for ERC-721 tokens:
https://eips.ethereum.org/EIPS/eip-4494.
In addition, this contract also implements the EIP-5267
function `eip712Domain`:
https://eips.ethereum.org/EIPS/eip-5267.
Eventually, this contract also implements the EIP-4906
metadata update extension:
https://eips.ethereum.org/EIPS/eip-4906.
The implementation is inspired by OpenZeppelin's
implementation here:
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol,
as well as by ApeAcademy's implementation here:
https://github.com/ApeAcademy/ERC721/blob/main/%7B%7Bcookiecutter.project_name%7D%7D/contracts/NFT.vy.
"""
# @dev We import and implement the `IERC165` interface,
# which is a built-in interface of the Vyper compiler.
from ethereum.ercs import IERC165
implements: IERC165
# @dev We import and implement the `IERC721` interface,
# which is a built-in interface of the Vyper compiler.
from ethereum.ercs import IERC721
implements: IERC721
# @dev We import and implement the `IERC721Metadata`
# interface, which is written using standard Vyper
# syntax.
from .interfaces import IERC721Metadata
implements: IERC721Metadata
# @dev We import and implement the `IERC721Enumerable`
# interface, which is written using standard Vyper
# syntax.
from .interfaces import IERC721Enumerable
implements: IERC721Enumerable
# @dev We import and implement the `IERC721Permit`
# interface, which is written using standard Vyper
# syntax.
from .interfaces import IERC721Permit
implements: IERC721Permit
# @dev We import and implement the `IERC4906` interface,
# which is written using standard Vyper syntax.
from .interfaces import IERC4906
implements: IERC4906
# @dev We import and implement the `IERC5267` interface,
# which is written using standard Vyper syntax.
from ..utils.interfaces import IERC5267
implements: IERC5267
# @dev We import the `IERC721Receiver` interface, which
# is written using standard Vyper syntax.
from .interfaces import IERC721Receiver
# @dev We import and use the `ownable` module.
from ..auth import ownable
uses: ownable
# @dev We import the `ecdsa` module.
# @notice Please note that the `ecdsa` module
# is stateless and therefore does not require
# the `uses` keyword for usage.
from ..utils import ecdsa
# @dev We import and initialise the `eip712_domain_separator` module.
from ..utils import eip712_domain_separator
initializes: eip712_domain_separator
# @dev We export (i.e. the runtime bytecode exposes these
# functions externally, allowing them to be called using
# the ABI encoding specification) the `external` getter
# function `owner` from the `ownable` module as well as the
# function `eip712Domain` from the `eip712_domain_separator`
# module.
# @notice Please note that you must always also export (if
# required by the contract logic) `public` declared `constant`,
# `immutable`, and state variables, for which Vyper automatically
# generates an `external` getter function for the variable.
exports: (
# @notice This ERC-721 implementation includes the `transfer_ownership`
# and `renounce_ownership` functions, which incorporate
# the additional built-in `is_minter` role logic and are
# therefore not exported from the `ownable` module.
ownable.owner,
eip712_domain_separator.eip712Domain,
)
# @dev Stores the ERC-165 interface identifier for each
# imported interface. The ERC-165 interface identifier
# is defined as the XOR of all function selectors in the
# interface.
# @notice If you are not using the full feature set of
# this contract, please ensure you exclude the unused
# ERC-165 interface identifiers in the main contract.
_SUPPORTED_INTERFACES: constant(bytes4[6]) = [
0x01FFC9A7, # The ERC-165 identifier for ERC-165.
0x80AC58CD, # The ERC-165 identifier for ERC-721.
0x5B5E139F, # The ERC-165 identifier for the ERC-721 metadata extension.
0x780E9D63, # The ERC-165 identifier for the ERC-721 enumeration extension.
0x589C5CE2, # The ERC-165 identifier for ERC-4494.
0x49064906, # The ERC-165 identifier for ERC-4906.
]
# @dev The 32-byte type hash of the `permit` function.
_PERMIT_TYPE_HASH: constant(bytes32) = keccak256(
"Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)"
)
# @dev Returns the token collection name.
# @notice If you declare a variable as `public`,
# Vyper automatically generates an `external`
# getter function for the variable. Furthermore,
# to preserve consistency with the interface for
# the optional metadata functions of the ERC-721
# standard, we use lower case letters for the
# `immutable` variables `name` and `symbol`.
name: public(immutable(String[25]))
# @dev Returns the token collection symbol.
# @notice See comment on lower case letters
# above at `name`.
symbol: public(immutable(String[5]))
# @dev Stores the base URI for computing `tokenURI`.
_BASE_URI: immutable(String[80])
# @dev Mapping from owner to operator approvals.
isApprovedForAll: public(HashMap[address, HashMap[address, bool]])
# @dev Returns `True` if an `address` has been
# granted the minter role.
is_minter: public(HashMap[address, bool])
# @dev Returns the current on-chain tracked nonce
# of `token_id`.
nonces: public(HashMap[uint256, uint256])
# @dev Mapping from owner address to token count.
_balances: HashMap[address, uint256]
# @dev Mapping from token ID to owner address.
_owners: HashMap[uint256, address]
# @dev Mapping from token ID to approved address.
_token_approvals: HashMap[uint256, address]
# @dev Mapping from owner to list of owned token IDs.
_owned_tokens: HashMap[address, HashMap[uint256, uint256]]
# @dev Mapping from token ID to index of the owner
# tokens list.
_owned_tokens_index: HashMap[uint256, uint256]
# @dev Array with all token IDs used for enumeration.
_all_tokens: DynArray[uint256, max_value(uint64)]
# @dev Mapping from token ID to position in the
# `_all_tokens` array.
_all_tokens_index: HashMap[uint256, uint256]
# @dev Mapping from token ID to token URI.
# @notice Since the Vyper design requires
# strings of fixed size, we arbitrarily set
# the maximum length for `_token_uris` to 432
# characters. Since we have set the maximum
# length for `_BASE_URI` to 80 characters,
# which implies a maximum character length
# for `tokenURI` of 512.
_token_uris: HashMap[uint256, String[432]]
# @dev An `uint256` counter variable that sets
# the token ID for each `safe_mint` call and
# then increments.
_counter: uint256
# @dev Emitted when the status of a `minter`
# address is changed.
event RoleMinterChanged:
minter: indexed(address)
status: bool
@deploy
@payable
def __init__(
name_: String[25], symbol_: String[5], base_uri_: String[80], name_eip712_: String[50], version_eip712_: String[20]
):
"""
@dev To omit the opcodes for checking the `msg.value`
in the creation-time EVM bytecode, the constructor
is declared as `payable`.
@notice At initialisation time, the `owner` role will be
assigned to the `msg.sender` since we `uses` the
`ownable` module, which implements the aforementioned
logic at contract creation time.
@param name_ The maximum 25-character user-readable string
name of the token collection.
@param symbol_ The maximum 5-character user-readable string
symbol of the token collection.
@param base_uri_ The maximum 80-character user-readable
string base URI for computing `tokenURI`.
@param name_eip712_ The maximum 50-character user-readable
string name of the signing domain, i.e. the name
of the dApp or protocol.
@param version_eip712_ The maximum 20-character current
main version of the signing domain. Signatures
from different versions are not compatible.
"""
self._counter = empty(uint256)
name = name_
symbol = symbol_
_BASE_URI = base_uri_
self.is_minter[msg.sender] = True
log RoleMinterChanged(minter=msg.sender, status=True)
eip712_domain_separator.__init__(name_eip712_, version_eip712_)
@external
@view
def supportsInterface(interface_id: bytes4) -> bool:
"""
@dev Returns `True` if this contract implements the
interface defined by `interface_id`.
@param interface_id The 4-byte interface identifier.
@return bool The verification whether the contract
implements the interface or not.
"""
return interface_id in _SUPPORTED_INTERFACES
@external
@view
def balanceOf(owner: address) -> uint256:
"""
@dev Returns the amount of tokens owned by `owner`.
@notice Note that `owner` cannot be the zero address.
@param owner The 20-byte owner address.
@return uint256 The 32-byte token amount owned
by `owner`.
"""
return self._balance_of(owner)
@external
@view
def ownerOf(token_id: uint256) -> address:
"""
@dev Returns the owner of the `token_id` token.
@notice Note that `token_id` must exist.
@param token_id The 32-byte identifier of the token.
@return address The 20-byte owner address.
"""
return self._owner_of(token_id)
@external
@payable
def approve(to: address, token_id: uint256):
"""
@dev Gives permission to `to` to transfer
`token_id` token to another account.
The approval is cleared when the token
is transferred.
@notice Only a single account can be approved
at a time, so approving the zero address
clears previous approvals. Also, the
caller must own the token or be an
approved operator, and `token_id` must
exist.
IMPORTANT: The function is declared as
`payable` to comply with the EIP-721
standard definition:
https://eips.ethereum.org/EIPS/eip-721.
@param to The 20-byte spender address.
@param token_id The 32-byte identifier of the token.
"""
owner: address = self._owner_of(token_id)
assert to != owner, "erc721: approval to current owner"
assert (
msg.sender == owner or self.isApprovedForAll[owner][msg.sender]
), "erc721: approve caller is not token owner or approved for all"
self._approve(to, token_id)
@external
@view
def getApproved(token_id: uint256) -> address:
"""
@dev Returns the account approved for `token_id`
token.
@notice Note that `token_id` must exist.
@param token_id The 32-byte identifier of the token.
@return address The 20-byte approved address.
"""
return self._get_approved(token_id)
@external
def setApprovalForAll(operator: address, approved: bool):
"""
@dev Approves or removes `operator` as an operator
for the caller. Operators can call `transferFrom`
or `safeTransferFrom` for any token owned by
the caller.
@notice Note that the `operator` cannot be the caller.
@param operator The 20-byte operator address.
@param approved The Boolean variable that sets the
approval status.
"""
self._set_approval_for_all(msg.sender, operator, approved)
@external
@payable
def transferFrom(owner: address, to: address, token_id: uint256):
"""
@dev Transfers `token_id` token from `owner` to `to`.
@notice WARNING: Note that the caller is responsible
to confirm that the recipient is capable of
receiving an ERC-721 token 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.
Note that `owner` and `to` cannot be the zero
address. Also, `token_id` token must exist and
must be owned by `owner`. Eventually, if the caller
is not `owner`, it must be approved to move this
token by either `approve` or `setApprovalForAll`.
IMPORTANT: The function is declared as `payable`
to comply with the EIP-721 standard definition:
https://eips.ethereum.org/EIPS/eip-721.
@param owner The 20-byte owner address.
@param to The 20-byte receiver address.
@param token_id The 32-byte identifier of the token.
"""
assert self._is_approved_or_owner(msg.sender, token_id), "erc721: caller is not token owner or approved"
self._transfer(owner, to, token_id)
@external
@payable
def safeTransferFrom(owner: address, to: address, token_id: uint256, data: Bytes[1_024] = b""):
"""
@dev Safely transfers `token_id` token from `owner`
to `to`.
@notice Note that `owner` and `to` cannot be the zero
address. Also, `token_id` token must exist and
must be owned by `owner`. Furthermore, if the caller
is not `owner`, it must be approved to move this
token by either `approve` or `setApprovalForAll`.
Eventually, if `to` refers to a smart contract,
it must implement {IERC721Receiver-onERC721Received},
which is called upon a safe transfer.
The Vyper compiler processes this function `safeTransferFrom`
as two separate function selectors, since a default
parameter `b""` is set in the function declaration.
Anyone can invoke this function using only `owner`,
`to`, and `token_id` as arguments, and is therefore
compatible with the function overloading of `safeTransferFrom`
in the standard ERC-721 interface. You can find more
information here:
- https://github.com/vyperlang/vyper/issues/903,
- https://github.com/vyperlang/vyper/pull/987.
IMPORTANT: The function is declared as `payable`
to comply with the EIP-721 standard definition:
https://eips.ethereum.org/EIPS/eip-721.
WARNING: This function can potentially allow a reentrancy
attack when transferring tokens to an untrusted contract,
when invoking {IERC721Receiver-onERC721Received} on the
receiver. We ensure that we consistently follow the checks-
effects-interactions (CEI) pattern to avoid being vulnerable
to this type of attack.
@param owner The 20-byte owner address.
@param to The 20-byte receiver address.
@param token_id The 32-byte identifier of the token.
@param data The maximum 1,024-byte additional data
with no specified format that is sent
to `to`.
"""
assert self._is_approved_or_owner(msg.sender, token_id), "erc721: caller is not token owner or approved"
self._safe_transfer(owner, to, token_id, data)
@external
@view
def tokenURI(token_id: uint256) -> String[512]:
"""
@dev Returns the Uniform Resource Identifier (URI)
for `token_id` token.
@notice Throws if `token_id` is not a valid ERC-721 token.
@param token_id The 32-byte identifier of the token.
@return String The maximum 512-character user-readable
string token URI of the `token_id` token.
"""
self._require_minted(token_id)
token_uri: String[432] = self._token_uris[token_id]
base_uri_length: uint256 = len(_BASE_URI)
# If there is no base URI, return the token URI.
if base_uri_length == empty(uint256):
return token_uri
# If both are set, concatenate the base URI
# and token URI.
elif len(token_uri) != empty(uint256):
return concat(_BASE_URI, token_uri)
# If there is no token URI but a base URI,
# concatenate the base URI and token ID.
elif base_uri_length != empty(uint256):
return concat(_BASE_URI, uint2str(token_id))
return ""
@external
@view
def totalSupply() -> uint256:
"""
@dev Returns the amount of tokens in existence.
@return uint256 The 32-byte token supply.
"""
return self._total_supply()
@external
@view
def tokenByIndex(index: uint256) -> uint256:
"""
@dev Returns a token ID at a given `index` of
all the tokens stored by the contract.
@notice Use along with `totalSupply` to enumerate
all tokens.
@param index The 32-byte counter (must be less
than `totalSupply`).
@return uint256 The 32-byte token ID at index
`index`.
"""
assert index < self._total_supply(), "erc721: global index out of bounds"
return self._all_tokens[index]
@external
@view
def tokenOfOwnerByIndex(owner: address, index: uint256) -> uint256:
"""
@dev Returns a token ID owned by `owner` at a
given `index` of its token list.
@notice Use along with `balanceOf` to enumerate
all of `owner`'s tokens.
@param owner The 20-byte owner address.
@param index The 32-byte counter (must be less
than `balanceOf(owner)`).
@return uint256 The 32-byte token ID owned by
`owner` at index `index`.
"""
assert index < self._balance_of(owner), "erc721: owner index out of bounds"
return self._owned_tokens[owner][index]
@external
def burn(token_id: uint256):
"""
@dev Burns the `token_id` token.
@notice Note that the caller must own `token_id`
or be an approved operator.
@param token_id The 32-byte identifier of the token.
"""
assert self._is_approved_or_owner(msg.sender, token_id), "erc721: caller is not token owner or approved"
self._burn(token_id)
@external
def safe_mint(owner: address, uri: String[432]):
"""
@dev Safely mints `token_id` and transfers it to `owner`.
@notice Only authorised minters can access this function.
Note that `owner` cannot be the zero address.
Also, new tokens will be automatically assigned
an incremental ID.
@param owner The 20-byte owner address.
@param uri The maximum 432-character user-readable
string URI for computing `tokenURI`.
"""
assert self.is_minter[msg.sender], "erc721: access is denied"
# New tokens will be automatically assigned an incremental ID.
# The first token ID will be zero.
token_id: uint256 = self._counter
self._counter = token_id + 1
# Theoretically, the following line could overflow
# if all 2**256 token IDs were minted. However,
# since we have bounded the dynamic array `_all_tokens`
# by the maximum value of `uint64` and the `_counter`
# increments above are checked for an overflow, this is
# no longer even theoretically possible.
self._safe_mint(owner, token_id, b"")
self._set_token_uri(token_id, uri)
@external
def set_minter(minter: address, status: bool):
"""
@dev Adds or removes an address `minter` to/from the
list of allowed minters. Note that only the
`owner` can add or remove `minter` addresses.
Also, the `minter` cannot be the zero address.
Eventually, the `owner` cannot remove himself
from the list of allowed minters.
@param minter The 20-byte minter address.
@param status The Boolean variable that sets the status.
"""
ownable._check_owner()
assert minter != empty(address), "erc721: minter is the zero address"
# We ensured in the previous step `ownable._check_owner`
# that `msg.sender` is the `owner`.
assert minter != msg.sender, "erc721: minter is owner address"
self.is_minter[minter] = status
log RoleMinterChanged(minter=minter, status=status)
@external
def permit(spender: address, token_id: uint256, deadline: uint256, v: uint8, r: bytes32, s: bytes32):
"""
@dev Sets permission to `spender` to transfer `token_id`
token to another account, given `owner`'s signed
approval.
@notice Note that `spender` cannot be the zero address.
Also, `deadline` must be a block timestamp in
the future. `v`, `r`, and `s` must be a valid
secp256k1 signature from `owner` over the
EIP-712-formatted function arguments. Eventually,
the signature must use `token_id`'s current nonce.
@param spender The 20-byte spender address.
@param token_id The 32-byte identifier of the token.
@param deadline The 32-byte block timestamp up
which the `spender` is allowed to spend `token_id`.
@param v The secp256k1 1-byte signature parameter `v`.
@param r The secp256k1 32-byte signature parameter `r`.
@param s The secp256k1 32-byte signature parameter `s`.
"""
assert block.timestamp <= deadline, "erc721: expired deadline"
current_nonce: uint256 = self.nonces[token_id]
self.nonces[token_id] = unsafe_add(current_nonce, 1)
struct_hash: bytes32 = keccak256(abi_encode(_PERMIT_TYPE_HASH, spender, token_id, current_nonce, deadline))
hash: bytes32 = eip712_domain_separator._hash_typed_data_v4(struct_hash)
signer: address = ecdsa._recover_vrs(hash, convert(v, uint256), convert(r, uint256), convert(s, uint256))
assert signer == self._owner_of(token_id), "erc721: invalid signature"
self._approve(spender, token_id)
@external
@view
def DOMAIN_SEPARATOR() -> bytes32:
"""
@dev Returns the domain separator for the current chain.
@return bytes32 The 32-byte domain separator.
"""
return eip712_domain_separator._domain_separator_v4()
@external
def transfer_ownership(new_owner: address):
"""
@dev Transfers the ownership of the contract
to a new account `new_owner`.
@notice Note that this function can only be
called by the current `owner`. Also,
the `new_owner` cannot be the zero address.
WARNING: The ownership transfer also removes
the previous owner's minter role and assigns
the minter role to `new_owner` accordingly.
@param new_owner The 20-byte address of the new owner.
"""
ownable._check_owner()
assert new_owner != empty(address), "erc721: new owner is the zero address"
self.is_minter[msg.sender] = False
log RoleMinterChanged(minter=msg.sender, status=False)
ownable._transfer_ownership(new_owner)
self.is_minter[new_owner] = True
log RoleMinterChanged(minter=new_owner, status=True)
@external
def renounce_ownership():
"""
@dev Leaves the contract without an owner.
@notice Renouncing ownership will leave the
contract without an owner, thereby
removing any functionality that is
only available to the owner. Note
that the `owner` is also removed from
the list of allowed minters.
WARNING: All other existing `minter`
addresses will still be able to create
new tokens. Consider removing all non-owner
minter addresses first via `set_minter`
before calling `renounce_ownership`.
"""
ownable._check_owner()
self.is_minter[msg.sender] = False
log RoleMinterChanged(minter=msg.sender, status=False)
ownable._transfer_ownership(empty(address))
@internal
@view
def _balance_of(owner: address) -> uint256:
"""
@dev An `internal` helper function that returns the
amount of tokens owned by `owner`.
@notice Note that `owner` cannot be the zero address.
@param owner The 20-byte owner address.
@return uint256 The 32-byte token amount owned
by `owner`.
"""
assert owner != empty(address), "erc721: the zero address is not a valid owner"
return self._balances[owner]
@internal
@view
def _owner_of(token_id: uint256) -> address:
"""
@dev An `internal` helper function that returns the
owner of the `token_id` token.
@notice Note that `token_id` must exist.
@param token_id The 32-byte identifier of the token.
@return address The 20-byte owner address.
"""
owner: address = self._owners[token_id]
assert owner != empty(address), "erc721: invalid token ID"
return owner
@internal
@view
def _require_minted(token_id: uint256):
"""
@dev Reverts if the `token_id` has not yet been minted.
@param token_id The 32-byte identifier of the token.
"""
assert self._exists(token_id), "erc721: invalid token ID"
@internal
@view
def _exists(token_id: uint256) -> bool:
"""
@dev Returns whether `token_id` exists.
@notice 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`).
@param token_id The 32-byte identifier of the token.
@return bool The verification whether `token_id` exists
or not.
"""
return self._owners[token_id] != empty(address)
@internal
def _approve(to: address, token_id: uint256):
"""
@dev Approves `to` to operate on `token_id`.
@param to The 20-byte spender address.
@param token_id The 32-byte identifier of the token.
"""
self._token_approvals[token_id] = to
log IERC721.Approval(owner=self._owner_of(token_id), approved=to, token_id=token_id)
@internal
@view
def _get_approved(token_id: uint256) -> address:
"""
@dev An `internal` helper function that returns the
account approved for `token_id` token.
@notice Note that `token_id` must exist.
@param token_id The 32-byte identifier of the token.
@return address The 20-byte approved address.
"""
self._require_minted(token_id)
return self._token_approvals[token_id]
@internal
def _set_approval_for_all(owner: address, operator: address, approved: bool):
"""
@dev Approves `operator` to operate on all of `owner` tokens.
@param owner The 20-byte owner address.
@param operator The 20-byte operator address.
@param approved The Boolean variable that sets the
approval status.
"""
assert owner != operator, "erc721: approve to caller"
self.isApprovedForAll[owner][operator] = approved
log IERC721.ApprovalForAll(owner=owner, operator=operator, approved=approved)
@internal
def _is_approved_or_owner(spender: address, token_id: uint256) -> bool:
"""
@dev Returns whether `spender` is allowed to manage
`token_id`.
@notice Note that `token_id` must exist.
@param spender The 20-byte spender address.
@param token_id The 32-byte identifier of the token.
"""
owner: address = self._owner_of(token_id)
return ((spender == owner) or (self.isApprovedForAll[owner][spender]) or (self._get_approved(token_id) == spender))
@internal
def _safe_mint(owner: address, token_id: uint256, data: Bytes[1_024]):
"""
@dev Safely mints `token_id` and transfers it to `owner`.
@notice Note that `token_id` must not exist. Also, if `owner`
refers to a smart contract, it must implement
{IERC721Receiver-onERC721Received}, which is called
upon a safe transfer.
WARNING: This `internal` function without access
restriction can potentially allow a reentrancy
attack when transferring tokens to an untrusted
contract, when invoking {IERC721Receiver-onERC721Received}
on the receiver. We ensure that we consistently
follow the checks-effects-interactions (CEI) pattern
to avoid being vulnerable to this type of attack.
@param owner The 20-byte owner address.
@param token_id The 32-byte identifier of the token.
@param data The maximum 1,024-byte additional data
with no specified format that is sent
to `owner`.
"""
self._mint(owner, token_id)
assert self._check_on_erc721_received(
empty(address), owner, token_id, data
), "erc721: transfer to non-IERC721Receiver implementer"
@internal
def _mint(owner: address, token_id: uint256):
"""
@dev Mints `token_id` and transfers it to `owner`.
@notice Note that `token_id` must not exist and
`owner` cannot be the zero address.
WARNING: Usage of this method is discouraged,
use `_safe_mint` whenever possible.
@param owner The 20-byte owner address.
@param token_id The 32-byte identifier of the token.
"""
assert owner != empty(address), "erc721: mint to the zero address"
assert not self._exists(token_id), "erc721: token already minted"
self._before_token_transfer(empty(address), owner, token_id)
# Checks that the `token_id` was not minted by the
# `_before_token_transfer` hook.
assert not self._exists(token_id), "erc721: token already minted"
# Theoretically, the following line could overflow
# if all 2**256 token IDs were minted to the same owner.
# However, since we have bounded the dynamic array
# `_all_tokens` by the maximum value of `uint64`,
# this is no longer even theoretically possible.
self._balances[owner] = unsafe_add(self._balances[owner], 1)
self._owners[token_id] = owner
log IERC721.Transfer(sender=empty(address), receiver=owner, token_id=token_id)
self._after_token_transfer(empty(address), owner, token_id)
@internal
def _safe_transfer(owner: address, to: address, token_id: uint256, data: Bytes[1_024]):
"""
@dev Safely transfers `token_id` token from
`owner` to `to`, checking first that contract
recipients are aware of the ERC-721 protocol
to prevent tokens from being forever locked.
@notice This `internal` function is equivalent to
`safeTransferFrom`, and can be used to e.g.
implement alternative mechanisms to perform
token transfers, such as signature-based.
Note that `owner` and `to` cannot be the zero
address. Also, `token_id` token must exist and
must be owned by `owner`. Eventually, if `to`
refers to a smart contract, it must implement
{IERC721Receiver-onERC721Received}, which is
called upon a safe transfer.
WARNING: This `internal` function can potentially
allow a reentrancy attack when transferring tokens
to an untrusted contract, when invoking {IERC721Receiver-onERC721Received}
on the receiver. We ensure that we consistently
follow the checks-effects-interactions (CEI) pattern
to avoid being vulnerable to this type of attack.
@param owner The 20-byte owner address.
@param to The 20-byte receiver address.
@param token_id The 32-byte identifier of the token.
@param data The maximum 1,024-byte additional data
with no specified format that is sent
to `to`.
"""
self._transfer(owner, to, token_id)
assert self._check_on_erc721_received(
owner, to, token_id, data
), "erc721: transfer to non-IERC721Receiver implementer"
@internal
def _transfer(owner: address, to: address, token_id: uint256):
"""
@dev Transfers `token_id` from `owner` to `to`.
As opposed to `transferFrom`, this imposes
no restrictions on `msg.sender`.
@notice Note that `to` cannot be the zero address.
Also, `token_id` token must be owned by
`owner`.
@param owner The 20-byte owner address.
@param to The 20-byte receiver address.
@param token_id The 32-byte identifier of the token.
"""
assert self._owner_of(token_id) == owner, "erc721: transfer from incorrect owner"
assert to != empty(address), "erc721: transfer to the zero address"
self._before_token_transfer(owner, to, token_id)
# Checks that the `token_id` was not transferred by the
# `_before_token_transfer` hook.
assert self._owner_of(token_id) == owner, "erc721: transfer from incorrect owner"
self._token_approvals[token_id] = empty(address)
# See comment why an overflow is not possible in the
# following two lines above at `_mint`.
self._balances[owner] = unsafe_sub(self._balances[owner], 1)
self._balances[to] = unsafe_add(self._balances[to], 1)
self._owners[token_id] = to
log IERC721.Transfer(sender=owner, receiver=to, token_id=token_id)
self._after_token_transfer(owner, to, token_id)
@internal
def _set_token_uri(token_id: uint256, token_uri: String[432]):
"""
@dev Sets `token_uri` as the token URI of `token_id`.
@notice Note that `token_id` must exist.
@param token_id The 32-byte identifier of the token.
@param token_uri The maximum 432-character user-readable
string URI for computing `tokenURI`.
"""
assert self._exists(token_id), "erc721: URI set of nonexistent token"
self._token_uris[token_id] = token_uri
log IERC4906.MetadataUpdate(_tokenId=token_id)
@internal
@view
def _total_supply() -> uint256:
"""
@dev An `internal` helper function that returns the amount
of tokens in existence.
@return uint256 The 32-byte token supply.
"""
return len(self._all_tokens)
@internal
def _burn(token_id: uint256):
"""
@dev Destroys `token_id`.
@notice The approval is cleared when the token is burned.
This is an `internal` function that does not check
if the sender is authorised to operate on the token.
Note that `token_id` must exist.
@param token_id The 32-byte identifier of the token.
"""
owner: address = self._owner_of(token_id)
self._before_token_transfer(owner, empty(address), token_id)
# Updates ownership in case the `token_id` was
# transferred by the `_before_token_transfer` hook.
owner = self._owner_of(token_id)
self._token_approvals[token_id] = empty(address)
# Overflow is not possible, as in this case more tokens would
# have to be burned/transferred than the owner originally
# received through minting and transfer.
self._balances[owner] = unsafe_sub(self._balances[owner], 1)
self._owners[token_id] = empty(address)
log IERC721.Transfer(sender=owner, receiver=empty(address), token_id=token_id)
self._after_token_transfer(owner, empty(address), token_id)
# Checks whether a token-specific URI has been set for the token
# and deletes the token URI from the storage mapping.
if len(self._token_uris[token_id]) != empty(uint256):
self._token_uris[token_id] = ""
@internal
def _check_on_erc721_received(owner: address, to: address, token_id: uint256, data: Bytes[1_024]) -> bool:
"""
@dev An `internal` function that invokes {IERC721Receiver-onERC721Received}
on a target address. The call is not executed
if the target address is not a contract.
@param owner The 20-byte address which previously
owned the token.
@param to The 20-byte address receiver address.
@param token_id The 32-byte identifier of the token.
@param data The maximum 1,024-byte additional data
with no specified format.
@return bool The verification whether the call correctly
returned the expected magic value.
"""
# Contract case.
if to.is_contract:
return_value: bytes4 = extcall IERC721Receiver(to).onERC721Received(msg.sender, owner, token_id, data)
assert return_value == method_id(
"onERC721Received(address,address,uint256,bytes)", output_type=bytes4
), "erc721: transfer to non-IERC721Receiver implementer"
return True
# EOA case.
return True
@internal
def _before_token_transfer(owner: address, to: address, token_id: uint256):
"""
@dev Hook that is called before any token transfer.
This includes minting and burning.
@notice The calling conditions are:
- when `owner` and `to` are both non-zero,
`owner`'s tokens will be transferred to `to`,
- when `owner` is zero, the tokens will
be minted for `to`,
- when `to` is zero, `owner`'s tokens will
be burned,
- `owner` and `to` are never both zero.
@param owner The 20-byte owner address.
@param to The 20-byte receiver address.
@param token_id The 32-byte identifier of the token.
"""
if owner == empty(address):
self._add_token_to_all_tokens_enumeration(token_id)
elif owner != to:
self._remove_token_from_owner_enumeration(owner, token_id)
if to == empty(address):
self._remove_token_from_all_tokens_enumeration(token_id)
elif to != owner:
self._add_token_to_owner_enumeration(to, token_id)
@internal
def _after_token_transfer(owner: address, to: address, token_id: uint256):
"""
@dev Hook that is called after any token transfer.
This includes minting and burning.
@notice The calling conditions are:
- when `owner` and `to` are both non-zero,
`owner`'s tokens were transferred to `to`,
- when `owner` is zero, the tokens were
be minted for `to`,
- when `to` is zero, `owner`'s tokens will
be burned,
- `owner` and `to` are never both zero.
@param owner The 20-byte owner address.
@param to The 20-byte receiver address.
@param token_id The 32-byte identifier of the token.
"""
pass
@internal
def _add_token_to_owner_enumeration(to: address, token_id: uint256):
"""
@dev This is an `internal` function that adds a token
to the ownership-tracking data structures.
@param to The 20-byte receiver address.
@param token_id The 32-byte identifier of the token.
"""
length: uint256 = self._balance_of(to)
self._owned_tokens[to][length] = token_id
self._owned_tokens_index[token_id] = length
@internal
def _add_token_to_all_tokens_enumeration(token_id: uint256):
"""
@dev This is an `internal` function that adds a token
to the token tracking data structures.
@param token_id The 32-byte identifier of the token.
"""
self._all_tokens_index[token_id] = len(self._all_tokens)
self._all_tokens.append(token_id)
@internal
def _remove_token_from_owner_enumeration(owner: address, token_id: uint256):
"""
@dev This is an `internal` function that removes a token
from the ownership-tracking data structures.
@notice Note that while the token is not assigned a new
owner, the `_owned_tokens_index` mapping is NOT
updated: this allows for gas optimisations e.g.
when performing a transfer operation (avoiding
double writes). This function has O(1) time
complexity, but alters the order of the
`_owned_tokens` array.
@param owner The 20-byte owner address.
@param token_id The 32-byte identifier of the token.
"""
# To prevent a gap in `owner`'s tokens array,
# we store the last token in the index of the
# token to delete, and then delete the last slot.
last_token_index: uint256 = self._balance_of(owner) - 1
token_index: uint256 = self._owned_tokens_index[token_id]
# When the token to delete is the last token,
# the swap operation is unnecessary.
if token_index != last_token_index:
last_token_id: uint256 = self._owned_tokens[owner][last_token_index]
# Moves the last token to the slot of the to-delete token.
self._owned_tokens[owner][token_index] = last_token_id
# Updates the moved token's index.
self._owned_tokens_index[last_token_id] = token_index
# This also deletes the contents at the
# last position of the array.
self._owned_tokens_index[token_id] = empty(uint256)
self._owned_tokens[owner][last_token_index] = empty(uint256)
@internal
def _remove_token_from_all_tokens_enumeration(token_id: uint256):
"""
@dev This is an `internal` function that removes a token
from the token tracking data structures.
@notice This function has O(1) time complexity, but
alters the order of the `_all_tokens` array.
@param token_id The 32-byte identifier of the token.
"""
# 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.
last_token_index: uint256 = len(self._all_tokens) - 1
token_index: uint256 = self._all_tokens_index[token_id]
# 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 burned) that we still do the
# swap here to avoid the gas cost of adding
# an `if` statement (like in `_remove_token_from_owner_enumeration`).
last_token_id: uint256 = self._all_tokens[last_token_index]
# Moves the last token to the slot of the to-delete token.
self._all_tokens[token_index] = last_token_id
# Updates the moved token's index.
self._all_tokens_index[last_token_id] = token_index
# This also deletes the contents at the
# last position of the array.
self._all_tokens_index[token_id] = empty(uint256)
self._all_tokens.pop()# @version 0.4.3
"""
OpenZeppelin's IVotes interface:
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/governance/utils/IVotes.sol
"""
# @dev Emitted when an account changes their delegate.
event DelegateChanged:
delegator: indexed(address)
fromDelegate: indexed(address)
toDelegate: indexed(address)
# @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of voting units.
event DelegateVotesChanged:
delegate: indexed(address)
previousVotes: uint256
newVotes: uint256
@external
@view
def getVotes(account: address) -> uint256:
"""
@dev Returns the current amount of votes that `account` has.
"""
...
@external
@view
def getPastVotes(account: address, timepoint: uint256) -> 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.
"""
...
@external
@view
def getPastTotalSupply(timepoint: uint256) -> 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.
@notice 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.
"""
...
@external
@view
def delegates(account: address) -> address:
"""
@dev Returns the delegate that `account` has chosen.
"""
...
@external
def delegate(delegatee: address):
"""
@dev Delegates votes from the sender to `delegatee`.
"""
...
@external
def delegateBySig(delegatee: address, nonce: uint256, expiry: uint256, v: uint8, r: bytes32, s: bytes32):
"""
@dev Delegates votes from signer to `delegatee`.
"""
...{
"outputSelection": {
"contracts/dao/VotingEscrow.vy": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
},
"search_paths": [
".venv/lib/pypy3.11/site-packages",
"."
]
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_for","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":true,"name":"locktime","type":"uint256"},{"indexed":false,"name":"type","type":"uint256"},{"indexed":false,"name":"ts","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_for","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"ts","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"prevSupply","type":"uint256"},{"indexed":false,"name":"supply","type":"uint256"}],"name":"Supply","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"clearance_checker","type":"address"}],"name":"SetTransferClearanceChecker","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previous_owner","type":"address"},{"indexed":true,"name":"new_owner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"minter","type":"address"},{"indexed":false,"name":"status","type":"bool"}],"name":"RoleMinterChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"approved","type":"address"},{"indexed":true,"name":"token_id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"operator","type":"address"},{"indexed":false,"name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"receiver","type":"address"},{"indexed":true,"name":"token_id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"token_id","type":"uint256"}],"name":"ownerOf","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"to","type":"address"},{"name":"token_id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"name":"operator","type":"address"},{"name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"token_id","type":"uint256"}],"name":"getApproved","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"address"},{"name":"arg1","type":"address"}],"name":"isApprovedForAll","outputs":[{"name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"owner","type":"address"},{"name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"token_id","type":"uint256"}],"name":"tokenURI","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"new_owner","type":"address"}],"name":"transfer_ownership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"interface_id","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"account","type":"address"}],"name":"delegates","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"delegatee","type":"address"},{"name":"nonce","type":"uint256"},{"name":"expiry","type":"uint256"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"clock","outputs":[{"name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_value","type":"uint256"},{"name":"_unlock_time","type":"uint256"}],"name":"create_lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_value","type":"uint256"}],"name":"increase_amount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_value","type":"uint256"},{"name":"_for","type":"address"}],"name":"increase_amount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_unlock_time","type":"uint256"}],"name":"increase_unlock_time","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"infinite_lock_toggle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_for","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"account","type":"address"}],"name":"getVotes","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"account","type":"address"},{"name":"timepoint","type":"uint256"}],"name":"getPastVotes","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalVotes","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"timepoint","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"transfer_clearance_checker","type":"address"}],"name":"set_transfer_clearance_checker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"owner","type":"address"},{"name":"to","type":"address"},{"name":"token_id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"owner","type":"address"},{"name":"to","type":"address"},{"name":"token_id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"owner","type":"address"},{"name":"to","type":"address"},{"name":"token_id","type":"uint256"},{"name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"addr","type":"address"}],"name":"get_last_user_slope","outputs":[{"name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"addr","type":"address"}],"name":"get_last_user_point","outputs":[{"components":[{"name":"bias","type":"uint256"},{"name":"slope","type":"uint256"}],"name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"_addr","type":"address"}],"name":"locked__end","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOKEN","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supply","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"address"}],"name":"locked","outputs":[{"components":[{"name":"amount","type":"int256"},{"name":"end","type":"uint256"}],"name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"uint256"}],"name":"point_history","outputs":[{"components":[{"name":"bias","type":"int256"},{"name":"slope","type":"int256"},{"name":"ts","type":"uint256"}],"name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"address"},{"name":"arg1","type":"uint256"}],"name":"user_point_history","outputs":[{"components":[{"name":"bias","type":"int256"},{"name":"slope","type":"int256"},{"name":"ts","type":"uint256"}],"name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"address"}],"name":"user_point_epoch","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"uint256"}],"name":"slope_changes","outputs":[{"name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"transfer_clearance_checker","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CLOCK_MODE","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"token","type":"address"},{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"base_uri","type":"string"}],"outputs":[],"stateMutability":"nonpayable","type":"constructor"}]Contract Creation Code
0x6143c051503461033b5760206144f85f395f518060a01c61033b576103805260206145185f395f516020816144f8015f395f516019811161033b57506039816144f8016103a0395060206145385f395f516020816144f8015f395f516005811161033b57506025816144f8016103e0395060206145585f395f516020816144f8015f395f516050811161033b57506070816144f80161042039506100a161018d565b60396103a06101c05e60256103e06102005e60706104206102405e600b6104a0527f4a75737420736179206e6f0000000000000000000000000000000000000000006104c0526104a0602b816102c05e5060096104e0527f746f204549503731320000000000000000000000000000000000000000000000610500526104e06029816103205e50610130610273565b610380516143c05242680100000000000000115561418061033f610000396143e0610000f35b5f546060526040515f556040516060517f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f6080a3565b33604052610199610156565b565b7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f606052614240516080526142a05160a0524660c0523060e05260a06040526040805160208201209050815250565b602061010051015f81601f0160051c6003811161033b57801561022957905b8060051b61010001518160051b6060016141800152600101818118610209575b5050506101605161426052610180516142805261010051610120206142405261016051610180206142a05261025f6101a061019b565b6101a05161418052466141a052306141c052565b5f6801000000000000000b556101c0516142c0526101e0516142e05261020051614300526102205161432052602061024051015f81601f0160051c6004811161033b5780156102df57905b8060051b61024001518160051b6101c00161418001526001018181186102be575b50505060016002336020525f5260405f2055337fbb6e183664bd7425a9e444072cb0f1c7f7c4d5486a36d7d24d0b0735687c2ef46001610360526020610360a260526102c06101005e60346103206101605e6103396101ea565b565b5f80fd5f3560e01c60026028820660011b61413001601e395f51565b6370a0823181186100555760243610341761412c576004358060a01c61412c5761016052602061016051604052610050610180612615565b610180f35b63adc6358981186126115760243610341761412c576004358060a01c61412c576040526801000000000000000d6040516020525f5260405f206001810190505460605260206060f35b636352211e81186100cb5760243610341761412c5760206004356040526100c66101406126c5565b610140f35b63587cde1e81186126115760243610341761412c576004358060a01c61412c5760405260206040f35b63095ea7b381186102c357604336111561412c576004358060a01c61412c576101a0526024356040526101286101e06126c5565b6101e0516101c0526101c0516101a051186101da576020806102605260216101e0527f6572633732313a20617070726f76616c20746f2063757272656e74206f776e65610200527f7200000000000000000000000000000000000000000000000000000000000000610220526101e08161026001604182825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610240528060040161025cfd5b6101c05133186101eb576001610209565b60016101c0516020525f5260405f2080336020525f5260405f209050545b6102aa5760208061026052603d6101e0527f6572633732313a20617070726f76652063616c6c6572206973206e6f7420746f610200527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c000000610220526101e08161026001605d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610240528060040161025cfd5b6101a05161014052602435610160526102c1612757565b005b6306fdde03811861030d573461412c576020806040528060400160396142c082398051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b6365fc387381186126115760443610341761412c575f5c60011461412c5760015f5d60243562093a808104905062093a8081028162093a8082041861412c579050610520526801000000000000000d336020525f5260405f2080546105405260018101546105605250630784ce0060043510156103fc576020806105e0526009610580527f4d696e2076616c756500000000000000000000000000000000000000000000006105a052610580816105e001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b610540511561047d576020806105e0526019610580527f5769746864726177206f6c6420746f6b656e73206669727374000000000000006105a052610580816105e001603982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b42610520511161052457602080610600526026610580527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e20746865206105a0527f66757475726500000000000000000000000000000000000000000000000000006105c0526105808161060001604682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105e052806004016105fcfd5b42630784ce00810181811061412c5790506105205111156105b7576020806105e052601e610580527f566f74696e67206c6f636b2063616e2062652034207965617273206d617800006105a052610580816105e001603e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b336102e05260043561030052610520516103205260406105406103405e6002610380526105e2613027565b336102605233610280526105f4613495565b5f5f5d005b63a22cb46581186126115760443610341761412c576004358060a01c61412c57610160526024358060011c61412c576101805233604052604061016060605e6106406127b1565b005b63081812fc81186106705760243610341761412c5760206004356101605261066b610180612926565b610180f35b634957677c811861068f5760243610341761412c5733610520526107f3565b637c74a17481186126115760243610341761412c576004358060a01c61412c57604052680129a2241af62c00106040516020525f5260405f2054606052680129a2241af62c000f6040516020525f5260405f206003606051670de0b6b3a764000081101561412c5702810190506001810190505460805260206080f35b63e985e9c581186126115760443610341761412c576004358060a01c61412c576040526024358060a01c61412c5760605260016040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b6395d89b4181186107ae573461412c5760208060405280604001602561430082398051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b63c2c4c5c181186107ce573461412c5760a0366040376107cc612a05565b005b635e56b23981186126115760443610341761412c576024358060a01c61412c57610520525b5f5c60011461412c5760015f5d6801000000000000000d610520516020525f5260405f2080546105405260018101546105605250630784ce006004351061412c5760016105405112156108b8576020806105e0526016610580527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006105a052610580816105e001603682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b42610560511161095f57602080610600526024610580527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686105a0527f64726177000000000000000000000000000000000000000000000000000000006105c0526105808161060001604482825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105e052806004016105fcfd5b610520516102e052600435610300525f6103205260406105406103405e60046103805261098a613027565b5f5f5d005b6318160ddd81186109af573461412c5760206109ab604061294a565b6040f35b63c87b56dd81186126115760243610341761412c576004356060526109d261289a565b6801000000000000000a6004356020525f5260405f2060208154015f81601f0160051c600f811161412c578015610a1d57905b808401548160051b6101600152600101818118610a05575b5050505060206143406103403961034051610a79576020806103605280610360016020610160510180610160835e508051806020830101601f825f03163682375050601f19601f82516020010116905081019050610360610c24565b6101605115610b03576020806105a0525f60206143405f395f5181610380018161436082395080820191505061016051816103800181610180825e5080820191505080610360526103609050816105a00160208251018083835e508051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506105a0610c24565b6103405115610bec576020806104c0525f60206143405f395f5181610400018161436082395080820191505060043580610b4b57603061036152600161036052610360610b8a565b5f604f905b82610b685780816103ae0352806103ae039250610b86565b600a8306603001816103ae0352600a83049250600101818118610b50575b5050805b905080516020820183610400018282825e505080830192505050806103e0526103e09050816104c00160208251018083835e508051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506104c0610c24565b6020806103805280610380015f81528051806020830101601f825f03163682375050601f19601f825160200101169050810190506103805bf35b634f6ccce781186126115760243610341761412c57610c45604061294a565b60405160043510610ce55760208060e05260226060527f6572633732313a20676c6f62616c20696e646578206f7574206f6620626f756e6080527f647300000000000000000000000000000000000000000000000000000000000060a05260608160e001604282825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060c0528060040160dcfd5b60043560095481101561412c57600a015460405260206040f35b632f745c598118610e085760443610341761412c576004358060a01c61412c576101605261016051604052610d35610180612615565b6101805160243510610dde576020806102205260216101a0527f6572633732313a206f776e657220696e646578206f7574206f6620626f756e646101c0527f73000000000000000000000000000000000000000000000000000000000000006101e0526101a08161022001604182825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b6007610160516020525f5260405f20806024356020525f5260405f20905054610180526020610180f35b63d1febfb981186126115760243610341761412c576003600435670de0b6b3a764000081101561412c57026801000000000000000f018054604052600181015460605260028101546080525060606040f35b63f0350c0481186126115760243610341761412c576004358060a01c61412c5761010052610e86612952565b61010051610f2b576020806101a0526026610120527f6f776e61626c653a206e6577206f776e657220697320746865207a65726f2061610140527f646472657373000000000000000000000000000000000000000000000000000061016052610120816101a001604682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610180528060040161019cfd5b61010051604052610f3a6129ce565b005b638da5cb5b8118612611573461412c575f5460405260206040f35b6301ffc9a781186110935760243610341761412c576004358060201b61412c576040526040517f01ffc9a7000000000000000000000000000000000000000000000000000000008118610fab576001611088565b7f80ac58cd000000000000000000000000000000000000000000000000000000008118610fd9576001611088565b7f5b5e139f000000000000000000000000000000000000000000000000000000008118611007576001611088565b7f780e9d63000000000000000000000000000000000000000000000000000000008118611035576001611088565b7f49064906000000000000000000000000000000000000000000000000000000008118611063576001611088565b7fe90fb3f6000000000000000000000000000000000000000000000000000000008118155b905060805260206080f35b63010ae75781186126115760243610341761412c576004358060a01c61412c57604052680129a2241af62c00106040516020525f5260405f205460605260206060f35b635c19a95c811861116a5760243610341761412c576004358060a01c61412c5760405260208060c052600d6060527f4e6f7420737570706f727465640000000000000000000000000000000000000060805260608160c001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060a0528060040160bcfd5b6391ddadf4811861118c573461412c57428060301c61412c5760405260206040f35b638e539e8c81186126115760243610341761412c5760206004356040526111b46101606137e5565b610160f35b63c3cda52081186126115760c43610341761412c576004358060a01c61412c576040526064358060081c61412c5760605260208060e052600d6080527f4e6f7420737570706f727465640000000000000000000000000000000000000060a05260808160e001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060c0528060040160dcfd5b63eff7a61281186126115760243610341761412c575f5c60011461412c5760015f5d6801000000000000000d336020525f5260405f208054610520526001810154610540525060043562093a808104905062093a8081028162093a8082041861412c579050610560526001610520511215611348576020806105e0526011610580527f4e6f7468696e67206973206c6f636b65640000000000000000000000000000006105a052610580816105e001603182825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b4261054051116113ca576020806105e052600c610580527f4c6f636b206578706972656400000000000000000000000000000000000000006105a052610580816105e001602c82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b61054051610560511161144f576020806105e052601f610580527f43616e206f6e6c7920696e637265617365206c6f636b206475726174696f6e006105a052610580816105e001603f82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b42630784ce00810181811061412c5790506105605111156114e2576020806105e052601e610580527f566f74696e67206c6f636b2063616e2062652034207965617273206d617800006105a052610580816105e001603e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b336102e0525f61030052610560516103205260406105206103405e60086103805261150b613027565b5f5f5d005b63b25ec64581186117dd573461412c575f5c60011461412c5760015f5d6801000000000000000d336020525f5260405f20805461052052600181015461054052504261054051116115d3576020806105c052600c610560527f4c6f636b2065787069726564000000000000000000000000000000000000000061058052610560816105c001602c82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105a052806004016105bcfd5b6001610520511215611657576020806105c0526011610560527f4e6f7468696e67206973206c6f636b656400000000000000000000000000000061058052610560816105c001603182825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105a052806004016105bcfd5b5f61056052610540511961178957680129a2241af62c0012546105805261058051156117535761058051635f1a104c6105a052336105c05260206105a060246105bc845afa6116a8573d5f5f3e3d5ffd5b3d602081183d6020100218806105a0016105c01161412c576105a0518060011c61412c576105e052506105e09050516117535760208061066052600b610600527f4e6f7420616c6c6f776564000000000000000000000000000000000000000000610620526106008161066001602b82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610640528060040161065cfd5b42630784ce00810181811061412c57905062093a808104905062093a8081028162093a8082041861412c579050610560526117af565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610560525b336102e0525f61030052610560516103205260406105206103405e6008610380526117d8613027565b5f5f5d005b6323b872dd81186126115760643610341761412c576004358060a01c61412c57610400526024358060a01c61412c576104205233610180526044356101a052611827610440613a97565b610440516118cc576020806104e052602d610460527f6572633732313a2063616c6c6572206973206e6f7420746f6b656e206f776e65610480527f72206f7220617070726f766564000000000000000000000000000000000000006104a052610460816104e001604d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06104c052806004016104dcfd5b610400516044351815611951576020806104a052600e610440527f57726f6e6720746f6b656e20494400000000000000000000000000000000000061046052610440816104a001602e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610480528060040161049cfd5b604061040060405e611964610440613b13565b610440516119e4576020806104c052600f610460527f4e656564206d61782076654c6f636b000000000000000000000000000000000061048052610460816104c001602f82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06104a052806004016104bcfd5b60406104006102e05e6119f5613d5e565b60443561026052611a046136cd565b005b633ccfd60b8118612611573461412c57336102e052611a45565b6351cff8d98118611c7e5760243610341761412c576004358060a01c61412c576102e0525b5f5c60011461412c5760015f5d6801000000000000000d336020525f5260405f208054610300526001810154610320525061032051421015611af9576020806103a0526016610340527f546865206c6f636b206469646e2774206578706972650000000000000000000061036052610340816103a001603682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610380528060040161039cfd5b610300515f811261412c576103405260406103006103605e5f610320525f610300526801000000000000000d336020525f5260405f20610300518155610320516001820155506801000000000000000c546103a0526103a0516103405180820382811161412c57905090506103c0526103c0516801000000000000000c5533604052604061036060605e604061030060a05e611b93612a05565b3361026052611ba06136cd565b60206143c05f395f5163a9059cbb6103e0526102e05161040052610340516104205260206103e060446103fc5f855af1611bdc573d5f5f3e3d5ffd5b3d602081183d6020100218806103e0016104001161412c576103e0518060011c61412c5761044052506104409050511561412c576102e051337ff341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567610340516103e052426104005260406103e0a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c60406103a06103e05e60406103e0a15f5f5d005b634bf5d7e98118612611573461412c57602080608052600e6040527f6d6f64653d74696d657374616d70000000000000000000000000000000000000606052604081608001602e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506080f35b639ab24eb08118611e235760243610341761412c576004358060a01c61412c57604052680129a2241af62c00106040516020525f5260405f2054606052606051611d44575f60805260206080611e21565b680129a2241af62c000f6040516020525f5260405f206003606051670de0b6b3a764000081101561412c5702810190508054608052600181015460a052600281015460c0525060805160a0514260c05180820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c57905090508082038281135f83121861412c57905090506080527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60805113611e0f575f6080525b6080515f811261412c5760e052602060e05bf35b6372e3af5081186126115760243610341761412c576004358060a01c61412c5761010052611e4f612952565b61010051680129a2241af62c0012557fa7fad85ba00d983deab3d92e1bbba81938da68cddbcb5d4f018ef3b1e66534fa61010051610120526020610120a1005b633a46b1a881186126115760443610341761412c576004358060a01c61412c576040525f606052680129a2241af62c00106040516020525f5260405f2054608052680129a2241af62c000f6040516020525f5260405f20600281019050546024351015611f03575f60a052602060a061207a565b5f6080905b8060a0526080516060511015611fac5760605160805180820182811061412c57905090506001810181811061412c5790508060011c905060c052602435680129a2241af62c000f6040516020525f5260405f20600360c051670de0b6b3a764000081101561412c570281019050600281019050541115611f9a5760c0516001810381811161412c579050608052611fa1565b60c0516060525b600101818118611f08575b5050680129a2241af62c000f6040516020525f5260405f206003606051670de0b6b3a764000081101561412c570281019050805460a052600181015460c052600281015460e0525060a05160c05160243560e05180820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c57905090508082038281135f83121861412c579050905060a0525f60a0511215612066575f61010052602061010061207a565b60a0515f811261412c576101005260206101005bf35b630d15fd7781186120a2573461412c5760204260405261209d6101606137e5565b610160f35b63cbf9fe5f81186120ef5760243610341761412c576004358060a01c61412c576040526801000000000000000d6040516020525f5260405f20805460605260018101546080525060406060f35b6328d09d4781186126115760443610341761412c576004358060a01c61412c57604052680129a2241af62c000f6040516020525f5260405f206003602435670de0b6b3a764000081101561412c57028101905080546060526001810154608052600281015460a0525060606060f35b6342842e0e81186126115760643610341761412c575f610a00526121b2565b63b88d4fde81186124935760843610341761412c576064356004018035610400811161412c575060208135018082610a003750505b6004358060a01c61412c576109c0526024358060a01c61412c576109e05233610180526044356101a0526121e7610e40613a97565b610e405161228c57602080610ee052602d610e60527f6572633732313a2063616c6c6572206973206e6f7420746f6b656e206f776e65610e80527f72206f7220617070726f76656400000000000000000000000000000000000000610ea052610e6081610ee001604d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610ec05280600401610edcfd5b6109c051604435181561231157602080610ea052600e610e40527f57726f6e6720746f6b656e204944000000000000000000000000000000000000610e6052610e4081610ea001602e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610e805280600401610e9cfd5b60406109c060405e612324610e40613b13565b610e40516123a457602080610ec052600f610e60527f4e656564206d61782076654c6f636b0000000000000000000000000000000000610e8052610e6081610ec001602f82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610ea05280600401610ebcfd5b60406109c06102e05e6123b5613d5e565b604435610260526123c46136cd565b60406109c060405e6044356080526020610a00510180610a0060a05e506123ec610e40613f9f565b610e405161249157602080610ee0526033610e60527f6572633732313a207472616e7366657220746f206e6f6e2d4945524337323152610e80527f6563656976657220696d706c656d656e74657200000000000000000000000000610ea052610e6081610ee001605382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610ec05280600401610edcfd5b005b63a7afdcae81186126115760243610341761412c576004358060a01c61412c57604052680129a2241af62c00106040516020525f5260405f2054606052680129a2241af62c000f6040516020525f5260405f206003606051670de0b6b3a764000081101561412c570281019050545f811261412c57608052680129a2241af62c000f6040516020525f5260405f206003606051670de0b6b3a764000081101561412c570281019050600181019050545f811261412c5760a05260406080f35b6382bfefc88118612611573461412c5760206143c060403960206040f35b63047fc9aa8118612611573461412c576801000000000000000c5460405260206040f35b63900cf0cf8118612611573461412c576801000000000000000e5460405260206040f35b637119748481186126115760243610341761412c57680129a2241af62c00116004356020525f5260405f205460405260206040f35b63cada36828118612611573461412c57680129a2241af62c00125460405260206040f35b5f5ffd5b6040516126b15760208060e052602d6060527f6572633732313a20746865207a65726f2061646472657373206973206e6f74206080527f612076616c6964206f776e65720000000000000000000000000000000000000060a05260608160e001604d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060c0528060040160dcfd5b60046040516020525f5260405f2054815250565b60056040516020525f5260405f205460605260605161274f5760208060e05260186080527f6572633732313a20696e76616c696420746f6b656e204944000000000000000060a05260808160e001603882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060c0528060040160dcfd5b606051815250565b610140516006610160516020525f5260405f20556101605161014051610160516040526127856101806126c5565b610180517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f6101a0a4565b6060516040511861282f5760208061010052601960a0527f6572633732313a20617070726f766520746f2063616c6c65720000000000000060c05260a08161010001603982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060e0528060040160fcfd5b60805160016040516020525f5260405f20806060516020525f5260405f209050556060516040517f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160805160a052602060a0a3565b60056040516020525f5260405f20541515815250565b6060516040526128aa6080612884565b6080516129245760208061010052601860a0527f6572633732313a20696e76616c696420746f6b656e204944000000000000000060c05260a08161010001603882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060e0528060040160fcfd5b565b6101605160605261293561289a565b6006610160516020525f5260405f2054815250565b600954815250565b5f543318156129cc5760208060a05260206040527f6f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260605260408160a001604082825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060805280600401609cfd5b565b5f546060526040515f556040516060517f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f6080a3565b6101003660e0376801000000000000000e546101e05260405115612b8c574260805111612a32575f612a3a565b600160605112155b15612aab5760805119612a57575f6101005260605160e052612aab565b606051630784ce008105905061010052610100516080514280820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c579050905060e0525b4260c05111612aba575f612ac2565b600160a05112155b15612b355760c05119612ae0575f6101605260a05161014052612b35565b60a051630784ce0081059050610160526101605160c0514280820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c5790509050610140525b680129a2241af62c00116080516020525f5260405f20546101a05260c05115612b8c5760805160c05118612b70576101a0516101c052612b8c565b680129a2241af62c001160c0516020525f5260405f20546101c0525b6040366102003742610240526101e05115612bdd5760036101e051670de0b6b3a764000081101561412c57026801000000000000000f01805461020052600181015461022052600281015461024052505b61024051610260526102605162093a808104905062093a8081028162093a8082041861412c579050610280525f60ff905b806102a0526102805162093a80810181811061412c579050610280525f6102c052426102805111612c5a57680129a2241af62c0011610280516020525f5260405f20546102c052612c60565b42610280525b6102005161022051610280516102605180820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c57905090508082038281135f83121861412c579050905061020052610220516102c0518082018281125f83121861412c5790509050610220527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102005113612d0c575f610200525b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102205113612d3c575f610220525b610280516102605261028051610240526101e0516001810181811061412c5790506101e05242610280511815612db25760036101e051670de0b6b3a764000081101561412c57026801000000000000000f0161020051815561022051600182015561024051600282015550600101818118612c0e575b50506101e0516801000000000000000e5560405115612e92576102205161016051610100518082038281135f83121861412c57905090508082018281125f83121861412c579050905061022052610200516101405160e0518082038281135f83121861412c57905090508082018281125f83121861412c5790509050610200527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102205113612e62575f610220525b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102005113612e92575f610200525b60036101e051670de0b6b3a764000081101561412c57026801000000000000000f01610200518155610220516001820155610240516002820155506040511561302557426080511115612f43576101a051610100518082018281125f83121861412c57905090506101a05260805160c05118612f27576101a051610160518082038281135f83121861412c57905090506101a0525b6101a051680129a2241af62c00116080516020525f5260405f20555b4260c0511115612f925760805160c05114612f92576101c051610160518082038281135f83121861412c57905090506101c0526101c051680129a2241af62c001160c0516020525f5260405f20555b680129a2241af62c00106040516020525f5260405f20546001810181811061412c5790506102a0526102a051680129a2241af62c00106040516020525f5260405f20554261018052680129a2241af62c000f6040516020525f5260405f2060036102a051670de0b6b3a764000081101561412c570281019050610140518155610160516001820155610180516002820155505b565b60406103406103a05e6801000000000000000c546103e0526103e0516103005180820182811061412c5790509050630784ce0081049050630784ce00810281630784ce0082041861412c57905061040052610400516103e05180820382811161412c579050905061042052610400516801000000000000000c5560406103a06104405e6103a051610420518060ff1c61412c578082018281125f83121861412c57905090506103a05261032051156130e257610320516103c0525b6801000000000000000d6102e0516020525f5260405f206103a05181556103c0516001820155506102e051604052604061044060605e60406103a060a05e613128612a05565b61042051156131a45760206143c05f395f516323b872dd61048052336104a052306104c052610420516104e0526020610480606461049c5f855af161316f573d5f5f3e3d5ffd5b3d602081183d602010021880610480016104a01161412c57610480518060011c61412c5761050052506105009050511561412c575b6103c0516102e051337f5f971bd00bf3ffbca8a6d72cdd4fd92cfd4f62636161921d1e5a64f0b64ccb6d6104205161048052610380516104a052426104c0526060610480a47f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c60406103e06104805e6040610480a1565b600954680100000000000000096040516020525f5260405f205560095467fffffffffffffffe811161412c5760405181600a01556001810160095550565b6101605160405261326b6101c0612615565b6101c0516001810381811161412c5790506101a0526008610180516020525f5260405f20546101c0526101a0516101c051146132fe576007610160516020525f5260405f20806101a0516020525f5260405f209050546101e0526101e0516007610160516020525f5260405f20806101c0516020525f5260405f209050556101c05160086101e0516020525f5260405f20555b5f6008610180516020525f5260405f20555f6007610160516020525f5260405f20806101a0516020525f5260405f20905055565b6009546001810381811161412c579050606052680100000000000000096040516020525f5260405f205460805260605160095481101561412c57600a015460a05260a05160805160095481101561412c57600a01556080516801000000000000000960a0516020525f5260405f20555f680100000000000000096040516020525f5260405f20556001600954801561412c57038060095550565b610160516040526133de6101c0612615565b6101c0516101a052610180516007610160516020525f5260405f20806101a0516020525f5260405f209050556101a0516008610180516020525f5260405f2055565b61020051613437576102405160405261345c61321b565b61022051610200511461345c576102005161016052610240516101805261345c613259565b610220516134735761024051604052613491613332565b6102005161022051146134915760406102206101605e6134916133cc565b565b565b61026051613515576020806103005260206102a0527f6572633732313a206d696e7420746f20746865207a65726f20616464726573736102c0526102a08161030001604082825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06102e052806004016102fcfd5b610280516040526135276102a0612884565b6102a051156135a85760208061032052601c6102c0527f6572633732313a20746f6b656e20616c7265616479206d696e746564000000006102e0526102c08161032001603c82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610300528060040161031cfd5b5f6102005260406102606102205e6135be613420565b610280516040526135d06102a0612884565b6102a051156136515760208061032052601c6102c0527f6572633732313a20746f6b656e20616c7265616479206d696e746564000000006102e0526102c08161032001603c82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610300528060040161031cfd5b60016004610260516020525f5260405f2054016004610260516020525f5260405f2055610260516005610280516020525f5260405f205561028051610260515f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f6102a0a45f604052604061026060605e6136cb613493565b565b610260516040526136df6102a06126c5565b6102a0516102805261028051610200525f610220526102605161024052613704613420565b610260516040526137166102a06126c5565b6102a051610280525f6006610260516020525f5260405f205560016004610280516020525f5260405f2054036004610280516020525f5260405f20555f6005610260516020525f5260405f2055610260515f610280517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f6102a0a4610280516040525f606052610260516080526137ac613493565b6801000000000000000a610260516020525f5260405f2054156137e3575f6801000000000000000a610260516020525f5260405f20555b565b6801000000000000000e54606052606051613803575f815250613a95565b68010000000000000011546040511015613820575f815250613a95565b5f6080525f6080905b8060a05260605160805110156138be5760805160605180820182811061412c57905090506001810181811061412c5790508060011c905060c052604051600360c051670de0b6b3a764000081101561412c57026801000000000000000f016002810190505411156138ac5760c0516001810381811161412c5790506060526138b3565b60c0516080525b600101818118613829575b50506003608051670de0b6b3a764000081101561412c57026801000000000000000f01805460a052600181015460c052600281015460e0525060605160805118613a2d5760e051610100525f60ff905b80610120526101005162093a80810181811061412c579050610100525f61014052604051610100511161395c57680129a2241af62c0011610100516020525f5260405f205461014052613964565b604051610100525b60a05160c0516101005160e05180820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c57905090508082038281135f83121861412c579050905060a0526040516101005118156139f95760c051610140518082018281125f83121861412c579050905060c0526101005160e05260010181811861390e575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60a05113613a87575f60a052613a87565b60a05160c05160405160e05180820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c57905090508082038281135f83121861412c579050905060a0525b60a0515f811261412c578152505b565b6101a051604052613aa96101e06126c5565b6101e0516101c0526101c0516101805118613ac5576001613b0e565b60016101c0516020525f5260405f2080610180516020525f5260405f20905054613b0b57610180516101a05161016052613b006101e0612926565b6101e0511815613b0e565b60015b815250565b680129a2241af62c00125460805260805115613bf757608051635f1a104c60a05260405160c052602060a0602460bc845afa613b51573d5f5f3e3d5ffd5b3d602081183d60201002188060a00160c01161412c5760a0518060011c61412c5760e0525060e0905051613bf75760208061016052600b610100527f4e6f7420616c6c6f776564000000000000000000000000000000000000000000610120526101008161016001602b82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610140528060040161015cfd5b6060516040511461412c5760403660a03742630784ce00810181811061412c57905062093a808104905062093a8081028162093a8082041861412c57905060e0526801000000000000000d6040516020525f5260405f2060018101905054610100526101005119613c69576001613c8f565b60e0516101005162093a808104905062093a8081028162093a8082041861412c57905018155b15613c9a57600160a0525b6801000000000000000d6060516020525f5260405f2060018101905054610120526101205119613ccb576001613cf1565b60e0516101205162093a808104905062093a8081028162093a8082041861412c57905018155b15613cfc57600160c0525b60a051613d09575f613d59565b60c051613d16575f613d59565b6101205162093a808104905062093a8081028162093a8082041861412c5790506101005162093a808104905062093a8081028162093a8082041861412c57905018155b815250565b6801000000000000000d6102e0516020525f5260405f20805461032052600181015461034052506801000000000000000d6102e0516020525f5260405f205f81555f6001820155506801000000000000000d610300516020525f5260405f208054610360526001810154610380525061036051610320518082018281125f83121861412c579050905061036052610360516801000000000000000d610300516020525f5260405f2055680129a2241af62c00106102e0516020525f5260405f20546001810181811061412c5790506103a0526103a051680129a2241af62c00106102e0516020525f5260405f2055680129a2241af62c000f6102e0516020525f5260405f2060036103a051670de0b6b3a764000081101561412c5702810190505f81555f600182015542600282015550680129a2241af62c0010610300516020525f5260405f20546001810181811061412c5790506103a0526103a051680129a2241af62c0010610300516020525f5260405f20556040366103c0376103805119613ef057610360516103e052613f47565b61036051630784ce00810590506103c0526103c051610380514280820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c57905090506103e0525b680129a2241af62c000f610300516020525f5260405f2060036103a051670de0b6b3a764000081101561412c5702810190506103e05181556103c05160018201554260028201555060a036604037613f9d612a05565b565b6060513b156141245760605163150b7a026104e05260803361050052604051610520526080516105405280610560528061050001602060a051018060a0835e508051806020830101601f825f03163682375050601f19601f8251602001011690508101505060206104e06104a46104fc5f855af161401f573d5f5f3e3d5ffd5b3d602081183d6020100218806104e0016105001161412c576104e0518060201b61412c576109a052506109a09050516104c0527f150b7a02000000000000000000000000000000000000000000000000000000006104c051181561411a576020806105605260336104e0527f6572633732313a207472616e7366657220746f206e6f6e2d4945524337323152610500527f6563656976657220696d706c656d656e74657200000000000000000000000000610520526104e08161056001605382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610540528060040161055cfd5b600181525061412a565b60018152505b565b5f80fd25520018257026110e5a098f217d0c261e8f0cff125b261106422611009e26111cf32611261100f42611070c261125942611076426111a0610d615102611207c11b91a2025ed0f3c25b805f9215e0f57855820d9bd45f862a3703e900ef32fd6148faa34ece7c6b6bd0e312da27c5c52082d41194180811850190260a165767970657283000403003900000000000000000000000001791f726b4103694969820be083196cc7c045ff000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000019566f74696e67457363726f773a205969656c6420426173697300000000000000000000000000000000000000000000000000000000000000000000000000000476655942000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x5f3560e01c60026028820660011b61413001601e395f51565b6370a0823181186100555760243610341761412c576004358060a01c61412c5761016052602061016051604052610050610180612615565b610180f35b63adc6358981186126115760243610341761412c576004358060a01c61412c576040526801000000000000000d6040516020525f5260405f206001810190505460605260206060f35b636352211e81186100cb5760243610341761412c5760206004356040526100c66101406126c5565b610140f35b63587cde1e81186126115760243610341761412c576004358060a01c61412c5760405260206040f35b63095ea7b381186102c357604336111561412c576004358060a01c61412c576101a0526024356040526101286101e06126c5565b6101e0516101c0526101c0516101a051186101da576020806102605260216101e0527f6572633732313a20617070726f76616c20746f2063757272656e74206f776e65610200527f7200000000000000000000000000000000000000000000000000000000000000610220526101e08161026001604182825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610240528060040161025cfd5b6101c05133186101eb576001610209565b60016101c0516020525f5260405f2080336020525f5260405f209050545b6102aa5760208061026052603d6101e0527f6572633732313a20617070726f76652063616c6c6572206973206e6f7420746f610200527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c000000610220526101e08161026001605d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610240528060040161025cfd5b6101a05161014052602435610160526102c1612757565b005b6306fdde03811861030d573461412c576020806040528060400160396142c082398051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b6365fc387381186126115760443610341761412c575f5c60011461412c5760015f5d60243562093a808104905062093a8081028162093a8082041861412c579050610520526801000000000000000d336020525f5260405f2080546105405260018101546105605250630784ce0060043510156103fc576020806105e0526009610580527f4d696e2076616c756500000000000000000000000000000000000000000000006105a052610580816105e001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b610540511561047d576020806105e0526019610580527f5769746864726177206f6c6420746f6b656e73206669727374000000000000006105a052610580816105e001603982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b42610520511161052457602080610600526026610580527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e20746865206105a0527f66757475726500000000000000000000000000000000000000000000000000006105c0526105808161060001604682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105e052806004016105fcfd5b42630784ce00810181811061412c5790506105205111156105b7576020806105e052601e610580527f566f74696e67206c6f636b2063616e2062652034207965617273206d617800006105a052610580816105e001603e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b336102e05260043561030052610520516103205260406105406103405e6002610380526105e2613027565b336102605233610280526105f4613495565b5f5f5d005b63a22cb46581186126115760443610341761412c576004358060a01c61412c57610160526024358060011c61412c576101805233604052604061016060605e6106406127b1565b005b63081812fc81186106705760243610341761412c5760206004356101605261066b610180612926565b610180f35b634957677c811861068f5760243610341761412c5733610520526107f3565b637c74a17481186126115760243610341761412c576004358060a01c61412c57604052680129a2241af62c00106040516020525f5260405f2054606052680129a2241af62c000f6040516020525f5260405f206003606051670de0b6b3a764000081101561412c5702810190506001810190505460805260206080f35b63e985e9c581186126115760443610341761412c576004358060a01c61412c576040526024358060a01c61412c5760605260016040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b6395d89b4181186107ae573461412c5760208060405280604001602561430082398051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b63c2c4c5c181186107ce573461412c5760a0366040376107cc612a05565b005b635e56b23981186126115760443610341761412c576024358060a01c61412c57610520525b5f5c60011461412c5760015f5d6801000000000000000d610520516020525f5260405f2080546105405260018101546105605250630784ce006004351061412c5760016105405112156108b8576020806105e0526016610580527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006105a052610580816105e001603682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b42610560511161095f57602080610600526024610580527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686105a0527f64726177000000000000000000000000000000000000000000000000000000006105c0526105808161060001604482825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105e052806004016105fcfd5b610520516102e052600435610300525f6103205260406105406103405e60046103805261098a613027565b5f5f5d005b6318160ddd81186109af573461412c5760206109ab604061294a565b6040f35b63c87b56dd81186126115760243610341761412c576004356060526109d261289a565b6801000000000000000a6004356020525f5260405f2060208154015f81601f0160051c600f811161412c578015610a1d57905b808401548160051b6101600152600101818118610a05575b5050505060206143406103403961034051610a79576020806103605280610360016020610160510180610160835e508051806020830101601f825f03163682375050601f19601f82516020010116905081019050610360610c24565b6101605115610b03576020806105a0525f60206143405f395f5181610380018161436082395080820191505061016051816103800181610180825e5080820191505080610360526103609050816105a00160208251018083835e508051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506105a0610c24565b6103405115610bec576020806104c0525f60206143405f395f5181610400018161436082395080820191505060043580610b4b57603061036152600161036052610360610b8a565b5f604f905b82610b685780816103ae0352806103ae039250610b86565b600a8306603001816103ae0352600a83049250600101818118610b50575b5050805b905080516020820183610400018282825e505080830192505050806103e0526103e09050816104c00160208251018083835e508051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506104c0610c24565b6020806103805280610380015f81528051806020830101601f825f03163682375050601f19601f825160200101169050810190506103805bf35b634f6ccce781186126115760243610341761412c57610c45604061294a565b60405160043510610ce55760208060e05260226060527f6572633732313a20676c6f62616c20696e646578206f7574206f6620626f756e6080527f647300000000000000000000000000000000000000000000000000000000000060a05260608160e001604282825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060c0528060040160dcfd5b60043560095481101561412c57600a015460405260206040f35b632f745c598118610e085760443610341761412c576004358060a01c61412c576101605261016051604052610d35610180612615565b6101805160243510610dde576020806102205260216101a0527f6572633732313a206f776e657220696e646578206f7574206f6620626f756e646101c0527f73000000000000000000000000000000000000000000000000000000000000006101e0526101a08161022001604182825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b6007610160516020525f5260405f20806024356020525f5260405f20905054610180526020610180f35b63d1febfb981186126115760243610341761412c576003600435670de0b6b3a764000081101561412c57026801000000000000000f018054604052600181015460605260028101546080525060606040f35b63f0350c0481186126115760243610341761412c576004358060a01c61412c5761010052610e86612952565b61010051610f2b576020806101a0526026610120527f6f776e61626c653a206e6577206f776e657220697320746865207a65726f2061610140527f646472657373000000000000000000000000000000000000000000000000000061016052610120816101a001604682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610180528060040161019cfd5b61010051604052610f3a6129ce565b005b638da5cb5b8118612611573461412c575f5460405260206040f35b6301ffc9a781186110935760243610341761412c576004358060201b61412c576040526040517f01ffc9a7000000000000000000000000000000000000000000000000000000008118610fab576001611088565b7f80ac58cd000000000000000000000000000000000000000000000000000000008118610fd9576001611088565b7f5b5e139f000000000000000000000000000000000000000000000000000000008118611007576001611088565b7f780e9d63000000000000000000000000000000000000000000000000000000008118611035576001611088565b7f49064906000000000000000000000000000000000000000000000000000000008118611063576001611088565b7fe90fb3f6000000000000000000000000000000000000000000000000000000008118155b905060805260206080f35b63010ae75781186126115760243610341761412c576004358060a01c61412c57604052680129a2241af62c00106040516020525f5260405f205460605260206060f35b635c19a95c811861116a5760243610341761412c576004358060a01c61412c5760405260208060c052600d6060527f4e6f7420737570706f727465640000000000000000000000000000000000000060805260608160c001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060a0528060040160bcfd5b6391ddadf4811861118c573461412c57428060301c61412c5760405260206040f35b638e539e8c81186126115760243610341761412c5760206004356040526111b46101606137e5565b610160f35b63c3cda52081186126115760c43610341761412c576004358060a01c61412c576040526064358060081c61412c5760605260208060e052600d6080527f4e6f7420737570706f727465640000000000000000000000000000000000000060a05260808160e001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060c0528060040160dcfd5b63eff7a61281186126115760243610341761412c575f5c60011461412c5760015f5d6801000000000000000d336020525f5260405f208054610520526001810154610540525060043562093a808104905062093a8081028162093a8082041861412c579050610560526001610520511215611348576020806105e0526011610580527f4e6f7468696e67206973206c6f636b65640000000000000000000000000000006105a052610580816105e001603182825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b4261054051116113ca576020806105e052600c610580527f4c6f636b206578706972656400000000000000000000000000000000000000006105a052610580816105e001602c82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b61054051610560511161144f576020806105e052601f610580527f43616e206f6e6c7920696e637265617365206c6f636b206475726174696f6e006105a052610580816105e001603f82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b42630784ce00810181811061412c5790506105605111156114e2576020806105e052601e610580527f566f74696e67206c6f636b2063616e2062652034207965617273206d617800006105a052610580816105e001603e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b336102e0525f61030052610560516103205260406105206103405e60086103805261150b613027565b5f5f5d005b63b25ec64581186117dd573461412c575f5c60011461412c5760015f5d6801000000000000000d336020525f5260405f20805461052052600181015461054052504261054051116115d3576020806105c052600c610560527f4c6f636b2065787069726564000000000000000000000000000000000000000061058052610560816105c001602c82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105a052806004016105bcfd5b6001610520511215611657576020806105c0526011610560527f4e6f7468696e67206973206c6f636b656400000000000000000000000000000061058052610560816105c001603182825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105a052806004016105bcfd5b5f61056052610540511961178957680129a2241af62c0012546105805261058051156117535761058051635f1a104c6105a052336105c05260206105a060246105bc845afa6116a8573d5f5f3e3d5ffd5b3d602081183d6020100218806105a0016105c01161412c576105a0518060011c61412c576105e052506105e09050516117535760208061066052600b610600527f4e6f7420616c6c6f776564000000000000000000000000000000000000000000610620526106008161066001602b82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610640528060040161065cfd5b42630784ce00810181811061412c57905062093a808104905062093a8081028162093a8082041861412c579050610560526117af565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610560525b336102e0525f61030052610560516103205260406105206103405e6008610380526117d8613027565b5f5f5d005b6323b872dd81186126115760643610341761412c576004358060a01c61412c57610400526024358060a01c61412c576104205233610180526044356101a052611827610440613a97565b610440516118cc576020806104e052602d610460527f6572633732313a2063616c6c6572206973206e6f7420746f6b656e206f776e65610480527f72206f7220617070726f766564000000000000000000000000000000000000006104a052610460816104e001604d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06104c052806004016104dcfd5b610400516044351815611951576020806104a052600e610440527f57726f6e6720746f6b656e20494400000000000000000000000000000000000061046052610440816104a001602e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610480528060040161049cfd5b604061040060405e611964610440613b13565b610440516119e4576020806104c052600f610460527f4e656564206d61782076654c6f636b000000000000000000000000000000000061048052610460816104c001602f82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06104a052806004016104bcfd5b60406104006102e05e6119f5613d5e565b60443561026052611a046136cd565b005b633ccfd60b8118612611573461412c57336102e052611a45565b6351cff8d98118611c7e5760243610341761412c576004358060a01c61412c576102e0525b5f5c60011461412c5760015f5d6801000000000000000d336020525f5260405f208054610300526001810154610320525061032051421015611af9576020806103a0526016610340527f546865206c6f636b206469646e2774206578706972650000000000000000000061036052610340816103a001603682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610380528060040161039cfd5b610300515f811261412c576103405260406103006103605e5f610320525f610300526801000000000000000d336020525f5260405f20610300518155610320516001820155506801000000000000000c546103a0526103a0516103405180820382811161412c57905090506103c0526103c0516801000000000000000c5533604052604061036060605e604061030060a05e611b93612a05565b3361026052611ba06136cd565b60206143c05f395f5163a9059cbb6103e0526102e05161040052610340516104205260206103e060446103fc5f855af1611bdc573d5f5f3e3d5ffd5b3d602081183d6020100218806103e0016104001161412c576103e0518060011c61412c5761044052506104409050511561412c576102e051337ff341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567610340516103e052426104005260406103e0a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c60406103a06103e05e60406103e0a15f5f5d005b634bf5d7e98118612611573461412c57602080608052600e6040527f6d6f64653d74696d657374616d70000000000000000000000000000000000000606052604081608001602e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506080f35b639ab24eb08118611e235760243610341761412c576004358060a01c61412c57604052680129a2241af62c00106040516020525f5260405f2054606052606051611d44575f60805260206080611e21565b680129a2241af62c000f6040516020525f5260405f206003606051670de0b6b3a764000081101561412c5702810190508054608052600181015460a052600281015460c0525060805160a0514260c05180820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c57905090508082038281135f83121861412c57905090506080527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60805113611e0f575f6080525b6080515f811261412c5760e052602060e05bf35b6372e3af5081186126115760243610341761412c576004358060a01c61412c5761010052611e4f612952565b61010051680129a2241af62c0012557fa7fad85ba00d983deab3d92e1bbba81938da68cddbcb5d4f018ef3b1e66534fa61010051610120526020610120a1005b633a46b1a881186126115760443610341761412c576004358060a01c61412c576040525f606052680129a2241af62c00106040516020525f5260405f2054608052680129a2241af62c000f6040516020525f5260405f20600281019050546024351015611f03575f60a052602060a061207a565b5f6080905b8060a0526080516060511015611fac5760605160805180820182811061412c57905090506001810181811061412c5790508060011c905060c052602435680129a2241af62c000f6040516020525f5260405f20600360c051670de0b6b3a764000081101561412c570281019050600281019050541115611f9a5760c0516001810381811161412c579050608052611fa1565b60c0516060525b600101818118611f08575b5050680129a2241af62c000f6040516020525f5260405f206003606051670de0b6b3a764000081101561412c570281019050805460a052600181015460c052600281015460e0525060a05160c05160243560e05180820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c57905090508082038281135f83121861412c579050905060a0525f60a0511215612066575f61010052602061010061207a565b60a0515f811261412c576101005260206101005bf35b630d15fd7781186120a2573461412c5760204260405261209d6101606137e5565b610160f35b63cbf9fe5f81186120ef5760243610341761412c576004358060a01c61412c576040526801000000000000000d6040516020525f5260405f20805460605260018101546080525060406060f35b6328d09d4781186126115760443610341761412c576004358060a01c61412c57604052680129a2241af62c000f6040516020525f5260405f206003602435670de0b6b3a764000081101561412c57028101905080546060526001810154608052600281015460a0525060606060f35b6342842e0e81186126115760643610341761412c575f610a00526121b2565b63b88d4fde81186124935760843610341761412c576064356004018035610400811161412c575060208135018082610a003750505b6004358060a01c61412c576109c0526024358060a01c61412c576109e05233610180526044356101a0526121e7610e40613a97565b610e405161228c57602080610ee052602d610e60527f6572633732313a2063616c6c6572206973206e6f7420746f6b656e206f776e65610e80527f72206f7220617070726f76656400000000000000000000000000000000000000610ea052610e6081610ee001604d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610ec05280600401610edcfd5b6109c051604435181561231157602080610ea052600e610e40527f57726f6e6720746f6b656e204944000000000000000000000000000000000000610e6052610e4081610ea001602e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610e805280600401610e9cfd5b60406109c060405e612324610e40613b13565b610e40516123a457602080610ec052600f610e60527f4e656564206d61782076654c6f636b0000000000000000000000000000000000610e8052610e6081610ec001602f82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610ea05280600401610ebcfd5b60406109c06102e05e6123b5613d5e565b604435610260526123c46136cd565b60406109c060405e6044356080526020610a00510180610a0060a05e506123ec610e40613f9f565b610e405161249157602080610ee0526033610e60527f6572633732313a207472616e7366657220746f206e6f6e2d4945524337323152610e80527f6563656976657220696d706c656d656e74657200000000000000000000000000610ea052610e6081610ee001605382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610ec05280600401610edcfd5b005b63a7afdcae81186126115760243610341761412c576004358060a01c61412c57604052680129a2241af62c00106040516020525f5260405f2054606052680129a2241af62c000f6040516020525f5260405f206003606051670de0b6b3a764000081101561412c570281019050545f811261412c57608052680129a2241af62c000f6040516020525f5260405f206003606051670de0b6b3a764000081101561412c570281019050600181019050545f811261412c5760a05260406080f35b6382bfefc88118612611573461412c5760206143c060403960206040f35b63047fc9aa8118612611573461412c576801000000000000000c5460405260206040f35b63900cf0cf8118612611573461412c576801000000000000000e5460405260206040f35b637119748481186126115760243610341761412c57680129a2241af62c00116004356020525f5260405f205460405260206040f35b63cada36828118612611573461412c57680129a2241af62c00125460405260206040f35b5f5ffd5b6040516126b15760208060e052602d6060527f6572633732313a20746865207a65726f2061646472657373206973206e6f74206080527f612076616c6964206f776e65720000000000000000000000000000000000000060a05260608160e001604d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060c0528060040160dcfd5b60046040516020525f5260405f2054815250565b60056040516020525f5260405f205460605260605161274f5760208060e05260186080527f6572633732313a20696e76616c696420746f6b656e204944000000000000000060a05260808160e001603882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060c0528060040160dcfd5b606051815250565b610140516006610160516020525f5260405f20556101605161014051610160516040526127856101806126c5565b610180517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f6101a0a4565b6060516040511861282f5760208061010052601960a0527f6572633732313a20617070726f766520746f2063616c6c65720000000000000060c05260a08161010001603982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060e0528060040160fcfd5b60805160016040516020525f5260405f20806060516020525f5260405f209050556060516040517f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160805160a052602060a0a3565b60056040516020525f5260405f20541515815250565b6060516040526128aa6080612884565b6080516129245760208061010052601860a0527f6572633732313a20696e76616c696420746f6b656e204944000000000000000060c05260a08161010001603882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060e0528060040160fcfd5b565b6101605160605261293561289a565b6006610160516020525f5260405f2054815250565b600954815250565b5f543318156129cc5760208060a05260206040527f6f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260605260408160a001604082825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060805280600401609cfd5b565b5f546060526040515f556040516060517f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f6080a3565b6101003660e0376801000000000000000e546101e05260405115612b8c574260805111612a32575f612a3a565b600160605112155b15612aab5760805119612a57575f6101005260605160e052612aab565b606051630784ce008105905061010052610100516080514280820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c579050905060e0525b4260c05111612aba575f612ac2565b600160a05112155b15612b355760c05119612ae0575f6101605260a05161014052612b35565b60a051630784ce0081059050610160526101605160c0514280820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c5790509050610140525b680129a2241af62c00116080516020525f5260405f20546101a05260c05115612b8c5760805160c05118612b70576101a0516101c052612b8c565b680129a2241af62c001160c0516020525f5260405f20546101c0525b6040366102003742610240526101e05115612bdd5760036101e051670de0b6b3a764000081101561412c57026801000000000000000f01805461020052600181015461022052600281015461024052505b61024051610260526102605162093a808104905062093a8081028162093a8082041861412c579050610280525f60ff905b806102a0526102805162093a80810181811061412c579050610280525f6102c052426102805111612c5a57680129a2241af62c0011610280516020525f5260405f20546102c052612c60565b42610280525b6102005161022051610280516102605180820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c57905090508082038281135f83121861412c579050905061020052610220516102c0518082018281125f83121861412c5790509050610220527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102005113612d0c575f610200525b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102205113612d3c575f610220525b610280516102605261028051610240526101e0516001810181811061412c5790506101e05242610280511815612db25760036101e051670de0b6b3a764000081101561412c57026801000000000000000f0161020051815561022051600182015561024051600282015550600101818118612c0e575b50506101e0516801000000000000000e5560405115612e92576102205161016051610100518082038281135f83121861412c57905090508082018281125f83121861412c579050905061022052610200516101405160e0518082038281135f83121861412c57905090508082018281125f83121861412c5790509050610200527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102205113612e62575f610220525b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102005113612e92575f610200525b60036101e051670de0b6b3a764000081101561412c57026801000000000000000f01610200518155610220516001820155610240516002820155506040511561302557426080511115612f43576101a051610100518082018281125f83121861412c57905090506101a05260805160c05118612f27576101a051610160518082038281135f83121861412c57905090506101a0525b6101a051680129a2241af62c00116080516020525f5260405f20555b4260c0511115612f925760805160c05114612f92576101c051610160518082038281135f83121861412c57905090506101c0526101c051680129a2241af62c001160c0516020525f5260405f20555b680129a2241af62c00106040516020525f5260405f20546001810181811061412c5790506102a0526102a051680129a2241af62c00106040516020525f5260405f20554261018052680129a2241af62c000f6040516020525f5260405f2060036102a051670de0b6b3a764000081101561412c570281019050610140518155610160516001820155610180516002820155505b565b60406103406103a05e6801000000000000000c546103e0526103e0516103005180820182811061412c5790509050630784ce0081049050630784ce00810281630784ce0082041861412c57905061040052610400516103e05180820382811161412c579050905061042052610400516801000000000000000c5560406103a06104405e6103a051610420518060ff1c61412c578082018281125f83121861412c57905090506103a05261032051156130e257610320516103c0525b6801000000000000000d6102e0516020525f5260405f206103a05181556103c0516001820155506102e051604052604061044060605e60406103a060a05e613128612a05565b61042051156131a45760206143c05f395f516323b872dd61048052336104a052306104c052610420516104e0526020610480606461049c5f855af161316f573d5f5f3e3d5ffd5b3d602081183d602010021880610480016104a01161412c57610480518060011c61412c5761050052506105009050511561412c575b6103c0516102e051337f5f971bd00bf3ffbca8a6d72cdd4fd92cfd4f62636161921d1e5a64f0b64ccb6d6104205161048052610380516104a052426104c0526060610480a47f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c60406103e06104805e6040610480a1565b600954680100000000000000096040516020525f5260405f205560095467fffffffffffffffe811161412c5760405181600a01556001810160095550565b6101605160405261326b6101c0612615565b6101c0516001810381811161412c5790506101a0526008610180516020525f5260405f20546101c0526101a0516101c051146132fe576007610160516020525f5260405f20806101a0516020525f5260405f209050546101e0526101e0516007610160516020525f5260405f20806101c0516020525f5260405f209050556101c05160086101e0516020525f5260405f20555b5f6008610180516020525f5260405f20555f6007610160516020525f5260405f20806101a0516020525f5260405f20905055565b6009546001810381811161412c579050606052680100000000000000096040516020525f5260405f205460805260605160095481101561412c57600a015460a05260a05160805160095481101561412c57600a01556080516801000000000000000960a0516020525f5260405f20555f680100000000000000096040516020525f5260405f20556001600954801561412c57038060095550565b610160516040526133de6101c0612615565b6101c0516101a052610180516007610160516020525f5260405f20806101a0516020525f5260405f209050556101a0516008610180516020525f5260405f2055565b61020051613437576102405160405261345c61321b565b61022051610200511461345c576102005161016052610240516101805261345c613259565b610220516134735761024051604052613491613332565b6102005161022051146134915760406102206101605e6134916133cc565b565b565b61026051613515576020806103005260206102a0527f6572633732313a206d696e7420746f20746865207a65726f20616464726573736102c0526102a08161030001604082825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06102e052806004016102fcfd5b610280516040526135276102a0612884565b6102a051156135a85760208061032052601c6102c0527f6572633732313a20746f6b656e20616c7265616479206d696e746564000000006102e0526102c08161032001603c82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610300528060040161031cfd5b5f6102005260406102606102205e6135be613420565b610280516040526135d06102a0612884565b6102a051156136515760208061032052601c6102c0527f6572633732313a20746f6b656e20616c7265616479206d696e746564000000006102e0526102c08161032001603c82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610300528060040161031cfd5b60016004610260516020525f5260405f2054016004610260516020525f5260405f2055610260516005610280516020525f5260405f205561028051610260515f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f6102a0a45f604052604061026060605e6136cb613493565b565b610260516040526136df6102a06126c5565b6102a0516102805261028051610200525f610220526102605161024052613704613420565b610260516040526137166102a06126c5565b6102a051610280525f6006610260516020525f5260405f205560016004610280516020525f5260405f2054036004610280516020525f5260405f20555f6005610260516020525f5260405f2055610260515f610280517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f6102a0a4610280516040525f606052610260516080526137ac613493565b6801000000000000000a610260516020525f5260405f2054156137e3575f6801000000000000000a610260516020525f5260405f20555b565b6801000000000000000e54606052606051613803575f815250613a95565b68010000000000000011546040511015613820575f815250613a95565b5f6080525f6080905b8060a05260605160805110156138be5760805160605180820182811061412c57905090506001810181811061412c5790508060011c905060c052604051600360c051670de0b6b3a764000081101561412c57026801000000000000000f016002810190505411156138ac5760c0516001810381811161412c5790506060526138b3565b60c0516080525b600101818118613829575b50506003608051670de0b6b3a764000081101561412c57026801000000000000000f01805460a052600181015460c052600281015460e0525060605160805118613a2d5760e051610100525f60ff905b80610120526101005162093a80810181811061412c579050610100525f61014052604051610100511161395c57680129a2241af62c0011610100516020525f5260405f205461014052613964565b604051610100525b60a05160c0516101005160e05180820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c57905090508082038281135f83121861412c579050905060a0526040516101005118156139f95760c051610140518082018281125f83121861412c579050905060c0526101005160e05260010181811861390e575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60a05113613a87575f60a052613a87565b60a05160c05160405160e05180820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c57905090508082038281135f83121861412c579050905060a0525b60a0515f811261412c578152505b565b6101a051604052613aa96101e06126c5565b6101e0516101c0526101c0516101805118613ac5576001613b0e565b60016101c0516020525f5260405f2080610180516020525f5260405f20905054613b0b57610180516101a05161016052613b006101e0612926565b6101e0511815613b0e565b60015b815250565b680129a2241af62c00125460805260805115613bf757608051635f1a104c60a05260405160c052602060a0602460bc845afa613b51573d5f5f3e3d5ffd5b3d602081183d60201002188060a00160c01161412c5760a0518060011c61412c5760e0525060e0905051613bf75760208061016052600b610100527f4e6f7420616c6c6f776564000000000000000000000000000000000000000000610120526101008161016001602b82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610140528060040161015cfd5b6060516040511461412c5760403660a03742630784ce00810181811061412c57905062093a808104905062093a8081028162093a8082041861412c57905060e0526801000000000000000d6040516020525f5260405f2060018101905054610100526101005119613c69576001613c8f565b60e0516101005162093a808104905062093a8081028162093a8082041861412c57905018155b15613c9a57600160a0525b6801000000000000000d6060516020525f5260405f2060018101905054610120526101205119613ccb576001613cf1565b60e0516101205162093a808104905062093a8081028162093a8082041861412c57905018155b15613cfc57600160c0525b60a051613d09575f613d59565b60c051613d16575f613d59565b6101205162093a808104905062093a8081028162093a8082041861412c5790506101005162093a808104905062093a8081028162093a8082041861412c57905018155b815250565b6801000000000000000d6102e0516020525f5260405f20805461032052600181015461034052506801000000000000000d6102e0516020525f5260405f205f81555f6001820155506801000000000000000d610300516020525f5260405f208054610360526001810154610380525061036051610320518082018281125f83121861412c579050905061036052610360516801000000000000000d610300516020525f5260405f2055680129a2241af62c00106102e0516020525f5260405f20546001810181811061412c5790506103a0526103a051680129a2241af62c00106102e0516020525f5260405f2055680129a2241af62c000f6102e0516020525f5260405f2060036103a051670de0b6b3a764000081101561412c5702810190505f81555f600182015542600282015550680129a2241af62c0010610300516020525f5260405f20546001810181811061412c5790506103a0526103a051680129a2241af62c0010610300516020525f5260405f20556040366103c0376103805119613ef057610360516103e052613f47565b61036051630784ce00810590506103c0526103c051610380514280820382811161412c57905090508060ff1c61412c5780820281191515600160ff1b841415178215848484051417161561412c57905090506103e0525b680129a2241af62c000f610300516020525f5260405f2060036103a051670de0b6b3a764000081101561412c5702810190506103e05181556103c05160018201554260028201555060a036604037613f9d612a05565b565b6060513b156141245760605163150b7a026104e05260803361050052604051610520526080516105405280610560528061050001602060a051018060a0835e508051806020830101601f825f03163682375050601f19601f8251602001011690508101505060206104e06104a46104fc5f855af161401f573d5f5f3e3d5ffd5b3d602081183d6020100218806104e0016105001161412c576104e0518060201b61412c576109a052506109a09050516104c0527f150b7a02000000000000000000000000000000000000000000000000000000006104c051181561411a576020806105605260336104e0527f6572633732313a207472616e7366657220746f206e6f6e2d4945524337323152610500527f6563656976657220696d706c656d656e74657200000000000000000000000000610520526104e08161056001605382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610540528060040161055cfd5b600181525061412a565b60018152505b565b5f80fd25520018257026110e5a098f217d0c261e8f0cff125b261106422611009e26111cf32611261100f42611070c261125942611076426111a0610d615102611207c11b91a2025ed0f3c25b805f9215e0f57a0532e09ee38adcd4089b204812691c51a32b3eb375436a86ae088d9b036f8b300000000000000000000000000000000000000000000000000000000000000010000000000000000000000008235c179e9e84688fbd8b12295efc26834dac211000000000000000000000000000000000000000000000000000000000000000b4a75737420736179206e6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009b29f4eab2afb955a939e5bb4e080257fdc8071103f99888124cf4197dffda0d0000000000000000000000000000000000000000000000000000000000000009746f2045495037313200000000000000000000000000000000000000000000004ca7bd70c5e2b6c43a1f5331172941165e5965688189aa73516af0c22165f1100000000000000000000000000000000000000000000000000000000000000019566f74696e67457363726f773a205969656c642042617369730000000000000000000000000000000000000000000000000000000000000000000000000000047665594200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001791f726b4103694969820be083196cc7c045ff
Loading...
Loading
Loading...
Loading
OVERVIEW
Yield protocol on top of Curve Finance.Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 100.00% | $0.488718 | 17,190,009.9872 | $8,401,067.3 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.