def test_against_simulation(alice, swap, n_coins, pool_token, idx): amount = pool_token.balanceOf(alice) balances = [swap.balances(i) for i in range(n_coins)] curve_model = Curve(swap.A(), balances, n_coins, tokens=pool_token.totalSupply()) expected, _ = curve_model.calc_withdraw_one_coin(amount, idx) assert swap.calc_withdraw_one_coin(amount, idx) == expected
def test_curve_in_contract(alice, swap, wrapped_coins, st_seed_amount, n_coins, approx, st_pct, wrapped_decimals): st_seed_amount = int(10**st_seed_amount) # add initial pool liquidity # we add at an imbalance of +10% for each subsequent coin initial_liquidity = [] for coin, decimals in zip(wrapped_coins, wrapped_decimals): amount = st_seed_amount * 10**decimals + 1 amount = int(amount * (1 + 0.1 * len(initial_liquidity))) coin._mint_for_testing(alice, amount, {"from": alice}) assert coin.balanceOf(alice) >= amount initial_liquidity.append(amount // 10) coin.approve(swap, amount // 10, {"from": alice}) swap.add_liquidity(initial_liquidity, 0, {"from": alice}) # initialize our python model using the same parameters as the contract balances = [swap.balances(i) for i in range(n_coins)] rates = [] for decimals in wrapped_decimals: rate = 10**18 precision = 10**(18 - decimals) rates.append(rate * precision) curve_model = Curve(2 * 360, balances, n_coins, rates) # execute a series of swaps and compare the python model to the contract results rates = [10**18 for i in range(n_coins)] exchange_pairs = deque(permutations(range(n_coins), 2)) while st_pct: exchange_pairs.rotate() send, recv = exchange_pairs[0] dx = int(2 * st_seed_amount * 10**wrapped_decimals[send] * st_pct.pop()) dy_1_c = swap.get_dy(send, recv, dx) dy_1_u = swap.get_dy_underlying(send, recv, dx) assert dy_1_u == dy_1_c dy_2 = curve_model.dy(send, recv, dx * (10**(18 - wrapped_decimals[send]))) dy_2 //= 10**(18 - wrapped_decimals[recv]) assert approx(dy_1_c, dy_2, 1e-8) or abs(dy_1_c - dy_2) <= 2
def test_curve_in_contract( alice, swap, wrapped_coins, wrapped_decimals, underlying_decimals, n_coins, approx, st_seed_amount, st_pct, ): st_seed_amount = int(10**st_seed_amount) # add initial pool liquidity # we add at an imbalance of +10% for each subsequent coin initial_liquidity = [] for coin, decimals in zip(wrapped_coins, wrapped_decimals): amount = st_seed_amount * 10**decimals + 1 coin._mint_for_testing(alice, amount, {'from': alice}) if hasattr(coin, 'set_exchange_rate'): rate = int(coin.get_rate() * (1 + 0.1 * len(initial_liquidity))) coin.set_exchange_rate(rate, {'from': alice}) assert coin.balanceOf(alice) >= amount initial_liquidity.append(amount // 10) coin.approve(swap, amount // 10, {'from': alice}) swap.add_liquidity(initial_liquidity, 0, {'from': alice}) # initialize our python model using the same parameters as the contract balances = [swap.balances(i) for i in range(n_coins)] rates = [] for coin, decimals in zip(wrapped_coins, underlying_decimals): if hasattr(coin, 'get_rate'): rate = coin.get_rate() else: rate = 10**18 precision = 10**(18 - decimals) rates.append(rate * precision) curve_model = Curve(2 * 360, balances, n_coins, rates) # execute a series of swaps and compare the python model to the contract results rates = [ wrapped_coins[i].get_rate() if hasattr(wrapped_coins[i], "get_rate") else 10**18 for i in range(n_coins) ] exchange_pairs = deque(permutations(range(n_coins), 2)) while st_pct: exchange_pairs.rotate() send, recv = exchange_pairs[0] dx = int(2 * st_seed_amount * 10**underlying_decimals[send] * st_pct.pop()) dx_c = dx * 10**18 // rates[send] dy_1_c = swap.get_dy(send, recv, dx_c) if hasattr(swap, "get_dy_underlying"): dy_1_u = swap.get_dy_underlying(send, recv, dx) else: dy_1_u = dy_1_c dy_1 = dy_1_c * rates[recv] // 10**18 dy_2 = curve_model.dy(send, recv, dx * (10**(18 - underlying_decimals[send]))) dy_2 //= (10**(18 - underlying_decimals[recv])) assert approx(dy_1, dy_2, 1e-8) or abs(dy_1 - dy_2) <= 2 assert approx(dy_1_u, dy_2, 1e-8) or abs(dy_1 - dy_2) <= 2
def test_simulated_exchange( chain, alice, bob, underlying_coins, wrapped_coins, underlying_decimals, wrapped_decimals, swap, n_coins, st_coin, st_divisor, ): """ Perform a series of token swaps and compare the resulting amounts and pool balances with those in our python-based model. Strategies ---------- st_coin : decimal[100] Array of decimal values, used to choose the coins used in each swap st_divisor: uint[50] Array of integers, used to choose the size of each swap """ # initialize our python model using the same parameters as the contract balances = [swap.balances(i) for i in range(n_coins)] rates = [] for coin, decimals in zip(wrapped_coins, wrapped_decimals): if hasattr(coin, 'get_rate'): rate = coin.get_rate() else: rate = 10**18 precision = 10**(18 - decimals) rates.append(rate * precision) curve_model = Curve(2 * 360, balances, n_coins, rates) # Start trading! rate_mul = [10**i for i in underlying_decimals] while st_coin: # Tune exchange rates for i, (coin, decimals) in enumerate(zip(wrapped_coins, underlying_decimals)): if hasattr(coin, 'get_rate'): rate = int(coin.get_rate() * 1.0001) coin.set_exchange_rate(rate, {'from': alice}) curve_model.p[i] = rate * (10**(18 - decimals)) chain.sleep(3600) # Simulate the exchange old_virtual_price = swap.get_virtual_price() # choose which coins to swap send, recv = [int(st_coin.pop() * n_coins) for _ in range(2)] if send == recv: # if send and recv are the same, adjust send send = abs(send - 1) value = 5 * rate_mul[send] // st_divisor.pop() x_0 = underlying_coins[send].balanceOf(bob) y_0 = underlying_coins[recv].balanceOf(bob) underlying_coins[send].approve(swap, 0, {'from': bob}) underlying_coins[send].approve(swap, value, {'from': bob}) amount = int(0.5 * value * rate_mul[recv] / rate_mul[send]) swap.exchange_underlying(send, recv, value, amount, {'from': bob}) x_1 = underlying_coins[send].balanceOf(bob) y_1 = underlying_coins[recv].balanceOf(bob) dy_m = curve_model.exchange(send, recv, value * max(rate_mul) // rate_mul[send]) dy_m = dy_m * rate_mul[recv] // max(rate_mul) assert x_0 - x_1 == value assert (y_1 - y_0) - dy_m <= dy_m * 1e-10 assert swap.get_virtual_price() > old_virtual_price assert wrapped_coins[send].balanceOf(swap) >= swap.balances(send) assert wrapped_coins[recv].balanceOf(swap) >= swap.balances(recv) # Final assertions - let's see what we have left final_balances = [swap.balances(i) for i in range(n_coins)] final_total = sum(final_balances[i] * rates[i] / 1e18 for i in range(n_coins)) assert [round(a / b, 6) for a, b in zip(final_balances, curve_model.x)] == [1.0] * n_coins assert final_total > n_coins * 100 * max(rate_mul)
def test_simulated_exchange( chain, alice, bob, underlying_coins, wrapped_coins, wrapped_decimals, swap, pool_data, n_coins, set_fees, st_coin, st_divisor, ): """ Perform a series of token swaps and compare the resulting amounts and pool balances with those in our python-based model. Strategies ---------- st_coin : decimal[100] Array of decimal values, used to choose the coins used in each swap st_divisor: uint[50] Array of integers, used to choose the size of each swap """ set_fees(10**7, 0) # add initial pool liquidity initial_liquidity = [] for underlying, decimals in zip(underlying_coins, wrapped_decimals): amount = 1000 * 10**decimals underlying._mint_for_testing(alice, amount, {"from": alice}) underlying.approve(swap, amount, {"from": alice}) initial_liquidity.append(amount // 10) swap.add_liquidity(initial_liquidity, 0, True, {"from": alice}) # initialize our python model using the same parameters as the contract balances = [swap.balances(i) for i in range(n_coins)] rates = [] for decimals in wrapped_decimals: rate = 10**18 precision = 10**(18 - decimals) rates.append(rate * precision) curve_model = Curve(2 * 360, balances, n_coins, rates) for coin, decimals in zip(underlying_coins, wrapped_decimals): # Fund bob with $100 of each coin and approve swap contract amount = 100 * 10**decimals coin._mint_for_testing(bob, amount, {"from": alice}) coin.approve(swap, amount, {"from": bob}) # Start trading! rate_mul = [10**i for i in wrapped_decimals] while st_coin: # Increase aToken balances by 1% to simulate accrued interest for i, coin in enumerate(wrapped_coins): coin._mint_for_testing(swap, coin.balanceOf(swap) // 100, {"from": alice}) curve_model.x[i] = int(curve_model.x[i] * 1.01) # Simulate the exchange old_virtual_price = swap.get_virtual_price() # choose which coins to swap send, recv = [int(st_coin.pop() * n_coins) for _ in range(2)] if send == recv: # if send and recv are the same, adjust send send = abs(send - 1) value = 5 * rate_mul[send] // st_divisor.pop() x_0 = underlying_coins[send].balanceOf(bob) y_0 = underlying_coins[recv].balanceOf(bob) underlying_coins[send].approve(swap, 0, {"from": bob}) underlying_coins[send].approve(swap, value, {"from": bob}) amount = int(0.5 * value * rate_mul[recv] / rate_mul[send]) swap.exchange_underlying(send, recv, value, amount, {"from": bob}) x_1 = underlying_coins[send].balanceOf(bob) y_1 = underlying_coins[recv].balanceOf(bob) dy_m = curve_model.exchange(send, recv, value * max(rate_mul) // rate_mul[send]) dy_m = dy_m * rate_mul[recv] // max(rate_mul) assert x_0 - x_1 == value assert (y_1 - y_0) - dy_m <= dy_m * 1e-10 assert swap.get_virtual_price() > old_virtual_price assert wrapped_coins[send].balanceOf(swap) >= swap.balances(send) assert wrapped_coins[recv].balanceOf(swap) >= swap.balances(recv) # Final assertions - let's see what we have left final_balances = [swap.balances(i) for i in range(n_coins)] final_total = sum(final_balances[i] * rates[i] / 1e18 for i in range(n_coins)) assert [round(a / b, 6) for a, b in zip(final_balances, curve_model.x)] == [1.0] * n_coins assert final_total > n_coins * 100 * max(rate_mul)
def test_curve_in_contract( alice, swap, underlying_coins, wrapped_coins, st_seed_amount, pool_data, n_coins, approx, st_pct ): coin_data = pool_data['coins'] st_seed_amount = int(10 ** st_seed_amount) # add initial pool liquidity # we add at an imbalance of +10% for each subsequent coin initial_liquidity = [] for underlying, wrapped, data in zip(underlying_coins, wrapped_coins, coin_data): amount = st_seed_amount * 10 ** (data['decimals'] + 1) underlying._mint_for_testing(alice, amount, {'from': alice}) if data['wrapped']: rate = int(10**18 * (1 + 0.1 * len(initial_liquidity))) wrapped.set_exchange_rate(rate, {'from': alice}) underlying.approve(wrapped, amount, {'from': alice}) wrapped.mint(amount, {'from': alice}) amount = amount * 10 ** 18 // rate assert wrapped.balanceOf(alice) >= amount initial_liquidity.append(amount // 10) wrapped.approve(swap, amount // 10, {'from': alice}) swap.add_liquidity(initial_liquidity, 0, {'from': alice}) # initialize our python model using the same parameters as the contract balances = [swap.balances(i) for i in range(n_coins)] rates = [] for (coin, data) in zip(wrapped_coins, pool_data['coins']): if data['wrapped']: rate = coin.get_rate() else: rate = 10 ** 18 precision = 10 ** (18-data['decimals']) rates.append(rate * precision) curve_model = Curve(2 * 360, balances, n_coins, rates) # execute a series of swaps and compare the python model to the contract results rates = [ wrapped_coins[i].get_rate() if coin_data[i]['wrapped'] else 10 ** 18 for i in range(n_coins) ] exchange_pairs = deque(permutations(range(n_coins), 2)) while st_pct: exchange_pairs.rotate() send, recv = exchange_pairs[0] dx = int(2 * st_seed_amount * 10 ** coin_data[send]['decimals'] * st_pct.pop()) dx_c = dx * 10 ** 18 // rates[send] dy_1_c = swap.get_dy(send, recv, dx_c) dy_1_u = swap.get_dy_underlying(send, recv, dx) dy_1 = dy_1_c * rates[recv] // 10 ** 18 dy_2 = curve_model.dy(send, recv, dx * (10 ** (18 - coin_data[send]['decimals']))) dy_2 //= (10 ** (18 - coin_data[recv]['decimals'])) assert approx(dy_1, dy_2, 1e-8) or abs(dy_1 - dy_2) <= 2 assert approx(dy_1_u, dy_2, 1e-8) or abs(dy_1 - dy_2) <= 2