Skip to content

Commit

Permalink
Merge branch 'feat/tests' of github.com:curvefi/twocrypto-ng into fea…
Browse files Browse the repository at this point in the history
…t/tests
  • Loading branch information
Filipp committed Nov 15, 2023
2 parents 91122cd + 407fd0f commit 2ff7b80
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 32 deletions.
8 changes: 4 additions & 4 deletions contracts/main/CurveTwocryptoFactory.vy
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def __init__(_fee_receiver: address, _admin: address):

@internal
@view
def _pack(x: uint256[3]) -> uint256:
def _pack_3(x: uint256[3]) -> uint256:
"""
@notice Packs 3 integers with values <= 10**18 into a uint256
@param x The uint256[3] to pack
Expand Down Expand Up @@ -181,15 +181,15 @@ def deploy_pool(
d: uint256 = ERC20(_coins[i]).decimals()
assert d < 19, "Max 18 decimals for coins"
decimals[i] = d
precisions[i] = 10** (18 - d)
precisions[i] = 10 ** (18 - d)

# pack fees
packed_fee_params: uint256 = self._pack(
packed_fee_params: uint256 = self._pack_3(
[mid_fee, out_fee, fee_gamma]
)

# pack liquidity rebalancing params
packed_rebalancing_params: uint256 = self._pack(
packed_rebalancing_params: uint256 = self._pack_3(
[allowed_extra_profit, adjustment_step, ma_exp_time]
)

Expand Down
68 changes: 40 additions & 28 deletions contracts/main/CurveTwocryptoOptimized.vy
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ cached_price_oracle: uint256 # <------- Price target given by moving average.
cached_xcp_oracle: uint256 # <----------- EMA of totalSupply * virtual_price.

last_prices: public(uint256)
last_timestamp: public(uint256[2]) # idx 0 is for prices, idx 1 is for xcp.
last_timestamp: public(uint256) # idx 0 is for prices, idx 1 is for xcp.
last_xcp: public(uint256)
xcp_ma_time: public(uint256)

Expand Down Expand Up @@ -247,7 +247,7 @@ def __init__(
self.cached_price_scale = initial_price
self.cached_price_oracle = initial_price
self.last_prices = initial_price
self.last_timestamp = [block.timestamp, block.timestamp]
self.last_timestamp = self._pack_2(block.timestamp, block.timestamp)
self.xcp_profit_a = 10**18
self.xcp_ma_time = 62324 # <--------- 12 hours default on contract start.

Expand Down Expand Up @@ -461,6 +461,8 @@ def add_liquidity(
@return uint256 Amount of LP tokens received by the `receiver
"""

self._claim_admin_fees() # <--------- Auto-claim admin fees occasionally.

A_gamma: uint256[2] = self._A_gamma()
xp: uint256[N_COINS] = self.balances
amountsp: uint256[N_COINS] = empty(uint256[N_COINS])
Expand Down Expand Up @@ -564,8 +566,6 @@ def add_liquidity(
price_scale
)

self._claim_admin_fees() # <--------- Auto-claim admin fees occasionally.

return d_token


Expand Down Expand Up @@ -638,7 +638,7 @@ def remove_liquidity(
xp: uint256[N_COINS] = self.xp(self.balances, self.cached_price_scale)
self.last_xcp = isqrt(xp[0] * xp[1])

last_timestamp: uint256[2] = self.last_timestamp
last_timestamp: uint256[2] = self._unpack_2(self.last_timestamp)
if last_timestamp[1] < block.timestamp:

cached_xcp_oracle: uint256 = self.cached_xcp_oracle
Expand All @@ -659,7 +659,7 @@ def remove_liquidity(
last_timestamp[1] = block.timestamp

# Pack and store timestamps:
self.last_timestamp = last_timestamp
self.last_timestamp = self._pack_2(last_timestamp[0], last_timestamp[1])

return withdraw_amounts

Expand All @@ -683,6 +683,8 @@ def remove_liquidity_one_coin(
@return Amount of tokens at index i received by the `receiver`
"""

self._claim_admin_fees() # <--------- Auto-claim admin fees occasionally.

A_gamma: uint256[2] = self._A_gamma()

dy: uint256 = 0
Expand Down Expand Up @@ -720,8 +722,6 @@ def remove_liquidity_one_coin(
msg.sender, token_amount, i, dy, approx_fee, packed_price_scale
)

self._claim_admin_fees() # <--------- Auto-claim admin fees occasionally.

return dy


Expand All @@ -730,7 +730,7 @@ def remove_liquidity_one_coin(

@internal
@pure
def _pack(x: uint256[3]) -> uint256:
def _pack_3(x: uint256[3]) -> uint256:
"""
@notice Packs 3 integers with values <= 10**18 into a uint256
@param x The uint256[3] to pack
Expand All @@ -741,7 +741,7 @@ def _pack(x: uint256[3]) -> uint256:

@internal
@pure
def _unpack(_packed: uint256) -> uint256[3]:
def _unpack_3(_packed: uint256) -> uint256[3]:
"""
@notice Unpacks a uint256 into 3 integers (values must be <= 10**18)
@param val The uint256 to unpack
Expand All @@ -754,6 +754,18 @@ def _unpack(_packed: uint256) -> uint256[3]:
]


@pure
@internal
def _pack_2(p1: uint256, p2: uint256) -> uint256:
return p1 | (p2 << 128)


@pure
@internal
def _unpack_2(packed: uint256) -> uint256[2]:
return [packed & (2**128 - 1), packed >> 128]


# ---------------------- AMM Internal Functions -------------------------------


Expand Down Expand Up @@ -849,7 +861,7 @@ def tweak_price(
price_oracle: uint256 = self.cached_price_oracle
last_prices: uint256 = self.last_prices
price_scale: uint256 = self.cached_price_scale
rebalancing_params: uint256[3] = self._unpack(self.packed_rebalancing_params)
rebalancing_params: uint256[3] = self._unpack_3(self.packed_rebalancing_params)
# Contains: allowed_extra_profit, adjustment_step, ma_time. -----^

total_supply: uint256 = self.totalSupply
Expand All @@ -858,7 +870,7 @@ def tweak_price(

# ----------------------- Update Oracles if needed -----------------------

last_timestamp: uint256[2] = self.last_timestamp
last_timestamp: uint256[2] = self._unpack_2(self.last_timestamp)
alpha: uint256 = 0
if last_timestamp[0] < block.timestamp: # 0th index is for price_oracle.

Expand Down Expand Up @@ -914,7 +926,7 @@ def tweak_price(
# Pack and store timestamps:
last_timestamp[1] = block.timestamp

self.last_timestamp = last_timestamp
self.last_timestamp = self._pack_2(last_timestamp[0], last_timestamp[1])

# `price_oracle` is used further on to calculate its vector distance from
# price_scale. This distance is used to calculate the amount of adjustment
Expand Down Expand Up @@ -1211,7 +1223,7 @@ def _A_gamma() -> uint256[2]:
@view
def _fee(xp: uint256[N_COINS]) -> uint256:

fee_params: uint256[3] = self._unpack(self.packed_fee_params)
fee_params: uint256[3] = self._unpack_3(self.packed_fee_params)
f: uint256 = xp[0] + xp[1]
f = fee_params[2] * 10**18 / (
fee_params[2] + 10**18 -
Expand Down Expand Up @@ -1308,7 +1320,7 @@ def _calc_withdraw_one_coin(

xp_imprecise: uint256[N_COINS] = xp
xp_correction: uint256 = xp[i] * N_COINS * token_amount / token_supply
fee: uint256 = self._unpack(self.packed_fee_params)[1] # <- self.out_fee.
fee: uint256 = self._unpack_3(self.packed_fee_params)[1] # <- self.out_fee.

if xp_correction < xp_imprecise[i]:
xp_imprecise[i] -= xp_correction
Expand Down Expand Up @@ -1508,13 +1520,13 @@ def internal_price_oracle() -> uint256:
"""
price_oracle: uint256 = self.cached_price_oracle
price_scale: uint256 = self.cached_price_scale
last_prices_timestamp: uint256 = self.last_timestamp[0]
last_prices_timestamp: uint256 = self._unpack_2(self.last_timestamp)[0]

if last_prices_timestamp < block.timestamp: # <------------ Update moving
# average if needed.

last_prices: uint256 = self.last_prices
ma_time: uint256 = self._unpack(self.packed_rebalancing_params)[2]
ma_time: uint256 = self._unpack_3(self.packed_rebalancing_params)[2]
alpha: uint256 = MATH.wad_exp(
-convert(
(block.timestamp - last_prices_timestamp) * 10**18 / ma_time,
Expand Down Expand Up @@ -1644,7 +1656,7 @@ def xcp_oracle() -> uint256:
@return uint256 Oracle value of xcp.
"""

last_prices_timestamp: uint256 = self.last_timestamp[1]
last_prices_timestamp: uint256 = self._unpack_2(self.last_timestamp)[1]
cached_xcp_oracle: uint256 = self.cached_xcp_oracle

if last_prices_timestamp < block.timestamp:
Expand Down Expand Up @@ -1747,7 +1759,7 @@ def mid_fee() -> uint256:
@notice Returns the current mid fee
@return uint256 mid_fee value.
"""
return self._unpack(self.packed_fee_params)[0]
return self._unpack_3(self.packed_fee_params)[0]


@view
Expand All @@ -1757,7 +1769,7 @@ def out_fee() -> uint256:
@notice Returns the current out fee
@return uint256 out_fee value.
"""
return self._unpack(self.packed_fee_params)[1]
return self._unpack_3(self.packed_fee_params)[1]


@view
Expand All @@ -1767,7 +1779,7 @@ def fee_gamma() -> uint256:
@notice Returns the current fee gamma
@return uint256 fee_gamma value.
"""
return self._unpack(self.packed_fee_params)[2]
return self._unpack_3(self.packed_fee_params)[2]


@view
Expand All @@ -1777,7 +1789,7 @@ def allowed_extra_profit() -> uint256:
@notice Returns the current allowed extra profit
@return uint256 allowed_extra_profit value.
"""
return self._unpack(self.packed_rebalancing_params)[0]
return self._unpack_3(self.packed_rebalancing_params)[0]


@view
Expand All @@ -1787,7 +1799,7 @@ def adjustment_step() -> uint256:
@notice Returns the current adjustment step
@return uint256 adjustment_step value.
"""
return self._unpack(self.packed_rebalancing_params)[1]
return self._unpack_3(self.packed_rebalancing_params)[1]


@view
Expand All @@ -1799,7 +1811,7 @@ def ma_time() -> uint256:
One can expect off-by-one errors here.
@return uint256 ma_time value.
"""
return self._unpack(self.packed_rebalancing_params)[2] * 694 / 1000
return self._unpack_3(self.packed_rebalancing_params)[2] * 694 / 1000


@view
Expand Down Expand Up @@ -1937,7 +1949,7 @@ def apply_new_parameters(
new_out_fee: uint256 = _new_out_fee
new_fee_gamma: uint256 = _new_fee_gamma

current_fee_params: uint256[3] = self._unpack(self.packed_fee_params)
current_fee_params: uint256[3] = self._unpack_3(self.packed_fee_params)

if new_out_fee < MAX_FEE + 1:
assert new_out_fee > MIN_FEE - 1 # dev: fee is out of range
Expand All @@ -1953,15 +1965,15 @@ def apply_new_parameters(
else:
new_fee_gamma = current_fee_params[2]

self.packed_fee_params = self._pack([new_mid_fee, new_out_fee, new_fee_gamma])
self.packed_fee_params = self._pack_3([new_mid_fee, new_out_fee, new_fee_gamma])

# ----------------- Set liquidity rebalancing parameters -----------------

new_allowed_extra_profit: uint256 = _new_allowed_extra_profit
new_adjustment_step: uint256 = _new_adjustment_step
new_ma_time: uint256 = _new_ma_time

current_rebalancing_params: uint256[3] = self._unpack(self.packed_rebalancing_params)
current_rebalancing_params: uint256[3] = self._unpack_3(self.packed_rebalancing_params)

if new_allowed_extra_profit > 10**18:
new_allowed_extra_profit = current_rebalancing_params[0]
Expand All @@ -1974,7 +1986,7 @@ def apply_new_parameters(
else:
new_ma_time = current_rebalancing_params[2]

self.packed_rebalancing_params = self._pack(
self.packed_rebalancing_params = self._pack_3(
[new_allowed_extra_profit, new_adjustment_step, new_ma_time]
)

Expand Down
7 changes: 7 additions & 0 deletions tests/fixtures/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ def user():
return acc


@pytest.fixture(scope="module")
def user_b():
acc = boa.env.generate_address()
boa.env.set_balance(acc, 10**25)
return acc


@pytest.fixture(scope="module")
def users():
accs = [i() for i in [boa.env.generate_address] * 10]
Expand Down
11 changes: 11 additions & 0 deletions tests/unitary/math/test_get_y.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,14 @@ def calculate_F_by_y0(y0):
) or abs(calculate_F_by_y0(result_get_y)) <= abs(
calculate_F_by_y0(result_original)
)


def test_get_y_revert(math_contract):
a = 1723894848
gamma = 24009999997600
x = [112497148627520223862735198942112, 112327102289152450435452075003508]
D = 224824250915890636214130540882688
i = 0

math_contract.newton_y(a, gamma, x, D, i)
math_contract.get_y(a, gamma, x, D, i)
23 changes: 23 additions & 0 deletions tests/unitary/math/test_packing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from boa.test import strategy
from hypothesis import given, settings


@given(val=strategy("uint256[3]", max_value=10**18))
@settings(max_examples=10000, deadline=None)
def test_pack_unpack_three_integers(swap, twocrypto_factory, val):

for contract in [swap, twocrypto_factory]:
packed = contract.internal._pack_3(val)
unpacked = swap.internal._unpack_3(packed) # swap unpacks
for i in range(3):
assert unpacked[i] == val[i]


@given(val=strategy("uint256[2]", max_value=2**128 - 1))
@settings(max_examples=10000, deadline=None)
def test_pack_unpack_2_integers(swap, val):

packed = swap.internal._pack_2(val[0], val[1])
unpacked = swap.internal._unpack_2(packed) # swap unpacks
for i in range(2):
assert unpacked[i] == val[i]
40 changes: 40 additions & 0 deletions tests/unitary/pool/test_admin_fee.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import boa

from tests.fixtures.pool import INITIAL_PRICES
from tests.utils.tokens import mint_for_testing


def test_admin_fee_after_deposit(swap, coins, fee_receiver, user, user_b):
quantities = [10**42 // p for p in INITIAL_PRICES]

for coin, q in zip(coins, quantities):
for u in [user, user_b]:
mint_for_testing(coin, u, q)
with boa.env.prank(u):
coin.approve(swap, 2**256 - 1)

split_quantities = [quantities[0] // 100, quantities[1] // 100]

with boa.env.prank(user):
swap.add_liquidity(split_quantities, 0)

with boa.env.prank(user_b):
for _ in range(100):
before = coins[1].balanceOf(user_b)
swap.exchange(0, 1, split_quantities[0] // 100, 0)
after = coins[1].balanceOf(user_b)
to_swap = after - before
swap.exchange(1, 0, to_swap, 0)

balances = [swap.balances(i) for i in range(2)]
print("Balance of the pool: " + str(balances[0]) + ", " + str(balances[1]))

ratio = 0.0001
split_quantities = [int(balances[0] * ratio), int(balances[1] * ratio)]
with boa.env.prank(user):
swap.add_liquidity(split_quantities, 0)

print("FEES 0: " + str(coins[0].balanceOf(fee_receiver)))
print("FEES 1: " + str(coins[1].balanceOf(fee_receiver)))

return swap

0 comments on commit 2ff7b80

Please sign in to comment.