vyperlang / vyper

Pythonic Smart Contract Language for the EVM
https://vyperlang.org
Other
4.84k stars 789 forks source link

Process gets killed when using storage layout override #3966

Open 0xtekgrinder opened 4 months ago

0xtekgrinder commented 4 months ago

Version Information

What'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

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?)

struct Point: bias: int128 slope: int128 # - dweight / dt ts: uint256 blk: 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 functions

struct LockedBalance: amount: int128 end: uint256

interface ERC20: def decimals() -> uint256: view def name() -> String[64]: view def symbol() -> String[32]: view def transfer(to: address, amount: uint256) -> bool: nonpayable def transferFrom(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 addresses

interface SmartWalletChecker: def check(addr: address) -> bool: nonpayable

DEPOSIT_FOR_TYPE: constant(int128) = 0 CREATE_LOCK_TYPE: constant(int128) = 1 INCREASE_LOCK_AMOUNT: constant(int128) = 2 INCREASE_UNLOCK_TIME: constant(int128) = 3

event CommitOwnership: admin: address

event ApplyOwnership: admin: address

event Deposit: provider: indexed(address) value: uint256 locktime: indexed(uint256) type: int128 ts: uint256

event Withdraw: provider: indexed(address) value: uint256 ts: uint256

event Supply: prevSupply: uint256 supply: uint256

WEEK: constant(uint256) = 7 86400 # all future times are rounded by week MAXTIME: constant(uint256) = 4 365 * 86400 # 4 years MULTIPLIER: constant(uint256) = 10 ** 18

token: public(address) supply: public(uint256)

locked: public(HashMap[address, LockedBalance])

epoch: public(uint256) point_history: public(Point[100000000000000000000000000000]) # epoch -> unsigned point user_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 change

name: 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 escrow

future_smart_wallet_checker: public(address) smart_wallet_checker: public(address)

admin: public(address) # Can and will be a smart contract future_admin: public(address)

initialized: public(bool)

emergency_withdrawal: public(bool)

@external def initialize(_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 """ assert self.initialized == False #dev: contract is already initialized self.initialized = True assert _admin!= ZERO_ADDRESS #dev: admin cannot be the 0 address self.admin = _admin self.token = token_addr self.smart_wallet_checker = _smart_wallet_checker self.point_history[0].blk = block.number self.point_history[0].ts = block.timestamp

_decimals: uint256 = ERC20(token_addr).decimals()
assert _decimals <= 255
self.decimals = _decimals

self.name = _name
self.symbol = _symbol    

@external def commit_transfer_ownership(addr: address): """ @notice Transfer ownership of VotingEscrow contract to addr @param addr Address to have ownership transferred to """ assert msg.sender == self.admin # dev: admin only assert addr != ZERO_ADDRESS # dev: future admin cannot be the 0 address self.future_admin = addr log CommitOwnership(addr)

@external def accept_transfer_ownership(): """ @notice Accept a pending ownership transfer """ _admin: address = self.future_admin assert msg.sender == _admin # dev: future admin only

self.admin = _admin
log ApplyOwnership(_admin)

@external def apply_transfer_ownership(): """ @notice Apply ownership transfer """ assert msg.sender == self.admin # dev: admin only _admin: address = self.future_admin assert _admin != ZERO_ADDRESS # dev: admin not set self.admin = _admin log ApplyOwnership(_admin)

@external def set_emergency_withdrawal(): assert msg.sender == self.admin self.emergency_withdrawal = True

@external @nonreentrant('lock') def withdraw_fast(): """ @notice withdraw all tokens when in emergency states """ assert self.emergency_withdrawal, "Emergency withdrawal not enabled"

_locked: LockedBalance = self.locked[msg.sender]
value: uint256 = convert(_locked.amount, uint256)

# remove lock
self.locked[msg.sender].end = 0
self.locked[msg.sender].amount = 0

assert ERC20(self.token).transfer(msg.sender, value)

log Withdraw(msg.sender, value, block.timestamp)

@external def commit_smart_wallet_checker(addr: address): """ @notice Set an external contract to check for approved smart contract wallets @param addr Address of Smart contract checker """ assert msg.sender == self.admin self.future_smart_wallet_checker = addr

@external def apply_smart_wallet_checker(): """ @notice Apply setting external contract to check approved smart contract wallets """ assert msg.sender == self.admin self.smart_wallet_checker = self.future_smart_wallet_checker

@internal def assert_not_contract(addr: address): """ @notice Check if the call is from a whitelisted smart contract, revert if not @param addr Address to be checked """ if addr != tx.origin: checker: address = self.smart_wallet_checker if checker != ZERO_ADDRESS: if SmartWalletChecker(checker).check(addr): return raise "Smart contract depositors not allowed"

@external @view def get_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] return self.user_point_history[addr][uepoch].slope

@external @view def user_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 """ return self.user_point_history[_addr][_idx].ts

@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

@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: int128 = 0 new_dslope: int128 = 0 _epoch: uint256 = self.epoch

if addr != ZERO_ADDRESS:
    # Calculate slopes and biases
    # Kept at zero when they have to
    if old_locked.end > block.timestamp and old_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)
    if new_locked.end > block.timestamp and new_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 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, 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 contract
initial_last_point: Point = last_point
block_slope: uint256 = 0  # dblock/dt
if block.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 is
t_i: uint256 = (last_checkpoint / WEEK) * WEEK
for i 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: int128 = 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, int128)
    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
    last_point.blk = initial_last_point.blk + block_slope * (t_i - initial_last_point.ts) / MULTIPLIER
    _epoch += 1
    if t_i == block.timestamp:
        last_point.blk = block.number
        break
    else:
        self.point_history[_epoch] = last_point

self.epoch = _epoch
# Now point_history is filled until t=now

if addr != 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)
    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 != 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]
    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
    u_new.blk = block.number
    self.user_point_history[addr][user_epoch] = u_new

@internal def _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_balance supply_before: uint256 = self.supply

self.supply = supply_before + _value
old_locked: LockedBalance = _locked
# Adding to existing lock, or if a lock is expired - creating a new one
_locked.amount += convert(_value, int128)
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 _value != 0:
    assert ERC20(self.token).transferFrom(sender, self, _value)

log Deposit(_addr, _value, _locked.end, type, block.timestamp)
log Supply(supply_before, supply_before + _value)

@external def checkpoint(): """ @notice Record global data to checkpoint """ assert not self.emergency_withdrawal, "Emergency withdrawal enabled" self._checkpoint(ZERO_ADDRESS, empty(LockedBalance), empty(LockedBalance))

@external @nonreentrant('lock') def deposit_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 """ assert not self.emergency_withdrawal, "Emergency withdrawal enabled" _locked: LockedBalance = self.locked[_addr]

assert _value > 0  # 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(_addr, _value, 0, self.locked[_addr], DEPOSIT_FOR_TYPE, msg.sender)

@external @nonreentrant('lock') 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 """ assert not self.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 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 + 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') def increase_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 """ assert not self.emergency_withdrawal, "Emergency withdrawal enabled" self.assert_not_contract(msg.sender) _locked: LockedBalance = self.locked[msg.sender]

assert _value > 0  # 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(msg.sender, _value, 0, _locked, INCREASE_LOCK_AMOUNT, msg.sender)

@external @nonreentrant('lock') 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 """ assert not self.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 weeks

assert _locked.end > block.timestamp, "Lock expired"
assert _locked.amount > 0, "Nothing is locked"
assert unlock_time > _locked.end, "Can only increase lock duration"
assert unlock_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') def withdraw(): """ @notice Withdraw all tokens for msg.sender @dev Only possible if the lock has expired """ assert not self.emergency_withdrawal, "Emergency withdrawal enabled" _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
self.supply = supply_before - value

# 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)

assert ERC20(self.token).transfer(msg.sender, value)

log Withdraw(msg.sender, value, block.timestamp)
log Supply(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 @view def find_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_epoch
for i in range(128):  # Will be always enough for 128-bit numbers
    if _min >= _max:
        break
    _mid: uint256 = (_min + _max + 1) / 2
    if self.point_history[_mid].blk <= _block:
        _min = _mid
    else:
        _max = _mid - 1
return _min

@internal @view def _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 = 0 maximum_value: uint256 = self.user_point_epoch[addr]

for i in range(128):  # Will be always enough for 128-bit numbers
    if minimum_value >= maximum_value:
        break
    mid: uint256 = (minimum_value + maximum_value + 1) / 2
    if self.user_point_history[addr][mid].ts <= ts:
        minimum_value = mid
    else:
        maximum_value = mid - 1
return minimum_value

@external @view def 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 """ return self._find_user_timestamp_epoch(addr, ts)

@external @view def balanceOf(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: return 0 else: last_point: Point = self.user_point_history[addr][_epoch] last_point.bias -= last_point.slope * convert(ts - last_point.ts, int128) if last_point.bias < 0: last_point.bias = 0 return convert(last_point.bias, uint256)

@internal @view def _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 yet
assert _block <= block.number

# Binary search
_min: uint256 = 0
_max: uint256 = self.user_point_epoch[addr]
for i 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[addr][_mid].blk <= _block:
        _min = _mid
    else:
        _max = _mid - 1

upoint: 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 = 0
d_t: uint256 = 0
if _epoch < max_epoch:
    point_1: Point = self.point_history[_epoch + 1]
    d_block = point_1.blk - point_0.blk
    d_t = point_1.ts - point_0.ts
else:
    d_block = block.number - point_0.blk
    d_t = block.timestamp - point_0.ts
block_time: uint256 = point_0.ts
if d_block != 0:
    block_time += d_t * (_block - point_0.blk) / d_block

upoint.bias -= upoint.slope * convert(block_time - upoint.ts, int128)
if upoint.bias >= 0:
    return convert(upoint.bias, uint256)
else:
    return 0

@external @view def balanceOfAt(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 """ return self._balanceOfAt(addr,_block)

@external @view def getPastVotes(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 """ return self._balanceOfAt(addr,_block)

@internal @view def supply_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 = point t_i: uint256 = (last_point.ts / WEEK) WEEK for i in range(255): t_i += WEEK d_slope: int128 = 0 if t_i > t: t_i = t else: d_slope = self.slope_changes[t_i] last_point.bias -= last_point.slope convert(t_i - last_point.ts, int128) if t_i == t: break last_point.slope += d_slope last_point.ts = t_i

if last_point.bias < 0:
    last_point.bias = 0
return convert(last_point.bias, uint256)

@external @view def totalSupply(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.epoch last_point: Point = self.point_history[_epoch] return self.supply_at(last_point, t)

@external @view def totalSupplyAt(_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.epoch target_epoch: uint256 = self.find_block_epoch(_block, _epoch)

point: Point = self.point_history[target_epoch]
dt: uint256 = 0
if target_epoch < _epoch:
    point_next: Point = self.point_history[target_epoch + 1]
    if point.blk != point_next.blk:
        dt = (_block - point.blk) * (point_next.ts - point.ts) / (point_next.blk - point.blk)
else:
    if point.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 point

return self.supply_at(point, point.ts + dt)

```json
{"nonreentrant.lock": {"type": "nonreentrant lock", "location": "storage", "slot": 4}, "token": {"type": "address", "location": "storage", "slot": 5}, "supply": {"type": "uint256", "location": "storage", "slot": 6}, "locked": {"type": "HashMap[address, LockedBalance]", "location": "storage", "slot": 7}, "epoch": {"type": "uint256", "location": "storage", "slot": 8}, "point_history": {"type": "Point[100000000000000000000000000000]", "location": "storage", "slot": 9}, "user_point_history": {"type": "HashMap[address, Point[1000000000]]", "location": "storage", "slot": 399999999999999965732603428873}, "user_point_epoch": {"type": "HashMap[address, uint256]", "location": "storage", "slot": 399999999999999965732603428874}, "slope_changes": {"type": "HashMap[uint256, int128]", "location": "storage", "slot": 399999999999999965732603428875}, "name": {"type": "String[64]", "location": "storage", "slot": 399999999999999965732603428876}, "symbol": {"type": "String[32]", "location": "storage", "slot": 399999999999999965732603428880}, "decimals": {"type": "uint256", "location": "storage", "slot": 399999999999999965732603428883}, "future_smart_wallet_checker": {"type": "address", "location": "storage", "slot": 399999999999999965732603428884}, "smart_wallet_checker": {"type": "address", "location": "storage", "slot": 399999999999999965732603428885}, "admin": {"type": "address", "location": "storage", "slot": 399999999999999965732603428886}, "future_admin": {"type": "address", "location": "storage", "slot": 399999999999999965732603428887}, "initialized": {"type": "bool", "location": "storage", "slot": 39999999999999996573260342888}, "emergency_withdrawal": {"type": "bool", "location": "storage", "slot": 39999999999999996573260342889}}

It have the same output

How can it be fixed?

I have no idea on how it can be fixed

cyberthirst commented 3 months ago

I described the cause in: https://github.com/vyperlang/vyper/pull/3789#issuecomment-2132795606

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.