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)