def s_join_swap_pool_amount_out(params, step, history, current_state, input_params: JoinSwapPoolAmountOutInput, output_params: JoinSwapPoolAmountOutOutput): pool = current_state['pool'].copy() max_token_in_amount = input_params.max_token_in.symbol tokens_in_symbol = input_params.max_token_in.symbol pool_amount_out = input_params.pool_amount_out swap_fee = pool['swap_fee'] total_weight = calculate_total_denorm_weight(pool) join_swap = BalancerMath.calc_single_in_given_pool_out( token_balance_in=Decimal(pool['tokens'][tokens_in_symbol].balance), token_weight_in=Decimal( pool['tokens'][tokens_in_symbol].denorm_weight), pool_supply=Decimal(pool['pool_shares']), total_weight=total_weight, pool_amount_out=pool_amount_out, swap_fee=Decimal(swap_fee)) _, pool = s_pool_update_fee(pool, {tokens_in_symbol: join_swap.fee}) token_in_amount = join_swap.result if token_in_amount > max_token_in_amount and VERBOSE: print( "WARNING: calculated that user should get {} pool shares but input specified that he should get {} pool shares instead." .format(pool_amount_out, max_token_in_amount)) pool['pool_shares'] = Decimal(pool['pool_shares']) + pool_amount_out pool['tokens'][tokens_in_symbol].balance += token_in_amount return pool
def s_swap_exact_amount_in(params, step, history, current_state, input_params: SwapExactAmountInInput, output_params: SwapExactAmountInOutput) -> dict: pool = current_state['pool'].copy() # Parse action params token_in_symbol = input_params.token_in.symbol token_amount_in = input_params.token_in.amount token_out = input_params.min_token_out.symbol pool_token_in = pool['tokens'][token_in_symbol] swap_fee = pool['swap_fee'] if not pool_token_in.bound: raise Exception('ERR_NOT_BOUND') out_record = pool['tokens'][token_out] if not out_record.bound: raise Exception('ERR_NOT_BOUND') if token_amount_in > Decimal(pool_token_in.balance) * MAX_IN_RATIO: raise Exception("ERR_MAX_IN_RATIO") swap_result = BalancerMath.calc_out_given_in( token_balance_in=pool_token_in.balance, token_weight_in=Decimal(pool_token_in.denorm_weight), token_balance_out=out_record.balance, token_weight_out=Decimal(out_record.denorm_weight), token_amount_in=token_amount_in, swap_fee=Decimal(swap_fee)) _, pool = s_pool_update_fee(pool, {token_in_symbol: swap_result.fee}) pool_in_balance = pool_token_in.balance + token_amount_in pool_token_in.balance = pool_in_balance pool_out_balance = out_record.balance - swap_result.result out_record.balance = pool_out_balance return pool
def test_calc_spot_price_50_50_no_fee(self): w_50_50_no_fee_1 = { 'tb_i': Decimal('10'), 'tw_i': Decimal('20'), 'tb_o': Decimal('5'), 'tw_o': Decimal('20'), 'fee': Decimal('0'), 'price': Decimal('2') } price = BalancerMath.calc_spot_price(token_balance_in=w_50_50_no_fee_1['tb_i'], token_weight_in=w_50_50_no_fee_1['tw_i'], token_balance_out=w_50_50_no_fee_1['tb_o'], token_weight_out=w_50_50_no_fee_1['tw_o'], swap_fee=w_50_50_no_fee_1['fee']) self.assertEqual(price, w_50_50_no_fee_1['price'])
def test_calc_spot_price_lbp_sim(self): # https://docs.google.com/spreadsheets/d/1t6VsMJF8lh4xuH_rfPNdT5DM3nY4orF9KFOj2HdMmuY/edit#gid=1392289526 params = { 'tb_i': Decimal('1333333'), 'tw_i': Decimal('4'), 'tb_o': Decimal('7500000'), 'tw_o': Decimal('36'), 'fee': Decimal('0.0001'), 'price': Decimal('1.6') } price = BalancerMath.calc_spot_price(token_balance_in=params['tb_i'], token_weight_in=params['tw_i'], token_balance_out=params['tb_o'], token_weight_out=params['tw_o'], swap_fee=params['fee']) self.assertAlmostEqual(price, params['price'], 3)
def s_exit_swap_extern_amount_out( params, step, history, current_state, input_params: ExitSwapPoolExternAmountOutInput, output_params: ExitSwapPoolExternAmountOutOutput) -> dict: """ Exit a pool by withdrawing liquidity for a single token_symbol. """ pool = current_state['pool'].copy() swap_fee = pool['swap_fee'] token_out_symbol = input_params.token_out.symbol # Check that all tokens_out are bound if not pool['tokens'][token_out_symbol].bound: raise Exception("ERR_NOT_BOUND") # Check that user is not trying to withdraw too many tokens token_amount_out = input_params.token_out.amount if token_amount_out > Decimal( pool['tokens'][token_out_symbol].balance) * MAX_OUT_RATIO: raise Exception("ERR_MAX_OUT_RATIO") total_weight = calculate_total_denorm_weight(pool) exit_swap = BalancerMath.calc_pool_in_given_single_out( token_balance_out=Decimal(pool['tokens'][token_out_symbol].balance), token_weight_out=Decimal( pool['tokens'][token_out_symbol].denorm_weight), pool_supply=Decimal(pool['pool_shares']), total_weight=Decimal(total_weight), token_amount_out=token_amount_out, swap_fee=Decimal(swap_fee)) pool_amount_in = exit_swap.result _, pool = s_pool_update_fee(pool, {token_out_symbol: exit_swap.fee}) if pool_amount_in == 0: raise Exception("ERR_MATH_APPROX") if pool_amount_in != output_params.pool_amount_in and VERBOSE: print( "WARNING: calculated that pool should get {} pool shares but input specified that pool should get {} pool shares instead" .format(pool_amount_in, output_params.pool_amount_in)) # Decrease token_symbol (give it to user) pool['tokens'][token_out_symbol].balance -= token_amount_out # Burn the user's incoming pool shares - exit fee exit_fee = pool_amount_in * EXIT_FEE pool['pool_shares'] = Decimal( pool['pool_shares']) - pool_amount_in - exit_fee return pool
def test_calc_out_given_in_fee(self): params = { 'ta_i': Decimal('1'), 'tb_i': Decimal('10'), 'tw_i': Decimal('10'), 'tb_o': Decimal('100'), 'tw_o': Decimal('30'), 'fee': Decimal('0.1'), 'ta_o': Decimal('2.8317232565404336') } token_amount_out = BalancerMath.calc_out_given_in(token_amount_in=params['ta_i'], token_weight_in=params['tw_i'], token_balance_in=params['tb_i'], token_weight_out=params['tw_o'], token_balance_out=params['tb_o'], swap_fee=params['fee']) self.assertAlmostEqual(token_amount_out.fee, Decimal('0.1')) self.assertAlmostEqual(token_amount_out.result, params['ta_o'], 7)
def test_calc_out_given_in_no_fee(self): params = { 'ta_i': Decimal('1'), 'tb_i': Decimal('10'), 'tw_i': Decimal('20'), 'tb_o': Decimal('100'), 'tw_o': Decimal('20'), 'fee': Decimal('0'), 'ta_o': Decimal('9.0909090909090909') } token_amount_out = BalancerMath.calc_out_given_in(token_amount_in=params['ta_i'], token_weight_in=params['tw_i'], token_balance_in=params['tb_i'], token_weight_out=params['tw_o'], token_balance_out=params['tb_o'], swap_fee=params['fee']) self.assertAlmostEqual(token_amount_out.fee, Decimal('0'), 3) self.assertAlmostEqual(token_amount_out.result, params['ta_o'], 3)
def test_calc_in_given_out(self): params = { 'ta_o': Decimal('1'), 'tb_i': Decimal('10'), 'tw_i': Decimal('10'), 'tb_o': Decimal('100'), 'tw_o': Decimal('30'), 'fee': Decimal('0.1'), 'ta_i': Decimal('0.340112801426272844') } token_amount_in = BalancerMath.calc_in_given_out(token_amount_out=params['ta_o'], token_weight_in=params['tw_i'], token_balance_in=params['tb_i'], token_weight_out=params['tw_o'], token_balance_out=params['tb_o'], swap_fee=params['fee']) self.assertAlmostEqual(token_amount_in.fee, Decimal('0.0340112801426272840754356244'), 7) self.assertAlmostEqual(token_amount_in.result, params['ta_i'], 7)
def test_calc_pool_out_given_single_in(self): params = { 'tb_i': Decimal('471000'), 'tw_i': Decimal('36'), 't_w': Decimal('40'), 'p_s': Decimal('100'), 'pa_o': Decimal('0.0019106349146009'), 'ta_i': Decimal('10'), 'fee': Decimal('0.001') } pool_amount_out = BalancerMath.calc_pool_out_given_single_in(token_balance_in=params['tb_i'], token_weight_in=params['tw_i'], pool_supply=params['p_s'], total_weight=params['t_w'], token_amount_in=params['ta_i'], swap_fee=params['fee']) self.assertAlmostEqual(pool_amount_out.fee, Decimal('0.001'), 7) self.assertAlmostEqual(pool_amount_out.result, params['pa_o'], 7)
def test_calc_single_in_given_pool_out(self): params = { 'tb_i': Decimal('471000'), 'tw_i': Decimal('36'), 'p_s': Decimal('100'), 't_w': Decimal('40'), 'pa_o': Decimal('10'), 'ta_i': Decimal('52621.106362779467365737'), 'fee': Decimal('0.001') } token_amount_in = BalancerMath.calc_single_in_given_pool_out(token_balance_in=params['tb_i'], token_weight_in=params['tw_i'], pool_supply=params['p_s'], total_weight=params['t_w'], pool_amount_out=params['pa_o'], swap_fee=params['fee']) self.assertAlmostEqual(token_amount_in.fee, Decimal('5.26211063623975586820477'), 7) self.assertAlmostEqual(token_amount_in.result, params['ta_i'], 5)
def generate_initial_state(initial_values_json: str, spot_price_base_currency: str) -> typing.Dict: with open(initial_values_json, "r") as f: initial_values = json.load(f, object_hook=token_finding_hook) # Figure out the tokens that are NOT the spot_price_base_currency other_tokens = [*initial_values['pool']['tokens'].keys()] other_tokens.remove(spot_price_base_currency) spot_prices = {} for t in other_tokens: base_token = initial_values['pool']['tokens'][spot_price_base_currency] other_token = initial_values['pool']['tokens'][t] spot_prices[t] = BalancerMath.calc_spot_price(token_balance_in=base_token.balance, token_weight_in=Decimal(base_token.denorm_weight), token_balance_out=other_token.balance, token_weight_out=Decimal(other_token.denorm_weight), swap_fee=Decimal(initial_values['pool']['swap_fee'])) initial_values["spot_prices"] = spot_prices return initial_values
def calculate_spot_prices(pool: dict, ref_token: str): swap_fee = pool['swap_fee'] balance_in = pool['tokens'][ref_token].balance weight_in = pool['tokens'][ref_token].weight spot_prices = {} for token in pool['tokens']: if token == ref_token: continue balance_out = pool['tokens'][token].balance weight_out = pool['tokens'][token].weight price = BalancerMath.calc_spot_price( token_balance_in=Decimal(balance_in), token_weight_in=Decimal(weight_in), token_balance_out=Decimal(balance_out), token_weight_out=Decimal(weight_out), swap_fee=Decimal(swap_fee)) spot_prices[token] = price return spot_prices
def test_calc_single_out_given_pool_in(self): params = { 'tb_o': Decimal('471000'), 'tw_o': Decimal('36'), 'p_s': Decimal('100'), 't_w': Decimal('40'), 'pa_i': Decimal('10'), 'ta_o': Decimal('52028.342757248973119087'), 'fee': Decimal('0.001') } token_amount_out = BalancerMath.calc_single_out_given_pool_in(token_balance_out=params['tb_o'], token_weight_out=params['tw_o'], pool_supply=params['p_s'], total_weight=params['t_w'], pool_amount_in=params['pa_i'], swap_fee=params['fee']) self.assertAlmostEqual(token_amount_out.fee, Decimal('5.20335461122343328240886'), 7) self.assertAlmostEqual(token_amount_out.result, params['ta_o'], 6)
def s_swap_exact_amount_out(params, step, history, current_state, input_params: SwapExactAmountOutInput, output_params: SwapExactAmountOutOutput) -> dict: pool = current_state['pool'].copy() # Parse action params token_in_symbol = input_params.max_token_in.symbol token_amount_out = input_params.token_out.amount token_out_symbol = input_params.token_out.symbol pool_token_out = pool['tokens'][token_out_symbol] pool_token_in = pool['tokens'][token_in_symbol] swap_fee = pool['swap_fee'] if not pool_token_out.bound: raise Exception('ERR_NOT_BOUND') if not pool_token_in.bound: raise Exception('ERR_NOT_BOUND') if token_amount_out > (pool_token_out.balance * MAX_OUT_RATIO): raise Exception("ERR_MAX_OUT_RATIO") swap_result = BalancerMath.calc_in_given_out( token_balance_in=Decimal(pool_token_in.balance), token_weight_in=Decimal(pool_token_in.denorm_weight), token_balance_out=Decimal(pool_token_out.balance), token_weight_out=Decimal(pool_token_out.denorm_weight), token_amount_out=token_amount_out, swap_fee=Decimal(swap_fee)) token_amount_in = swap_result.result if token_amount_in > input_params.max_token_in.amount and VERBOSE: # raise Exception('ERR_LIMIT_IN') print( f"WARNING: token_amount_in {token_amount_in} > max {input_params.max_token_in.amount}" ) _, pool = s_pool_update_fee(pool, {token_in_symbol: swap_result.fee}) pool_token_in.balance = pool_token_in.balance + token_amount_in pool_token_out.balance = pool_token_out.balance - token_amount_out return pool
def s_exit_swap_pool_amount_in( params, step, history, current_state, input_params: ExitSwapPoolAmountInInput, output_params: ExitSwapPoolAmountInOutput) -> dict: pool = current_state['pool'].copy() swap_fee = pool['swap_fee'] pool_token_out = pool['tokens'][output_params.token_out.symbol] if not pool_token_out.bound: raise Exception("ERR_NOT_BOUND") pool_amount_in = input_params.pool_amount_in total_weight = calculate_total_denorm_weight(pool) exit_swap = BalancerMath.calc_single_out_given_pool_in( token_balance_out=Decimal(pool_token_out.balance), token_weight_out=Decimal(pool_token_out.denorm_weight), pool_supply=Decimal(pool['pool_shares']), total_weight=Decimal(total_weight), pool_amount_in=pool_amount_in, swap_fee=Decimal(swap_fee)) token_amount_out = exit_swap.result if token_amount_out > pool_token_out.balance * MAX_OUT_RATIO: raise Exception("ERR_MAX_OUT_RATIO") generated_fees = pool['generated_fees'] generated_fees[output_params.token_out.symbol] = Decimal( generated_fees[output_params.token_out.symbol]) + exit_swap.fee pool['tokens'][output_params.token_out.symbol].balance -= token_amount_out # Burn the user's incoming pool shares - exit fee exit_fee = pool_amount_in * EXIT_FEE pool['pool_shares'] = Decimal( pool['pool_shares']) - pool_amount_in - exit_fee return pool
def s_join_swap_extern_amount_in( params, step, history, current_state, input_params: JoinSwapExternAmountInInput, output_params: JoinSwapExternAmountInOutput) -> dict: """ Join a pool by providing liquidity for a single token_symbol. """ pool = current_state['pool'].copy() tokens_in_symbol = input_params.token_in.symbol token_in_amount = input_params.token_in.amount pool_amount_out_expected = output_params.pool_amount_out swap_fee = pool['swap_fee'] total_weight = calculate_total_denorm_weight(pool) join_swap = BalancerMath.calc_pool_out_given_single_in( token_balance_in=Decimal(pool['tokens'][tokens_in_symbol].balance), token_weight_in=Decimal( pool['tokens'][tokens_in_symbol].denorm_weight), pool_supply=Decimal(pool['pool_shares']), total_weight=total_weight, token_amount_in=token_in_amount, swap_fee=Decimal(swap_fee)) _, pool = s_pool_update_fee(pool, {tokens_in_symbol: join_swap.fee}) pool_amount_out = join_swap.result if pool_amount_out != pool_amount_out_expected and VERBOSE: print( "WARNING: calculated that user should get {} pool shares but input specified that he should get {} pool shares instead." .format(pool_amount_out, pool_amount_out_expected)) pool['pool_shares'] = Decimal(pool['pool_shares']) + pool_amount_out pool['tokens'][tokens_in_symbol].balance += token_in_amount return pool