Adding Liquidity

Providing liquidity to Orbital AMM means creating a new tick with specific price range parameters. This page walks through the full LP flow.

Tick Parameters

A tick is defined by:

  • r— Sphere radius (how much liquidity you're providing)
  • k — Hyperplane offset (defines the price range)

Instead of computing k directly, use the helper that converts from a depeg price:

import { kFromDepegPrice } from '@taurus-swap/sdk';

// For a 0.99 depeg threshold (1% depeg allowed)
const k = kFromDepegPrice(r, 0.99, n);

Computing Deposit Amounts

The SDK computes how much of each token you need to deposit:

import { computeDepositPerToken } from '@taurus-swap/sdk';

const deposits = computeDepositPerToken(
  poolState,
  {
    r: 10_000_000n,  // 10M liquidity
    k: kFromDepegPrice(10_000_000n, 0.99, 5)
  }
);

// deposits[i] is the amount of token i to transfer
console.log('Deposit amounts:', deposits);

The Deposit Flow

  1. Compute tick parameters (r, k)
  2. Calculate deposit per token
  3. Opt in to all token ASAs (if not already)
  4. Build transaction group: n ASA transfers + app call
  5. Sign and send
  6. Receive LP position NFT (box key proves ownership)

Full Example

import {
  readPoolState,
  computeDepositPerToken,
  kFromDepegPrice,
  buildAddLiquidityGroup
} from '@taurus-swap/sdk';

async function addLiquidity() {
  // 1. Read current pool state
  const poolState = await readPoolState(algodClient, POOL_APP_ID);

  // 2. Define tick parameters
  const LIQUIDITY_AMOUNT = 10_000_000n;  // 10M units
  const DEPEG_THRESHOLD = 0.99;           // 1% depeg allowed

  const r = LIQUIDITY_AMOUNT;
  const k = kFromDepegPrice(r, DEPEG_THRESHOLD, poolState.n);

  // 3. Compute deposit per token
  const deposits = computeDepositPerToken(poolState, { r, k });

  console.log('Depositing:');
  deposits.forEach((amt, i) => {
    const display = Number(amt) / 1e6;
    console.log(`  Token ${i}: ${display}`);
  });

  // 4. Build transaction group
  const { txGroup } = await buildAddLiquidityGroup(
    algodClient,
    POOL_APP_ID,
    account,
    {
      r,
      k,
      deposits
    }
  );

  // 5. Sign and send
  const signedTxns = await wallet.signTransaction(
    txGroup.map((tx) => tx.txn)
  );

  const result = await algodClient
    .sendGroupTransaction(signedTxns)
    .do();

  // 6. Wait for confirmation
  const confirmation = await algosdk.waitForConfirmation(
    algodClient,
    result.txId,
    4
  );

  console.log('Liquidity added in round:', confirmation['confirmed-round']);

  // The tick ID is in the app call logs
  const tickId = extractTickIdFromLogs(confirmation.logs);
  console.log('Your tick ID:', tickId);

  return { tickId, txId: result.txId };
}

Transaction Group Structure

For n=5 tokens:

┌─────────────────────────────────────┐
│ Tx 0-4: ASA Transfers (5 txns)      │
│  - Sender: LP                       │
│  - Receiver: Pool                   │
│  - Amount: deposits[i] for each     │
├─────────────────────────────────────┤
│ Tx 5: App Call (add_tick)           │
│  - Method: add_tick(r, k, ...)      │
│  - Creates tick box                 │
│  - Creates position box             │
└─────────────────────────────────────┘

Total: n + 1 transactions

Opting In to Tokens

Before adding liquidity, ensure you're opted in to all pool tokens:

import { checkAssetOptIn, buildAssetOptInTx } from '@taurus-swap/sdk';

// Check opt-in status
const optIns = await Promise.all(
  poolState.tokenAsas.map((asa) =>
    checkAssetOptIn(algodClient, account.addr, asa)
  )
);

// Build opt-in transactions for missing assets
const optInTxs = [];
for (let i = 0; i < optIns.length; i++) {
  if (!optIns[i]) {
    optInTxs.push(
      await buildAssetOptInTx(algodClient, poolState.tokenAsas[i])
    );
  }
}

// Sign and send opt-ins first
if (optInTxs.length > 0) {
  const signedOptIns = await wallet.signTransaction(
    optInTxs.map((tx) => tx.txn)
  );
  await algodClient.sendGroupTransaction(signedOptIns).do();
}

Position Ownership

LP positions are stored in box storage with key:

pos:{owner_address}{tick_id}

The box contains:

  • shares — Your share of the tick's total liquidity
  • feeCheckpoints — Fee growth snapshot for each token

To read your position later:

const position = await readPosition(
  algodClient,
  POOL_APP_ID,
  account.addr,
  tickId
);

console.log('Your shares:', position.shares);
console.log('Pending fees:', position.pendingFees);

Removing Liquidity

To remove liquidity (withdraw your share):

import { buildRemoveLiquidityGroup } from '@taurus-swap/sdk';

const { txGroup } = await buildRemoveLiquidityGroup(
  algodClient,
  POOL_APP_ID,
  account,
  {
    tickId,
    sharesToRemove: position.shares  // Remove all
  }
);

// Sign and send...
const result = await executeAndSend(txGroup, wallet);

// You'll receive:
// - Proportional share of reserves (all tokens)
// - All accrued fees

Claiming Fees

To claim fees without removing liquidity:

import { buildClaimFeesGroup } from '@taurus-swap/sdk';

const { txGroup } = await buildClaimFeesGroup(
  algodClient,
  POOL_APP_ID,
  account,
  tickId
);

// Sign and send...
// You'll receive accrued fees in all tokens
Note:Fees are automatically claimed when you remove liquidity. You don't need a separate claim transaction.