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)
Exemple #6
0
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