You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
what command you ran
vyper contracts/dao/veANGLE.vy --storage-layout-file storage.json
the code that caused the failure (see this link for help with formatting code)
# @version 0.3.10"""@title Voting Escrow@author Angle Protocol@license MIT@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)."""# Original idea and credit:# Curve Finance's veCRV# https://github.com/curvefi/curve-dao-contracts/blob/master/contracts/VotingEscrow.vy# veANGLE is a fork with only one view functions added to it to make veANGLE compatible# with Compound governance system. The references to the controller have also been removed# 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?)structPoint:
bias: int128slope: int128# - dweight / dtts: uint256blk: uint256# block# We cannot really do block numbers per se b/c slope is per time, not per block# and per block could be fairly bad b/c Ethereum changes blocktimes.# What we can do is to extrapolate ***At functionsstructLockedBalance:
amount: int128end: uint256interfaceERC20:
defdecimals() ->uint256: viewdefname() ->String[64]: viewdefsymbol() ->String[32]: viewdeftransfer(to: address, amount: uint256) ->bool: nonpayabledeftransferFrom(spender: address, to: address, amount: uint256) ->bool: nonpayable# Interface for checking whether address belongs to a whitelisted# type of a smart wallet.# When new types are added - the whole contract is changed# The check() method is modifying to be able to use caching# for individual wallet addressesinterfaceSmartWalletChecker:
defcheck(addr: address) ->bool: nonpayableDEPOSIT_FOR_TYPE: constant(int128) =0CREATE_LOCK_TYPE: constant(int128) =1INCREASE_LOCK_AMOUNT: constant(int128) =2INCREASE_UNLOCK_TIME: constant(int128) =3eventCommitOwnership:
admin: addresseventApplyOwnership:
admin: addresseventDeposit:
provider: indexed(address)
value: uint256locktime: indexed(uint256)
type: int128ts: uint256eventWithdraw:
provider: indexed(address)
value: uint256ts: uint256eventSupply:
prevSupply: uint256supply: uint256WEEK: constant(uint256) =7*86400# all future times are rounded by weekMAXTIME: constant(uint256) =4*365*86400# 4 yearsMULTIPLIER: constant(uint256) =10**18token: public(address)
supply: public(uint256)
locked: public(HashMap[address, LockedBalance])
epoch: public(uint256)
point_history: public(Point[100000000000000000000000000000]) # epoch -> unsigned pointuser_point_history: public(HashMap[address, Point[1000000000]]) # user -> Point[user_epoch]user_point_epoch: public(HashMap[address, uint256])
slope_changes: public(HashMap[uint256, int128]) # time -> signed slope changename: public(String[64])
symbol: public(String[32])
decimals: public(uint256)
# Checker for whitelisted (smart contract) wallets which are allowed to deposit# The goal is to prevent tokenizing the escrowfuture_smart_wallet_checker: public(address)
smart_wallet_checker: public(address)
admin: public(address) # Can and will be a smart contractfuture_admin: public(address)
initialized: public(bool)
emergency_withdrawal: public(bool)
@externaldefinitialize(_admin: address, token_addr: address, _smart_wallet_checker: address, _name: String[64], _symbol: String[32]):
""" @notice Contract initializer @param _admin Future veANGLE admin @param token_addr `ERC20ANGLE` token address @param _smart_wallet_checker Future smart wallet checker contract @param _name Token name @param _symbol Token symbol """assertself.initialized==False#dev: contract is already initializedself.initialized=Trueassert_admin!=ZERO_ADDRESS#dev: admin cannot be the 0 addressself.admin=_adminself.token=token_addrself.smart_wallet_checker=_smart_wallet_checkerself.point_history[0].blk=block.numberself.point_history[0].ts=block.timestamp_decimals: uint256=ERC20(token_addr).decimals()
assert_decimals<=255self.decimals=_decimalsself.name=_nameself.symbol=_symbol@externaldefcommit_transfer_ownership(addr: address):
""" @notice Transfer ownership of VotingEscrow contract to `addr` @param addr Address to have ownership transferred to """assertmsg.sender==self.admin# dev: admin onlyassertaddr!=ZERO_ADDRESS# dev: future admin cannot be the 0 addressself.future_admin=addrlogCommitOwnership(addr)
@externaldefaccept_transfer_ownership():
""" @notice Accept a pending ownership transfer """_admin: address=self.future_adminassertmsg.sender==_admin# dev: future admin onlyself.admin=_adminlogApplyOwnership(_admin)
@externaldefapply_transfer_ownership():
""" @notice Apply ownership transfer """assertmsg.sender==self.admin# dev: admin only_admin: address=self.future_adminassert_admin!=ZERO_ADDRESS# dev: admin not setself.admin=_adminlogApplyOwnership(_admin)
@externaldefset_emergency_withdrawal():
assertmsg.sender==self.adminself.emergency_withdrawal=True@external@nonreentrant('lock')defwithdraw_fast():
""" @notice withdraw all tokens when in emergency states """assertself.emergency_withdrawal, "Emergency withdrawal not enabled"_locked: LockedBalance=self.locked[msg.sender]
value: uint256=convert(_locked.amount, uint256)
# remove lockself.locked[msg.sender].end=0self.locked[msg.sender].amount=0assertERC20(self.token).transfer(msg.sender, value)
logWithdraw(msg.sender, value, block.timestamp)
@externaldefcommit_smart_wallet_checker(addr: address):
""" @notice Set an external contract to check for approved smart contract wallets @param addr Address of Smart contract checker """assertmsg.sender==self.adminself.future_smart_wallet_checker=addr@externaldefapply_smart_wallet_checker():
""" @notice Apply setting external contract to check approved smart contract wallets """assertmsg.sender==self.adminself.smart_wallet_checker=self.future_smart_wallet_checker@internaldefassert_not_contract(addr: address):
""" @notice Check if the call is from a whitelisted smart contract, revert if not @param addr Address to be checked """ifaddr!=tx.origin:
checker: address=self.smart_wallet_checkerifchecker!=ZERO_ADDRESS:
ifSmartWalletChecker(checker).check(addr):
returnraise"Smart contract depositors not allowed"@external@viewdefget_last_user_slope(addr: address) ->int128:
""" @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]
returnself.user_point_history[addr][uepoch].slope@external@viewdefuser_point_history__ts(_addr: address, _idx: uint256) ->uint256:
""" @notice Get the timestamp for checkpoint `_idx` for `_addr` @param _addr User wallet address @param _idx User epoch number @return Epoch time of the checkpoint """returnself.user_point_history[_addr][_idx].ts@external@viewdeflocked__end(_addr: address) ->uint256:
""" @notice Get timestamp when `_addr`'s lock finishes @param _addr User wallet @return Epoch time of the lock end """returnself.locked[_addr].end@internaldef_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: int128=0new_dslope: int128=0_epoch: uint256=self.epochifaddr!=ZERO_ADDRESS:
# Calculate slopes and biases# Kept at zero when they have toifold_locked.end>block.timestampandold_locked.amount>0:
u_old.slope=old_locked.amount/convert(MAXTIME, int128)
u_old.bias=u_old.slope*convert(old_locked.end-block.timestamp, int128)
ifnew_locked.end>block.timestampandnew_locked.amount>0:
u_new.slope=new_locked.amount/convert(MAXTIME, int128)
u_new.bias=u_new.slope*convert(new_locked.end-block.timestamp, int128)
# 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 zerosold_dslope=self.slope_changes[old_locked.end]
ifnew_locked.end!=0:
ifnew_locked.end==old_locked.end:
new_dslope=old_dslopeelse:
new_dslope=self.slope_changes[new_locked.end]
last_point: Point=Point({bias: 0, slope: 0, ts: block.timestamp, blk: block.number})
if_epoch>0:
last_point=self.point_history[_epoch]
last_checkpoint: uint256=last_point.ts# initial_last_point is used for extrapolation to calculate block number# (approximately, for *At methods) and save them# as we cannot figure that out exactly from inside the contractinitial_last_point: Point=last_pointblock_slope: uint256=0# dblock/dtifblock.timestamp>last_point.ts:
block_slope=MULTIPLIER* (block.number-last_point.blk) / (block.timestamp-last_point.ts)
# If last point is already recorded in this block, slope=0# But that's ok b/c we know the block in such case# Go over weeks to fill history and calculate what the current point ist_i: uint256= (last_checkpoint/WEEK) *WEEKforiinrange(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 brokent_i+=WEEKd_slope: int128=0ift_i>block.timestamp:
t_i=block.timestampelse:
d_slope=self.slope_changes[t_i]
last_point.bias-=last_point.slope*convert(t_i-last_checkpoint, int128)
last_point.slope+=d_slopeiflast_point.bias<0: # This can happenlast_point.bias=0iflast_point.slope<0: # This cannot happen - just in caselast_point.slope=0last_checkpoint=t_ilast_point.ts=t_ilast_point.blk=initial_last_point.blk+block_slope* (t_i-initial_last_point.ts) /MULTIPLIER_epoch+=1ift_i==block.timestamp:
last_point.blk=block.numberbreakelse:
self.point_history[_epoch] =last_pointself.epoch=_epoch# Now point_history is filled until t=nowifaddr!=ZERO_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)
iflast_point.slope<0:
last_point.slope=0iflast_point.bias<0:
last_point.bias=0# Record the changed point into historyself.point_history[_epoch] =last_pointifaddr!=ZERO_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]ifold_locked.end>block.timestamp:
# old_dslope was <something> - u_old.slope, so we cancel thatold_dslope+=u_old.slopeifnew_locked.end==old_locked.end:
old_dslope-=u_new.slope# It was a new deposit, not extensionself.slope_changes[old_locked.end] =old_dslopeifnew_locked.end>block.timestamp:
ifnew_locked.end>old_locked.end:
new_dslope-=u_new.slope# old slope disappeared at this pointself.slope_changes[new_locked.end] =new_dslope# else: we recorded it already in old_dslope# Now handle user historyuser_epoch: uint256=self.user_point_epoch[addr] +1self.user_point_epoch[addr] =user_epochu_new.ts=block.timestampu_new.blk=block.numberself.user_point_history[addr][user_epoch] =u_new@internaldef_deposit_for(_addr: address, _value: uint256, unlock_time: uint256, locked_balance: LockedBalance, type: int128, sender: address):
""" @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_balancesupply_before: uint256=self.supplyself.supply=supply_before+_valueold_locked: LockedBalance=_locked# Adding to existing lock, or if a lock is expired - creating a new one_locked.amount+=convert(_value, int128)
ifunlock_time!=0:
_locked.end=unlock_timeself.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_value!=0:
assertERC20(self.token).transferFrom(sender, self, _value)
logDeposit(_addr, _value, _locked.end, type, block.timestamp)
logSupply(supply_before, supply_before+_value)
@externaldefcheckpoint():
""" @notice Record global data to checkpoint """assertnotself.emergency_withdrawal, "Emergency withdrawal enabled"self._checkpoint(ZERO_ADDRESS, empty(LockedBalance), empty(LockedBalance))
@external@nonreentrant('lock')defdeposit_for(_addr: address, _value: uint256):
""" @notice Deposit `_value` tokens for `_addr` and add to the lock @dev Anyone (even a smart contract) can deposit for someone else, but cannot extend their locktime and deposit for a brand new user @param _addr User's wallet address @param _value Amount to add to user's lock """assertnotself.emergency_withdrawal, "Emergency withdrawal enabled"_locked: LockedBalance=self.locked[_addr]
assert_value>0# dev: need non-zero valueassert_locked.amount>0, "No existing lock found"assert_locked.end>block.timestamp, "Cannot add to expired lock. Withdraw"self._deposit_for(_addr, _value, 0, self.locked[_addr], DEPOSIT_FOR_TYPE, msg.sender)
@external@nonreentrant('lock')defcreate_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 """assertnotself.emergency_withdrawal, "Emergency withdrawal enabled"self.assert_not_contract(msg.sender)
unlock_time: uint256= (_unlock_time/WEEK) *WEEK# Locktime is rounded down to weeks_locked: LockedBalance=self.locked[msg.sender]
assert_value>0# dev: need non-zero valueassert_locked.amount==0, "Withdraw old tokens first"assertunlock_time>block.timestamp, "Can only lock until time in the future"assertunlock_time<=block.timestamp+MAXTIME, "Voting lock can be 4 years max"self._deposit_for(msg.sender, _value, unlock_time, _locked, CREATE_LOCK_TYPE, msg.sender)
@external@nonreentrant('lock')defincrease_amount(_value: uint256):
""" @notice Deposit `_value` additional tokens for `msg.sender` without modifying the unlock time @param _value Amount of tokens to deposit and add to the lock """assertnotself.emergency_withdrawal, "Emergency withdrawal enabled"self.assert_not_contract(msg.sender)
_locked: LockedBalance=self.locked[msg.sender]
assert_value>0# dev: need non-zero valueassert_locked.amount>0, "No existing lock found"assert_locked.end>block.timestamp, "Cannot add to expired lock. Withdraw"self._deposit_for(msg.sender, _value, 0, _locked, INCREASE_LOCK_AMOUNT, msg.sender)
@external@nonreentrant('lock')defincrease_unlock_time(_unlock_time: uint256):
""" @notice Extend the unlock time for `msg.sender` to `_unlock_time` @param _unlock_time New epoch time for unlocking """assertnotself.emergency_withdrawal, "Emergency withdrawal enabled"self.assert_not_contract(msg.sender)
_locked: LockedBalance=self.locked[msg.sender]
unlock_time: uint256= (_unlock_time/WEEK) *WEEK# Locktime is rounded down to weeksassert_locked.end>block.timestamp, "Lock expired"assert_locked.amount>0, "Nothing is locked"assertunlock_time>_locked.end, "Can only increase lock duration"assertunlock_time<=block.timestamp+MAXTIME, "Voting lock can be 4 years max"self._deposit_for(msg.sender, 0, unlock_time, _locked, INCREASE_UNLOCK_TIME, msg.sender)
@external@nonreentrant('lock')defwithdraw():
""" @notice Withdraw all tokens for `msg.sender` @dev Only possible if the lock has expired """assertnotself.emergency_withdrawal, "Emergency withdrawal enabled"_locked: LockedBalance=self.locked[msg.sender]
assertblock.timestamp>=_locked.end, "The lock didn't expire"value: uint256=convert(_locked.amount, uint256)
old_locked: LockedBalance=_locked_locked.end=0_locked.amount=0self.locked[msg.sender] =_lockedsupply_before: uint256=self.supplyself.supply=supply_before-value# old_locked can have either expired <= timestamp or zero end# _locked has only 0 end# Both can have >= 0 amountself._checkpoint(msg.sender, old_locked, _locked)
assertERC20(self.token).transfer(msg.sender, value)
logWithdraw(msg.sender, value, block.timestamp)
logSupply(supply_before, supply_before-value)
# The following ERC20/minime-compatible methods are not real balanceOf and supply!# They measure the weights for the purpose of voting, so they don't represent# real coins.@internal@viewdeffind_block_epoch(_block: uint256, max_epoch: uint256) ->uint256:
""" @notice Binary search to estimate timestamp for block number @param _block Block to find @param max_epoch Don't go beyond this epoch @return Approximate timestamp for block """# Binary search_min: uint256=0_max: uint256=max_epochforiinrange(128): # Will be always enough for 128-bit numbersif_min>=_max:
break_mid: uint256= (_min+_max+1) /2ifself.point_history[_mid].blk<=_block:
_min=_midelse:
_max=_mid-1return_min@internal@viewdef_find_user_timestamp_epoch(addr: address, ts: uint256) ->uint256:
""" @notice Find the epoch for a user's timestamp @param addr User wallet address @param ts Epoch time to find @return User epoch number """minimum_value: uint256=0maximum_value: uint256=self.user_point_epoch[addr]
foriinrange(128): # Will be always enough for 128-bit numbersifminimum_value>=maximum_value:
breakmid: uint256= (minimum_value+maximum_value+1) /2ifself.user_point_history[addr][mid].ts<=ts:
minimum_value=midelse:
maximum_value=mid-1returnminimum_value@external@viewdeffind_user_timestamp_epoch(addr: address, ts: uint256) ->uint256:
""" @notice Find the epoch for a user's timestamp @param addr User wallet address @param ts Epoch time to find @return User epoch number """returnself._find_user_timestamp_epoch(addr, ts)
@external@viewdefbalanceOf(addr: address, ts: uint256=block.timestamp) ->uint256:
""" @notice Get the current voting power for `msg.sender` @dev Adheres to the ERC20 `balanceOf` interface for Aragon compatibility @param addr User wallet address @param ts Epoch time to return voting power at @return User voting power """_epoch: uint256=self._find_user_timestamp_epoch(addr, ts)
if_epoch==0:
return0else:
last_point: Point=self.user_point_history[addr][_epoch]
last_point.bias-=last_point.slope*convert(ts-last_point.ts, int128)
iflast_point.bias<0:
last_point.bias=0returnconvert(last_point.bias, uint256)
@internal@viewdef_balanceOfAt(addr: address, _block: uint256) ->uint256:
""" @notice measure voting power of `addr` at block height `_block` @param addr User's wallet address @param _block Block to calculate the voting power at @return Voting power """# Copying and pasting totalSupply code because Vyper cannot pass by# reference yetassert_block<=block.number# Binary search_min: uint256=0_max: uint256=self.user_point_epoch[addr]
foriinrange(128): # Will be always enough for 128-bit numbersif_min>=_max:
break_mid: uint256= (_min+_max+1) /2ifself.user_point_history[addr][_mid].blk<=_block:
_min=_midelse:
_max=_mid-1upoint: Point=self.user_point_history[addr][_min]
max_epoch: uint256=self.epoch_epoch: uint256=self.find_block_epoch(_block, max_epoch)
point_0: Point=self.point_history[_epoch]
d_block: uint256=0d_t: uint256=0if_epoch<max_epoch:
point_1: Point=self.point_history[_epoch+1]
d_block=point_1.blk-point_0.blkd_t=point_1.ts-point_0.tselse:
d_block=block.number-point_0.blkd_t=block.timestamp-point_0.tsblock_time: uint256=point_0.tsifd_block!=0:
block_time+=d_t* (_block-point_0.blk) /d_blockupoint.bias-=upoint.slope*convert(block_time-upoint.ts, int128)
ifupoint.bias>=0:
returnconvert(upoint.bias, uint256)
else:
return0@external@viewdefbalanceOfAt(addr: address, _block: uint256) ->uint256:
""" @notice Measure voting power of `addr` at block height `_block` @dev Adheres to MiniMe `balanceOfAt` interface: https://github.com/Giveth/minime @param addr User's wallet address @param _block Block to calculate the voting power at @return Voting power """returnself._balanceOfAt(addr,_block)
@external@viewdefgetPastVotes(addr: address, _block: uint256) ->uint256:
""" @notice Measure voting power of `addr` at block height `_block` @dev Adheres to ERC20Votes `getPastVotes` interface: @openzeppelin-contracts-upgradeable/blob/master/contracts/token/ERC20/extensions/ERC20VotesCompUpgradeable.sol @param addr User's wallet address @param _block Block to calculate the voting power at @return Voting power """returnself._balanceOfAt(addr,_block)
@internal@viewdefsupply_at(point: Point, t: uint256) ->uint256:
""" @notice Calculate total voting power at some point in the past @param point The point (bias/slope) to start search from @param t Time to calculate the total voting power at @return Total voting power at that time """last_point: Point=pointt_i: uint256= (last_point.ts/WEEK) *WEEKforiinrange(255):
t_i+=WEEKd_slope: int128=0ift_i>t:
t_i=telse:
d_slope=self.slope_changes[t_i]
last_point.bias-=last_point.slope*convert(t_i-last_point.ts, int128)
ift_i==t:
breaklast_point.slope+=d_slopelast_point.ts=t_iiflast_point.bias<0:
last_point.bias=0returnconvert(last_point.bias, uint256)
@external@viewdeftotalSupply(t: uint256=block.timestamp) ->uint256:
""" @notice Calculate total voting power @dev Adheres to the ERC20 `totalSupply` interface for Aragon compatibility @return Total voting power """_epoch: uint256=self.epochlast_point: Point=self.point_history[_epoch]
returnself.supply_at(last_point, t)
@external@viewdeftotalSupplyAt(_block: uint256) ->uint256:
""" @notice Calculate total voting power at some point in the past @param _block Block to calculate the total voting power at @return Total voting power at `_block` """assert_block<=block.number_epoch: uint256=self.epochtarget_epoch: uint256=self.find_block_epoch(_block, _epoch)
point: Point=self.point_history[target_epoch]
dt: uint256=0iftarget_epoch<_epoch:
point_next: Point=self.point_history[target_epoch+1]
ifpoint.blk!=point_next.blk:
dt= (_block-point.blk) * (point_next.ts-point.ts) / (point_next.blk-point.blk)
else:
ifpoint.blk!=block.number:
dt= (_block-point.blk) * (block.timestamp-point.ts) / (block.number-point.blk)
# Now dt contains info on how far are we beyond pointreturnself.supply_at(point, point.ts+dt)
The overrides don't use intervals for tracking allocations (as opposed to eg SimpleAllocator - this is bc we don't have the guarantee that the overridden addresses will only grow). Instead, we do O(n) allocations where n is determined by the size of storage variables. Also, we do an O(n) traversal which causes the long comp. times.
Version Information
vyper --version
): 0.3.10python --version
): 3.12.3What's your issue about?
When I am compiling my contract with a storage layout override it justs kill the process after some time without any more information
Please include information like:
[1] 229387 killed vyper contracts/dao/veANGLE.vy --storage-layout-file storage.json
what command you ran
vyper contracts/dao/veANGLE.vy --storage-layout-file storage.json
the code that caused the failure (see this link for help with formatting code)
--verbose
flag turned onIt have the same output
How can it be fixed?
I have no idea on how it can be fixed
The text was updated successfully, but these errors were encountered: