Skip to content

Commit

Permalink
Merge pull request #190 from curveresearch/get-in-amount
Browse files Browse the repository at this point in the history
Get in amount
  • Loading branch information
chanhosuh authored Aug 4, 2023
2 parents 9eade79 + d5513a1 commit 82ce358
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 20 deletions.
7 changes: 7 additions & 0 deletions changelog.d/20230802_174720_chanhosuh_get_in_amount.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Added
-----

- Added `get_y` to `CurveCryptoPool` as with the stableswap pools.
This breaks tight adherence to the vyper interface, but makes it easier
for integrators.

35 changes: 35 additions & 0 deletions curvesim/pool/cryptoswap/pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,41 @@ def get_dy(self, i: int, j: int, dx: int) -> int:

return dy

def get_y(self, i, j, x, xp):
r"""
Calculate x[j] if one makes x[i] = x.
Parameters
----------
i: int
index of coin; usually the "in"-token
j: int
index of coin; usually the "out"-token
x: int
balance of i-th coin in units of D
xp: list of int
coin balances in units of D
Returns
-------
int
The balance of the j-th coin, in units of D, for the other
coin balances given.
Note
----
This is a "view" function; it doesn't change the state of the pool.
"""
A: int = self.A
gamma: int = self.gamma
D: int = _newton_D(A, gamma, xp)

xp = xp.copy()
xp[i] = x

y, _ = _get_y(A, gamma, xp, D, j)
return y

def _fee(self, xp: List[int]) -> int:
"""
f = fee_gamma / (fee_gamma + (1 - K))
Expand Down
23 changes: 7 additions & 16 deletions curvesim/pool/sim_interface/cryptoswap.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,22 +117,13 @@ def get_in_amount(self, coin_in, coin_out, out_balance_perc):
An approximate quantity to swap to achieve the target out-token
balance
"""
raise SimPoolError("`get_in_amount` not implemented for SimCurveCryptoPool.")
# i, j = self.get_asset_indices(coin_in, coin_out)

# The cryptoswap (dynamic) fee is calculated on the state `xp`,
# which has been adjusted by increasing in-token balance and
# decreasing out-token balance.
#
# This means we can't just back out the `in_amount` using `get_y`
# as with stableswap (fixed fee).
#
# We can use the following get_dx helper function:
# https://github.com/curvefi/tricrypto-ng/blob/main/contracts/main/CurveCryptoViews3Optimized.vy#L183
# Tricrypto-ng uses this 5 times in a loop, we may want to increase the
# number of iterations.

# return in_amount
i, j = self.get_asset_indices(coin_in, coin_out)

xp = self._xp()
xp_j = int(xp[j] * out_balance_perc)

in_amount = self.get_y(j, i, xp_j, xp) - xp[i]
return in_amount

@property
@override
Expand Down
39 changes: 35 additions & 4 deletions test/unit/test_tricrypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
MIN_GAMMA,
PRECISION,
)
from curvesim.pool.cryptoswap.pool import _get_y, _newton_D


def get_math(tricrypto):
Expand Down Expand Up @@ -250,7 +251,7 @@ def test_multiple_exchange_with_repeg(
max_examples=5,
deadline=None,
)
def test_newton_D(vyper_tricrypto, A, gamma, x0, x1, x2):
def test__newton_D(vyper_tricrypto, A, gamma, x0, x1, x2):
"""Test D calculation against vyper implementation."""

xp = [x0, x1, x2]
Expand All @@ -261,7 +262,7 @@ def test_newton_D(vyper_tricrypto, A, gamma, x0, x1, x2):
MATH = get_math(vyper_tricrypto)
# pylint: disable=no-member
expected_D = MATH.newton_D(A, gamma, xp)
D = tricrypto_ng.newton_D(A, gamma, xp)
D = _newton_D(A, gamma, xp)

assert D == expected_D

Expand Down Expand Up @@ -314,7 +315,7 @@ def test_get_p(vyper_tricrypto, A, gamma, x0, x1, x2):
max_examples=5,
deadline=None,
)
def test_get_y(vyper_tricrypto, A, gamma, x0, x1, x2, pair, dx_perc):
def test__get_y(vyper_tricrypto, A, gamma, x0, x1, x2, pair, dx_perc):
"""Test D calculation against vyper implementation."""
i, j = pair

Expand All @@ -330,12 +331,42 @@ def test_get_y(vyper_tricrypto, A, gamma, x0, x1, x2, pair, dx_perc):
xp[i] += xp[i] * dx_perc // 10000

expected_y_out = MATH.get_y(A, gamma, xp, D, j)
y_out = tricrypto_ng.get_y(A, gamma, xp, D, j)
y_out = _get_y(A, gamma, xp, D, j)

assert y_out[0] == expected_y_out[0]
assert y_out[1] == expected_y_out[1]


def test_get_y(vyper_tricrypto):
"""
Test `get_y`.
Note `_get_y`, which is the pure version, is already tested
thoroughly in its own test against the vyper.
This test is a sanity check to make sure we pass values in correctly
to the underlying `_get_y` implementation.
"""
pool = initialize_pool(vyper_tricrypto)

xp = pool._xp()
A = pool.A
gamma = pool.gamma
D = _newton_D(A, gamma, xp)

i = 0
j = 1

# `get_y` will set i-th balance to `x`
x = xp[i] * 102 // 100
y = pool.get_y(i, j, x, xp)

xp[i] = x
expected_y, _ = _get_y(A, gamma, xp, D, j)

assert y == expected_y


@given(st.integers(min_value=-42139678854452767551, max_value=135305999368893231589))
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
Expand Down

0 comments on commit 82ce358

Please sign in to comment.