How rebalancing works?

The function responsible for the rebalancing behavior in StableSwap AMM is located in the addLiquidity function of the Swap.sol contract. This function utilizes the logic defined in the SwapUtils.sol library to calculate how deposits affect the pool's balance and invariant.

function calculateTokenAmount(
        Swap storage self,
        uint256[] calldata amounts,
        bool deposit
    ) external view returns (uint256) {
        uint256 a = _getAPrecise(self);
        uint256[] memory balances = self.balances;
        uint256[] memory multipliers = self.tokenPrecisionMultipliers;

        uint256 d0 = getD(_xp(balances, multipliers), a);
        for (uint256 i = 0; i < balances.length; i++) {
            if (deposit) {
                balances[i] = balances[i].add(amounts[i]);
            } else {
                balances[i] = balances[i].sub(
                    amounts[i],
                    "Cannot withdraw more than available"
                );
            }
        }
        
        uint256 d1 = getD(_xp(balances, multipliers), a);
        uint256 totalSupply = self.lpToken.totalSupply();

        if (totalSupply == 0) {
            return d1;
        }

        if (deposit) {
            return d1.sub(d0).mul(totalSupply).div(d0);
        } else {
            return d0.sub(d1).mul(totalSupply).div(d0);
        }
    }

Code block from following contract

https://scan.coredao.org/address/0x784d5dBf7a5d6DA74D18a42B8D0D66643d329399#code

contracts/stable-amm/SwapUtils.sol

Key Function: addLiquidity

Location:

  • Contract: Swap.sol

  • Function: addLiquidity(uint256[] memory amounts, uint256 minToMint, uint256 deadline)

Purpose:

This function is called when a user deposits tokens into the pool. It:

  1. Accepts an array of token amounts (amounts) corresponding to each asset in the pool.

  2. Calculates how these deposits impact the pool's invariant and adjusts the balances of each asset accordingly.

  3. Mints LP tokens to represent the depositor's share of the pool.

How it handles Rebalancing:

  • The function uses calculateTokenAmount from SwapUtils.sol to compute how much liquidity should be added based on the current state of the pool and the deposited amounts.

  • If one asset is underrepresented (e.g. $100 worth of BTC), a larger portion of your deposit is effectively allocated to that asset to rebalance the pool.

  • The StableSwap invariant ensures that this redistribution happens automatically, but it results in slippage for deposits into imbalanced pools.

Supporting Function: calculateTokenAmount

Location:

  • Library: SwapUtils.sol

  • Function: calculateTokenAmount(uint256[] memory balances, uint256[] memory amounts, uint256 amp)

Purpose:

This function calculates the number of LP tokens to mint based on:

  1. The current balances of assets in the pool.

  2. The amplification coefficient (amp) to determine how tightly balanced the pool should remain.

  3. The deposited amounts.

How it contributes to Rebalancing:

  • It computes how much value your deposit adds to each asset in relation to their current weights.

  • If an asset is underrepresented, more of your deposit is allocated toward it, which can result in receiving fewer LP tokens than expected.

The addLiquidity function in Swap.sol, combined with calculateTokenAmount in SwapUtils.sol, is directly responsible for rebalancing deposits in StableSwap AMM. These functions use the StableSwap invariant and amplification coefficient to redistribute liquidity across assets, ensuring that the pool remains balanced but causing slippage when assets are highly imbalanced.

Taking it further to understand underlying mathematics. The provided calculateTokenAmount function is key to understanding how the 0.1 BTC gets redistributed during a deposit. Here's a pseudo-algorithm that explains the steps and logic behind it:

Steps:

  1. Get Amplification Coefficient:

    • Retrieve the amplification coefficient AAAA using _getAPrecise(self). This determines how sensitive the pool is to imbalances.

  2. Calculate Initial Invariant (d0):

    • Use the function getD() to calculate the pool's invariant d0d0d0d0​, which represents the "balanced state" of the pool before the deposit.

    • The invariant takes into account:

      • Current token balances (balances[]).

      • Token precision multipliers (multipliers[]), which normalize token values to a common precision.

      • Amplification coefficient AAAA.

  3. Simulate New Balances After Deposit:

    • Iterate over each token in the pool:

      • If deposit == true, add the corresponding deposit amount from amounts[i] to balances[i].

      • If deposit == false, subtract the withdrawal amount from balances[i].

    • This step simulates what the new token balances would look like after the deposit or withdrawal.

  4. Calculate New Invariant (d1):

    • Recalculate the pool's invariant d1d1d1d1​ using the updated token balances after simulating the deposit/withdrawal.

  5. Determine LP Token Impact:

    • Retrieve the total supply of LP tokens (totalSupply).

    • If this is the first deposit (totalSupply == 0), mint LP tokens equal to d1d1d1d1​ (the new invariant). The first depositor gets all LP tokens proportional to their contribution.

    • Otherwise, calculate how many LP tokens should be minted or burned based on the change in invariants:

      • For deposits:

        LPTokensMinted=(d1d0)totalSupplyd0LPTokensMinted=d0(d1d0)totalSupplyLP Tokens Minted=(d1−d0)⋅totalSupplyd0LP Tokens Minted=d0​(d1​−d0​)⋅totalSupply​
      • For withdrawals:

        LPTokensBurned=(d0d1)totalSupplyd0LPTokensBurned=d0(d0d1)totalSupplyLP Tokens Burned=(d0−d1)⋅totalSupplyd0LP Tokens Burned=d0​(d0​−d1​)⋅totalSupply​
  6. Redistribution of Value (Slippage):

    • The difference between d1d1d1d1​ and d0d0d0d0​ reflects how much value your deposit adds to or removes from the pool.

    • If one asset is disproportionately small (e.g., $100 worth of BTC), its balance increases significantly relative to others, causing slippage.

    • This results in fewer LP tokens being minted for your deposit because part of your deposit is "absorbed" to rebalance the underrepresented asset.

  7. Return LP Tokens Minted/Burned:

    • Return the calculated number of LP tokens minted (for deposits) or burned (for withdrawals).

Here is the scenario when user deposit 1 BTC into a highly imbalanced pool:

  1. The initial invariant d0d0d0d0​ is calculated based on current balances, where one asset (e.g., $100 worth of BTC) is severely underrepresented.

  2. After adding 1 BTC, a significant portion of your deposit is allocated to rebalance this underrepresented asset, increasing its balance disproportionately compared to others.

  3. The new invariant d1d1d1d1​ reflects this rebalancing but shows that not all of your deposited value contributes equally to minting LP tokens.

  4. The difference between d1d1d1d1​ and d0d0d0d0​, combined with total supply, determines how much value is effectively redistributed within the pool.

For example

  • When you deposit 1 BTC (Asset 2) into the pool, the algorithm recalculates the pool's invariant (D) to maintain balance between all assets.

  • If Asset 1 (e.g., $100) is underrepresented compared to Asset 2 (e.g., $92,000 for 1 BTC), the StableSwap formula redistributes a portion of the value from Asset 2 into Asset 1 to restore equilibrium.

  • This redistribution occurs mathematically, not physically. The algorithm adjusts the relative prices of the assets in the pool by changing their ratios. As a result:

    • Some of your deposited BTC effectively increases the "value" of Asset 1 in the pool.

    • The pool remains balanced according to the amplification coefficient (A), which controls how tightly it maintains parity between assets.

  • Mathematical Redistribution:

    • The redistribution isn't physical (no tokens are moved between accounts). Instead, it's reflected in the recalculated ratios and prices within the pool.

    • For example, after depositing 1 BTC, you might find that swapping out some BTC now gives you slightly more of Asset 1 than before, indicating its value has increased.

Last updated