balancer_math.py

This page explains Balancer math coded in Python

Path: balancer_math.py file discussed in this article. This file uses balancer_constants.py as an imported file. Important notice: all swaps in Balancer pools are occurring between two tokens. It means, that if the pool has 8 tokens, all trades are executing between different trading pairs (token1 vs token2, token1 vs token3, etc). When the user sends tokens to the pool for swapping them to other tokens the "i" label refers to "tokens in", or to tokens that were "sold" in this swap. The "o" refers to "tokens out", which means that these tokens were "bought" by the user. The Balancer AMM math from Whitepaper doesn't contain a swap fee/exit fee, but Solidity and Python implementations contain it. The fee is set to zero in the constants (as in the smart contracts)

All methods except spot price return the class BalancerMathResult

@dataclass
class BalancerMathResult:
    # The relevant result of the operation
    result: Decimal
    # Amount of tokens the pool keeps in the token going into the pool (for join and joinswaps) or out the pool (exits)
    fee: Decimal

calc_sport_price

This function returns the spot price based on the ratio of token balances in the pool taking into account their weights:

SPio=B0WiWoBi11swapfeeSP^o_i = { \frac{B_0}{W_i} }\cdot{ \frac{W_o}{B_i} }\cdot{\frac{1}{1-swapfee}}
    def calc_spot_price(
            token_balance_in: Decimal,
            token_weight_in: Decimal,
            token_balance_out: Decimal,
            token_weight_out: Decimal,
            swap_fee: Decimal):
        numer = token_balance_in / token_weight_in
        denom = token_balance_out / token_weight_out
        ratio = numer / denom
        scale = 1 / (1 - swap_fee)
        return ratio * scale

Note, that spot price is the "price without slippage" since the amount of asset for trading is assumed to be very low (lim -> 0).

calc_out_given_in

This function is the function for computing the result of swapping assets. A - amount of tokens (in and out) B - token balance in the pool (for "token in" and "token out") W - weights of these tokens inside the pool (for "token in" and "token out")

A0=B0(1(BiBi+Ai(1swapfee))WiWo)A_{0} = B_{0} \cdot \left(1 – \left(\frac{B_{i}}{B_{i}+A_{i}\cdot(1-swapfee)}\right)^{\frac{W_{i}}{W_{o}}}\right)
@staticmethod
def calc_out_given_in(
            token_amount_in: Decimal,
            token_balance_in: Decimal,
            token_weight_in: Decimal,
            token_balance_out: Decimal,
            token_weight_out: Decimal,
            swap_fee: Decimal) -> BalancerMathResult:
        weight_ratio = token_weight_in / token_weight_out
        adjusted_in = token_amount_in * (1 - swap_fee)
        y = token_balance_in / (token_balance_in + adjusted_in)
        foo = pow(y, weight_ratio)
        bar = 1 - foo
        token_amount_out = token_balance_out * bar
        return BalancerMathResult(token_amount_out, token_amount_in - adjusted_in)

calc_in_given_out

This formula is used for the calculation of how many tokens "in" you need to send to the pool to receive the desired number of tokens "out" back.

Ai=Bi((BoBoAo)WoWi1)11swapfeeA_{i} = B_{i} \cdot \left(\left(\frac{B_{o}}{B_{o}-A_{o}}\right)^{\frac{W_{o}}{W_{i}}}-1\right)\cdot\frac{1}{1-swapfee}
    @staticmethod
    def calc_in_given_out(
            token_balance_out: Decimal,
            token_balance_in: Decimal,
            token_amount_out: Decimal,
            token_weight_in: Decimal,
            token_weight_out: Decimal,
            swap_fee: Decimal):
        weight_ratio = token_weight_out / token_weight_in
        diff = token_balance_out - token_amount_out
        y = token_balance_out / diff
        foo = pow(y, weight_ratio)
        foo = foo - 1
        fee_adjustment = 1 - swap_fee
        token_amount_in_no_fee = (token_balance_in * foo)
        token_amount_in = token_amount_in_no_fee / fee_adjustment

        return BalancerMathResult(token_amount_in, token_amount_in - token_amount_in_no_fee)

calc_pool_out_given_single_in

This function returns the number of pool shares (or BPT tokens) issued as a result of single-side liquidity provision (join a pool with a specified number of "token in" asset). Bt is a pool balance, and At is the amount of this token deposited to the pool.

Pissued=Psupply((1+At(1(1WiWt)swapfee)+BtBt)WiWt1)P_{issued} = P_{supply} \cdot \left(\left(1+\frac{A_t\cdot(1-(1-\frac{W_i}{W_t})\cdot swapfee)+B_t}{B_t}\right)^\frac{W_i}{W_t} -1\right)
    @staticmethod
    def calc_pool_out_given_single_in(
            token_balance_in: Decimal,
            token_weight_in: Decimal,
            pool_supply: Decimal,
            total_weight: Decimal,
            token_amount_in: Decimal,
            swap_fee: Decimal):
        # Charge the trading fee for the proportion of tokenAi
        #  which is implicitly traded to the other pool tokens.
        # That proportion is (1- weightTokenIn)
        # tokenAiAfterFee = tAi * (1 - (1-weightTi) * poolFee)
        normalized_weight = token_weight_in / total_weight
        zaz = (BONE - normalized_weight) * swap_fee
        token_amount_in_after_fee = token_amount_in * (BONE - zaz)

        new_token_balance_in = token_balance_in + token_amount_in_after_fee
        token_in_ratio = new_token_balance_in / token_balance_in

        # new_pool_supply = (ratio_ti ^ weight_ti) * pool_supply
        pool_ratio = pow(token_in_ratio, normalized_weight)
        new_pool_supply = pool_ratio * pool_supply
        pool_amount_out = new_pool_supply - pool_supply
        return BalancerMathResult(pool_amount_out, token_amount_in - token_amount_in_after_fee)

calc_single_in_given_pool_out

This is another single-side liquidity provision function. It returns the number of At (token in) that is necessary to supply for minting a specified number of Pool Shares.

At=Bt(1(1PissuedPsupply)WtWi)1(1WiWt)swapfeeA_t = B_t \cdot \left(1-\left(1-\frac{P_{issued}}{P_{supply}}\right)^\frac{W_t}{W_i}\right)\cdot\frac{1}{(1-\frac{W_i}{W_t})\cdot swapfee}
    @staticmethod
    def calc_single_in_given_pool_out(
            token_balance_in: Decimal,
            token_weight_in: Decimal,
            pool_supply: Decimal,
            total_weight: Decimal,
            pool_amount_out: Decimal,
            swap_fee: Decimal):
        normalized_weight = token_weight_in / total_weight
        new_pool_supply = pool_supply + pool_amount_out
        pool_ratio = new_pool_supply / pool_supply
        # newBalTi = pool_ratio^(1/weightTi) * balTi
        boo = 1 / normalized_weight
        token_ratio = pow(pool_ratio, boo)
        new_token_balance_in = token_ratio * token_balance_in
        token_amount_in_after_fee = new_token_balance_in - token_balance_in
        # Do reverse order of fees charged in joinswap_ExternAmountIn, this way
        #     ``` pAo == joinswap_ExternAmountIn(Ti, joinswap_pool_amount_out(pAo, Ti)) ```
        # tAi = tAiAfterFee / (1 - (1-weightTi) * swap_fee)
        zar = ((1 - normalized_weight) * swap_fee)
        token_amount_in = token_amount_in_after_fee / (1 - zar)
        return BalancerMathResult(token_amount_in, token_amount_in - token_amount_in_after_fee)

calc_single_out_given_pool_in

This function returns the number of "token out" for a given number of redeeming Pool Shares (or BPT tokens).

fee = token_amount_out_before_swap_fee - token_amount_out

At=Bt(1(1PredeemedPsupply)1Wt)A_t = B_t \cdot \left(1-\left(1-\frac{P_{redeemed}}{P_{supply}}\right)^\frac{1}{W_t}\right)
    @staticmethod
    def calc_single_out_given_pool_in(
            token_balance_out: Decimal,
            token_weight_out: Decimal,
            pool_supply: Decimal,
            total_weight: Decimal,
            pool_amount_in: Decimal,
            swap_fee: Decimal
    ):
        normalized_weight = token_weight_out / total_weight
        # charge exit fee on the pool token side
        # pAiAfterExitFee = pAi*(1-exitFee)

        pool_amount_in_after_exit_fee = pool_amount_in * (1 - balancer_constants.EXIT_FEE)
        new_pool_supply = pool_supply - pool_amount_in_after_exit_fee
        pool_ratio = new_pool_supply / pool_supply
        # newBalTo = pool_ratio ^ (1 / weightTo) * balTo
        token_out_ratio = pow(pool_ratio, (balancer_constants.BONE / normalized_weight))
        new_token_balance_out = token_out_ratio * token_balance_out
        token_amount_out_before_swap_fee = token_balance_out - new_token_balance_out
        # charge swap fee on the output token side
        # tAo = tAoBeforeswap_fee * (1 - (1-weightTo) * swap_fee)
        zaz = (balancer_constants.BONE - normalized_weight) * swap_fee
        token_amount_out = token_amount_out_before_swap_fee * (balancer_constants.BONE - zaz)
        return BalancerMathResult(token_amount_out, token_amount_out_before_swap_fee - token_amount_out)

calc_pool_in_given_single_out

This function returns the number of Pool Shares (or BPT tokens) that should be redeemed in order to receive the specified number of the "token out".

    @staticmethod
    def calc_pool_in_given_single_out(
            token_balance_out: Decimal,
            token_weight_out: Decimal,
            pool_supply: Decimal,
            total_weight: Decimal,
            token_amount_out: Decimal,
            swap_fee: Decimal
    ):
        # charge swap fee on the output token side
        normalized_weight = token_weight_out / total_weight
        # tAoBeforeswap_fee = tAo / (1 - (1-weightTo) * swap_fee) 
        zoo = balancer_constants.BONE - normalized_weight
        zar = zoo * swap_fee
        token_amount_out_before_swap_fee = (token_amount_out / (balancer_constants.BONE - zar))
        newtoken_balance_out = token_balance_out - token_amount_out_before_swap_fee
        token_out_ratio = newtoken_balance_out / token_balance_out

        # newpool_supply = (ratioTo ^ weightTo) * pool_supply
        poolRatio = pow(token_out_ratio, normalized_weight)
        newpool_supply = poolRatio * pool_supply
        pool_amount_in_after_exit_fee = pool_supply - newpool_supply

        # charge exit fee on the pool token side
        # pAi = pAiAfterExitFee/(1-exitFee)
        pool_amount_in = pool_amount_in_after_exit_fee / (balancer_constants.BONE - balancer_constants.EXIT_FEE)
        return BalancerMathResult(pool_amount_in, token_amount_out_before_swap_fee - token_amount_out)

Last updated