Skip to content

Commit

Permalink
use correct asset precisions for erc4626
Browse files Browse the repository at this point in the history
  • Loading branch information
bout3fiddy committed Oct 12, 2023
1 parent 63b46dc commit fa9731a
Show file tree
Hide file tree
Showing 5 changed files with 489 additions and 17 deletions.
22 changes: 17 additions & 5 deletions contracts/main/CurveStableSwapMetaNG.vy
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,10 @@ rate_multipliers: immutable(DynArray[uint256, MAX_COINS])
# [bytes4 method_id][bytes8 <empty>][bytes20 oracle]
oracles: DynArray[uint256, MAX_COINS]

# For ERC4626 tokens, we need:
call_amount: immutable(uint256)
scale_factor: immutable(uint256)

last_prices_packed: DynArray[uint256, MAX_COINS] # packing: last_price, ma_price
last_D_packed: uint256 # packing: last_D, ma_D
ma_exp_time: public(uint256)
Expand Down Expand Up @@ -320,7 +324,14 @@ def __init__(
# _exchange_underlying:
ERC20(_base_coins[i]).approve(BASE_POOL, max_value(uint256))

self.last_prices_packed.append(self.pack_2(10**18, 10**18))
# For ERC4626 tokens:
if asset_types[0] == 3:
# In Vyper 0.3.10, if immutables are not set, because of an if-statement,
# it is by default set to 0; this is fine in the case of these two
# immutables, since they are only used if asset_types[0] == 3.
call_amount = 10**convert(ERC20Detailed(_coins[0]).decimals(), uint256)
_underlying_asset: address = ERC4626(_coins[0]).asset()
scale_factor = 10**(18 - convert(ERC20Detailed(_underlying_asset).decimals(), uint256))

# ----------------- Parameters independent of pool type ------------------

Expand All @@ -337,6 +348,9 @@ def __init__(
self.D_ma_time = 62324 # <--------- 12 hours default on contract start.
self.ma_last_time = self.pack_2(block.timestamp, block.timestamp)

# ------------------- initialize storage for DynArrays ------------------

self.last_prices_packed.append(self.pack_2(10**18, 10**18))
for i in range(N_COINS_128):

self.oracles.append(convert(_method_ids[i], uint256) * 2**224 | convert(_oracles[i], uint256))
Expand Down Expand Up @@ -500,13 +514,11 @@ def _stored_rates() -> DynArray[uint256, MAX_COINS]:

elif asset_types[0] == 3: # ERC4626

coin_decimals: uint256 = convert(ERC20Detailed(coins[0]).decimals(), uint256)

# rates[0] * fetched_rate / PRECISION
rates[0] = unsafe_div(
rates[0] * ERC4626(coins[0]).convertToAssets(10**coin_decimals) * 10**(18 - coin_decimals),
rates[0] * ERC4626(coins[0]).convertToAssets(call_amount) * scale_factor,
PRECISION
)
) # 1e18 precision

return rates

Expand Down
45 changes: 33 additions & 12 deletions contracts/main/CurveStableSwapNG.vy
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ rate_multipliers: immutable(DynArray[uint256, MAX_COINS])
# [bytes4 method_id][bytes8 <empty>][bytes20 oracle]
oracles: DynArray[uint256, MAX_COINS]

# For ERC4626 tokens, we need:
call_amount: immutable(DynArray[uint256, MAX_COINS])
scale_factor: immutable(DynArray[uint256, MAX_COINS])

last_prices_packed: DynArray[uint256, MAX_COINS] # packing: last_price, ma_price
last_D_packed: uint256 # packing: last_D, ma_D
ma_exp_time: public(uint256)
Expand Down Expand Up @@ -259,11 +263,6 @@ def __init__(
N_COINS = __n_coins
N_COINS_128 = convert(__n_coins, int128)

for i in range(MAX_COINS):
if i == __n_coins - 1:
break
self.last_prices_packed.append(self.pack_2(10**18, 10**18))

rate_multipliers = _rate_multipliers
POOL_IS_REBASING_IMPLEMENTATION = 2 in _asset_types

Expand All @@ -280,18 +279,37 @@ def __init__(
self.D_ma_time = 62324 # <--------- 12 hours default on contract start.
self.ma_last_time = self.pack_2(block.timestamp, block.timestamp)

# ------------------- initialize storage for DynArrays ------------------

_call_amount: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
_scale_factor: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
for i in range(MAX_COINS_128):

if i == N_COINS_128:
break

self.oracles.append(convert(_method_ids[i], uint256) * 2**224 | convert(_oracles[i], uint256))
if i < N_COINS_128 - 1:
self.last_prices_packed.append(self.pack_2(10**18, 10**18))

# --------------------------- initialize storage ---------------------------
self.oracles.append(convert(_method_ids[i], uint256) * 2**224 | convert(_oracles[i], uint256))
self.stored_balances.append(0)
self.admin_balances.append(0)

# --------------------------- ERC20 stuff ----------------------------
if _asset_types[i] == 3:

_call_amount.append(10**convert(ERC20Detailed(_coins[i]).decimals(), uint256))
_underlying_asset: address = ERC4626(_coins[i]).asset()
_scale_factor.append(10**(18 - convert(ERC20Detailed(_underlying_asset).decimals(), uint256)))

else:

_call_amount.append(0)
_scale_factor.append(0)

call_amount = _call_amount
scale_factor = _scale_factor

# ----------------------------- ERC20 stuff ------------------------------

name = _name
symbol = _symbol
Expand Down Expand Up @@ -421,10 +439,13 @@ def _stored_rates() -> DynArray[uint256, MAX_COINS]:

elif asset_types[i] == 3: # ERC4626

coin_decimals: uint256 = convert(ERC20Detailed(coins[i]).decimals(), uint256)
fetched_rate: uint256 = ERC4626(coins[i]).convertToAssets(10**coin_decimals) * 10**(18 - coin_decimals)

rates[i] = unsafe_div(rates[i] * fetched_rate, PRECISION)
# fetched_rate: uint256 = ERC4626(coins[i]).convertToAssets(call_amount[i]) * scale_factor[i]
# here: call_amount has ERC4626 precision, but the returned value is scaled up to 18
# using scale_factor which is (18 - n) if underlying asset has n decimals.
rates[i] = unsafe_div(
rates[i] * ERC4626(coins[i]).convertToAssets(call_amount[i]) * scale_factor[i],
PRECISION
) # 1e18 precision

return rates

Expand Down
Loading

0 comments on commit fa9731a

Please sign in to comment.