token-swap: Remove stable-swap simulation tests (#3555)
This commit is contained in:
parent
8d1e57018a
commit
180d80be7d
|
@ -2100,29 +2100,6 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47741a8bc60fb26eb8d6e0238bbb26d8575ff623fdc97b1a2c00c050b9684ed8"
|
||||
dependencies = [
|
||||
"indoc-impl",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc-impl"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2 1.0.43",
|
||||
"quote 1.0.21",
|
||||
"syn 1.0.99",
|
||||
"unindent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.2"
|
||||
|
@ -3043,25 +3020,6 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880"
|
||||
dependencies = [
|
||||
"paste-impl",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste-impl"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pbkdf2"
|
||||
version = "0.4.0"
|
||||
|
@ -3328,12 +3286,6 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "0.4.30"
|
||||
|
@ -3487,54 +3439,6 @@ dependencies = [
|
|||
"autotools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cf01dbf1c05af0a14c7779ed6f3aa9deac9c3419606ac9de537a2d649005720"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"indoc",
|
||||
"libc",
|
||||
"parking_lot 0.11.2",
|
||||
"paste",
|
||||
"pyo3-build-config",
|
||||
"pyo3-macros",
|
||||
"unindent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-build-config"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbf9e4d128bfbddc898ad3409900080d8d5095c379632fbbfbb9c8cfb1fb852b"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67701eb32b1f9a9722b4bc54b548ff9d7ebfded011c12daece7b9063be1fd755"
|
||||
dependencies = [
|
||||
"pyo3-macros-backend",
|
||||
"quote 1.0.21",
|
||||
"syn 1.0.99",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros-backend"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f44f09e825ee49a105f2c7b23ebee50886a9aee0746f4dd5a704138a64b0218a"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.43",
|
||||
"pyo3-build-config",
|
||||
"quote 1.0.21",
|
||||
"syn 1.0.99",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qstring"
|
||||
version = "0.7.2"
|
||||
|
@ -4398,13 +4302,6 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788"
|
||||
|
||||
[[package]]
|
||||
name = "sim"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"pyo3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simpl"
|
||||
version = "0.1.0"
|
||||
|
@ -6356,7 +6253,6 @@ dependencies = [
|
|||
"num-traits",
|
||||
"proptest",
|
||||
"roots",
|
||||
"sim",
|
||||
"solana-program",
|
||||
"solana-sdk",
|
||||
"spl-math",
|
||||
|
@ -7218,12 +7114,6 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.4.1"
|
||||
|
|
|
@ -28,7 +28,6 @@ roots = { version = "0.0.7", optional = true }
|
|||
[dev-dependencies]
|
||||
proptest = "1.0"
|
||||
roots = "0.0.7"
|
||||
sim = { path = "./sim" }
|
||||
solana-sdk = "1.11.6"
|
||||
test-case = "2.2"
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
[package]
|
||||
name = "sim"
|
||||
version = "0.1.0"
|
||||
authors = ["michaelhly <michaelhly@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
pyo3 = { version = "0.15.1", features = ["auto-initialize"] }
|
|
@ -1,188 +0,0 @@
|
|||
# Source from: https://github.com/curvefi/curve-contract/blob/master/tests/simulation.py
|
||||
|
||||
class Curve:
|
||||
|
||||
"""
|
||||
Python model of Curve pool math.
|
||||
"""
|
||||
|
||||
def __init__(self, A, D, n, fee = 10 ** 7, p=None, tokens=None):
|
||||
"""
|
||||
A: Amplification coefficient
|
||||
D: Total deposit size
|
||||
n: number of currencies
|
||||
p: target prices
|
||||
"""
|
||||
self.A = A # actually A * n ** (n - 1) because it's an invariant
|
||||
self.n = n
|
||||
self.fee = fee
|
||||
if p:
|
||||
self.p = p
|
||||
else:
|
||||
self.p = [10 ** 18] * n
|
||||
if isinstance(D, list):
|
||||
self.x = D
|
||||
else:
|
||||
self.x = [D // n * 10 ** 18 // _p for _p in self.p]
|
||||
self.tokens = tokens
|
||||
|
||||
def xp(self):
|
||||
return [x * p // 10 ** 18 for x, p in zip(self.x, self.p)]
|
||||
|
||||
def D(self):
|
||||
"""
|
||||
D invariant calculation in non-overflowing integer operations
|
||||
iteratively
|
||||
|
||||
A * sum(x_i) * n**n + D = A * D * n**n + D**(n+1) / (n**n * prod(x_i))
|
||||
|
||||
Converging solution:
|
||||
D[j+1] = (A * n**n * sum(x_i) - D[j]**(n+1) / (n**n prod(x_i))) / (A * n**n - 1)
|
||||
"""
|
||||
Dprev = 0
|
||||
xp = self.xp()
|
||||
S = sum(xp)
|
||||
D = S
|
||||
Ann = self.A * self.n
|
||||
|
||||
counter = 0
|
||||
|
||||
while abs(D - Dprev) > 1:
|
||||
D_P = D
|
||||
for x in xp:
|
||||
D_P = D_P * D // (self.n * x + 1)
|
||||
Dprev = D
|
||||
D = (Ann * S + D_P * self.n) * D // ((Ann - 1) * D + (self.n + 1) * D_P)
|
||||
|
||||
counter += 1
|
||||
if counter > 1000:
|
||||
break
|
||||
|
||||
return D
|
||||
|
||||
def y(self, i, j, x):
|
||||
"""
|
||||
Calculate x[j] if one makes x[i] = x
|
||||
|
||||
Done by solving quadratic equation iteratively.
|
||||
x_1**2 + x1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
|
||||
x_1**2 + b*x_1 = c
|
||||
|
||||
x_1 = (x_1**2 + c) / (2*x_1 + b)
|
||||
"""
|
||||
D = self.D()
|
||||
xx = self.xp()
|
||||
xx[i] = x # x is quantity of underlying asset brought to 1e18 precision
|
||||
xx = [xx[k] for k in range(self.n) if k != j]
|
||||
Ann = self.A * self.n
|
||||
c = D
|
||||
for y in xx:
|
||||
c = c * D // (y * self.n)
|
||||
c = c * D // (self.n * Ann)
|
||||
b = sum(xx) + D // Ann - D
|
||||
y_prev = 0
|
||||
y = D
|
||||
|
||||
counter = 0
|
||||
|
||||
while abs(y - y_prev) > 1:
|
||||
y_prev = y
|
||||
y = (y ** 2 + c) // (2 * y + b)
|
||||
|
||||
counter += 1
|
||||
if counter > 1000:
|
||||
break
|
||||
|
||||
return y # the result is in underlying units too
|
||||
|
||||
def y_D(self, i, _D):
|
||||
"""
|
||||
Calculate x[j] if one makes x[i] = x
|
||||
|
||||
Done by solving quadratic equation iteratively.
|
||||
x_1**2 + x1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
|
||||
x_1**2 + b*x_1 = c
|
||||
|
||||
x_1 = (x_1**2 + c) / (2*x_1 + b)
|
||||
"""
|
||||
xx = self.xp()
|
||||
xx = [xx[k] for k in range(self.n) if k != i]
|
||||
S = sum(xx)
|
||||
Ann = self.A * self.n
|
||||
c = _D
|
||||
for y in xx:
|
||||
c = c * _D // (y * self.n)
|
||||
c = c * _D // (self.n * Ann)
|
||||
b = S + _D // Ann
|
||||
y_prev = 0
|
||||
y = _D
|
||||
|
||||
counter = 0
|
||||
|
||||
while abs(y - y_prev) > 1:
|
||||
y_prev = y
|
||||
y = (y ** 2 + c) // (2 * y + b - _D)
|
||||
|
||||
counter += 1
|
||||
if counter > 1000:
|
||||
break
|
||||
|
||||
return y # the result is in underlying units too
|
||||
|
||||
def dy(self, i, j, dx):
|
||||
# dx and dy are in underlying units
|
||||
xp = self.xp()
|
||||
return xp[j] - self.y(i, j, xp[i] + dx)
|
||||
|
||||
def exchange(self, i, j, dx):
|
||||
xp = self.xp()
|
||||
x = xp[i] + dx
|
||||
y = self.y(i, j, x)
|
||||
dy = xp[j] - y
|
||||
fee = dy * self.fee // 10 ** 10
|
||||
|
||||
#assert dy > 0
|
||||
if dy == 0:
|
||||
return 0
|
||||
|
||||
self.x[i] = x * 10 ** 18 // self.p[i]
|
||||
self.x[j] = (y + fee) * 10 ** 18 // self.p[j]
|
||||
return dy - fee
|
||||
|
||||
def remove_liquidity_imbalance(self, amounts):
|
||||
_fee = self.fee * self.n // (4 * (self.n - 1))
|
||||
|
||||
old_balances = self.x
|
||||
new_balances = self.x[:]
|
||||
D0 = self.D()
|
||||
for i in range(self.n):
|
||||
new_balances[i] -= amounts[i]
|
||||
self.x = new_balances
|
||||
D1 = self.D()
|
||||
self.x = old_balances
|
||||
fees = [0] * self.n
|
||||
for i in range(self.n):
|
||||
ideal_balance = D1 * old_balances[i] // D0
|
||||
difference = abs(ideal_balance - new_balances[i])
|
||||
fees[i] = _fee * difference // 10 ** 10
|
||||
new_balances[i] -= fees[i]
|
||||
self.x = new_balances
|
||||
D2 = self.D()
|
||||
self.x = old_balances
|
||||
|
||||
token_amount = (D0 - D2) * self.tokens // D0
|
||||
|
||||
return token_amount
|
||||
|
||||
def calc_withdraw_one_coin(self, token_amount, i):
|
||||
xp = self.xp()
|
||||
if self.fee:
|
||||
fee = self.fee - self.fee * xp[i] // sum(xp) + 5 * 10 ** 5
|
||||
else:
|
||||
fee = 0
|
||||
|
||||
D0 = self.D()
|
||||
D1 = D0 - token_amount * D0 // self.tokens
|
||||
dy = xp[i] - self.y_D(i, D1)
|
||||
|
||||
return dy - dy * fee // 10 ** 10
|
|
@ -1,193 +0,0 @@
|
|||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyTuple;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
|
||||
const FILE_NAME: &str = "simulation.py";
|
||||
const FILE_PATH: &str = "sim/simulation.py";
|
||||
const MODULE_NAME: &str = "simulation";
|
||||
|
||||
const DEFAULT_POOL_TOKENS: u128 = 0;
|
||||
const DEFAULT_TARGET_PRICE: u128 = 1000000000000000000;
|
||||
pub const MODEL_FEE_NUMERATOR: u128 = 1;
|
||||
pub const MODEL_FEE_DENOMINATOR: u128 = 1000;
|
||||
|
||||
pub struct StableSwapModel {
|
||||
py_src: String,
|
||||
pub amp_factor: u128,
|
||||
pub balances: Vec<u128>,
|
||||
pub n_coins: u8,
|
||||
pub fee: u128,
|
||||
pub target_prices: Vec<u128>,
|
||||
pub pool_tokens: u128,
|
||||
}
|
||||
|
||||
impl StableSwapModel {
|
||||
pub fn new(amp_factor: u128, balances: Vec<u128>, n_coins: u8) -> StableSwapModel {
|
||||
let mut src_file = File::open(FILE_PATH).unwrap();
|
||||
let mut src_content = String::new();
|
||||
let _ = src_file.read_to_string(&mut src_content);
|
||||
|
||||
Self {
|
||||
py_src: src_content,
|
||||
amp_factor,
|
||||
balances,
|
||||
n_coins,
|
||||
fee: 0,
|
||||
target_prices: vec![DEFAULT_TARGET_PRICE, DEFAULT_TARGET_PRICE],
|
||||
pool_tokens: DEFAULT_POOL_TOKENS,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_pool_tokens(
|
||||
amp_factor: u128,
|
||||
balances: Vec<u128>,
|
||||
n_coins: u8,
|
||||
pool_token_amount: u128,
|
||||
) -> StableSwapModel {
|
||||
let mut src_file = File::open(FILE_PATH).unwrap();
|
||||
let mut src_content = String::new();
|
||||
let _ = src_file.read_to_string(&mut src_content);
|
||||
|
||||
Self {
|
||||
py_src: src_content,
|
||||
amp_factor,
|
||||
balances,
|
||||
n_coins,
|
||||
fee: 0,
|
||||
target_prices: vec![DEFAULT_TARGET_PRICE, DEFAULT_TARGET_PRICE],
|
||||
pool_tokens: pool_token_amount,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sim_d(&self) -> u128 {
|
||||
let gil = Python::acquire_gil();
|
||||
return self
|
||||
.call0(gil.python(), "D")
|
||||
.unwrap()
|
||||
.extract(gil.python())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn sim_dy(&self, i: u128, j: u128, dx: u128) -> u128 {
|
||||
let gil = Python::acquire_gil();
|
||||
return self
|
||||
.call1(gil.python(), "dy", (i, j, dx))
|
||||
.unwrap()
|
||||
.extract(gil.python())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn sim_exchange(&self, i: u128, j: u128, dx: u128) -> u128 {
|
||||
let gil = Python::acquire_gil();
|
||||
return self
|
||||
.call1(gil.python(), "exchange", (i, j, dx))
|
||||
.unwrap()
|
||||
.extract(gil.python())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn sim_xp(&self) -> Vec<u128> {
|
||||
let gil = Python::acquire_gil();
|
||||
return self
|
||||
.call0(gil.python(), "xp")
|
||||
.unwrap()
|
||||
.extract(gil.python())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn sim_y(&self, i: u128, j: u128, x: u128) -> u128 {
|
||||
let gil = Python::acquire_gil();
|
||||
return self
|
||||
.call1(gil.python(), "y", (i, j, x))
|
||||
.unwrap()
|
||||
.extract(gil.python())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn sim_y_d(&self, i: u128, d: u128) -> u128 {
|
||||
let gil = Python::acquire_gil();
|
||||
return self
|
||||
.call1(gil.python(), "y_D", (i, d))
|
||||
.unwrap()
|
||||
.extract(gil.python())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn sim_remove_liquidity_imbalance(&self, amounts: Vec<u128>) -> u128 {
|
||||
let gil = Python::acquire_gil();
|
||||
return self
|
||||
.call1(
|
||||
gil.python(),
|
||||
"remove_liquidity_imbalance",
|
||||
PyTuple::new(gil.python(), amounts.to_vec()),
|
||||
)
|
||||
.unwrap()
|
||||
.extract(gil.python())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn sim_calc_withdraw_one_coin(&self, token_amount: u128, i: u128) -> u128 {
|
||||
let gil = Python::acquire_gil();
|
||||
return self
|
||||
.call1(gil.python(), "calc_withdraw_one_coin", (token_amount, i))
|
||||
.unwrap()
|
||||
.extract(gil.python())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn call0(&self, py: Python, method_name: &str) -> Result<PyObject, PyErr> {
|
||||
let sim = PyModule::from_code(py, &self.py_src, FILE_NAME, MODULE_NAME).unwrap();
|
||||
let model = sim
|
||||
.getattr("Curve")?
|
||||
.call1((
|
||||
self.amp_factor,
|
||||
self.balances.to_vec(),
|
||||
self.n_coins,
|
||||
self.fee,
|
||||
self.target_prices.to_vec(),
|
||||
self.pool_tokens,
|
||||
))
|
||||
.unwrap()
|
||||
.to_object(py);
|
||||
let py_ret = model.as_ref(py).call_method0(method_name);
|
||||
self.extract_py_ret(py, py_ret)
|
||||
}
|
||||
|
||||
fn call1(
|
||||
&self,
|
||||
py: Python,
|
||||
method_name: &str,
|
||||
args: impl IntoPy<Py<PyTuple>>,
|
||||
) -> Result<PyObject, PyErr> {
|
||||
let sim = PyModule::from_code(py, &self.py_src, FILE_NAME, MODULE_NAME).unwrap();
|
||||
let model = sim
|
||||
.getattr("Curve")?
|
||||
.call1((
|
||||
self.amp_factor,
|
||||
self.balances.to_vec(),
|
||||
self.n_coins,
|
||||
self.fee,
|
||||
self.target_prices.to_vec(),
|
||||
self.pool_tokens,
|
||||
))
|
||||
.unwrap()
|
||||
.to_object(py);
|
||||
let py_ret = model.as_ref(py).call_method1(method_name, args);
|
||||
self.extract_py_ret(py, py_ret)
|
||||
}
|
||||
|
||||
fn extract_py_ret(&self, py: Python, ret: PyResult<&PyAny>) -> Result<PyObject, PyErr> {
|
||||
match ret {
|
||||
Ok(v) => v.extract(),
|
||||
Err(e) => {
|
||||
e.print_and_set_sys_last_vars(py);
|
||||
panic!("Python execution failed.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_src(&self) {
|
||||
println!("{}", self.py_src);
|
||||
}
|
||||
}
|
|
@ -388,7 +388,6 @@ mod tests {
|
|||
RoundDirection, INITIAL_SWAP_POOL_AMOUNT,
|
||||
};
|
||||
use proptest::prelude::*;
|
||||
use sim::StableSwapModel;
|
||||
|
||||
#[test]
|
||||
fn initial_pool_amount() {
|
||||
|
@ -437,54 +436,6 @@ mod tests {
|
|||
assert_eq!(result.destination_amount_swapped, 0);
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn swap_no_fee(
|
||||
swap_source_amount in 100..1_000_000_000_000_000_000u128,
|
||||
swap_destination_amount in 100..1_000_000_000_000_000_000u128,
|
||||
source_amount in 100..100_000_000_000u128,
|
||||
amp in 1..150u64
|
||||
) {
|
||||
prop_assume!(source_amount < swap_source_amount);
|
||||
|
||||
let curve = StableCurve { amp };
|
||||
|
||||
let model: StableSwapModel = StableSwapModel::new(
|
||||
curve.amp.into(),
|
||||
vec![swap_source_amount, swap_destination_amount],
|
||||
N_COINS,
|
||||
);
|
||||
|
||||
let result = curve.swap_without_fees(
|
||||
source_amount,
|
||||
swap_source_amount,
|
||||
swap_destination_amount,
|
||||
TradeDirection::AtoB,
|
||||
);
|
||||
|
||||
let result = result.unwrap();
|
||||
let sim_result = model.sim_exchange(0, 1, source_amount);
|
||||
|
||||
let diff =
|
||||
(sim_result as i128 - result.destination_amount_swapped as i128).abs();
|
||||
|
||||
// tolerate a difference of 2 because of the ceiling during calculation
|
||||
let tolerance = std::cmp::max(2, sim_result as i128 / 1_000_000_000);
|
||||
|
||||
assert!(
|
||||
diff <= tolerance,
|
||||
"result={}, sim_result={}, amp={}, source_amount={}, swap_source_amount={}, swap_destination_amount={}, diff={}",
|
||||
result.destination_amount_swapped,
|
||||
sim_result,
|
||||
amp,
|
||||
source_amount,
|
||||
swap_source_amount,
|
||||
swap_destination_amount,
|
||||
diff
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pack_curve() {
|
||||
let amp = 1;
|
||||
|
|
Loading…
Reference in New Issue