Quoting Swaps

The getSwapQuote function computes the output amount for a swap by solving the torus invariant. It handles tick crossings automatically.

Usage

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

const quote = await getSwapQuote(poolState, {
  tokenInIndex: 0,      // Selling USDC
  tokenOutIndex: 1,     // Buying USDT
  amountIn: 100_000_000n // 100 USDC (microunits)
});

console.log('Output:', quote.amountOut);
console.log('Price impact:', quote.priceImpact);
console.log('Fee:', quote.fee);

SwapQuote Type

interface SwapQuote {
  // Output
  amountOut: bigint;    // Amount of output token (microunits)

  // Pricing
  priceImpact: number;  // Price impact as decimal (0.0014 = 0.14%)
  effectivePrice: number; // Output/Input ratio

  // Fees
  fee: bigint;          // Fee amount (in input token microunits)
  feeBps: number;       // Fee in basis points

  // Path info
  ticksCrossed: number; // Number of ticks crossed
  segments: TradeSegment[]; // Trade path segments

  // Validation
  minOut: bigint;       // Minimum output after slippage
}

interface TradeSegment {
  amountIn: bigint;
  amountOut: bigint;
  tickCrossedId: number;  // 0 if no crossing
  newTickState: number;
}

Field Descriptions

Output

  • amountOut— The exact output amount in microunits. Use this for the user's "You will receive" display.

Pricing

  • priceImpact — How much the trade moves the price. 0.0014 means 0.14% price slippage from mid-price.
  • effectivePrice — The actual exchange rate: amountOut / amountIn. For stablecoin swaps, this should be close to 1.0.

Fees

  • fee — The fee amount in input token microunits. Deducted before the trade is computed.
  • feeBps — Fee in basis points. 30 = 0.3%.

Path Info

  • ticksCrossed — Number of tick boundaries crossed. More crossings = more complex transaction.
  • segments — The trade path. Each segment is a portion of the trade between crossings.

Validation

  • minOut — amountOut minus slippage tolerance. Pass this to the transaction builder.

Full Example with Slippage

const quote = await getSwapQuote(poolState, {
  tokenInIndex: 0,
  tokenOutIndex: 1,
  amountIn: 100_000_000n
});

// Apply slippage tolerance (0.5% = 50 bps)
const SLIPPAGE_BPS = 50;
const minOut = quote.amountOut * (10000n - BigInt(SLIPPAGE_BPS)) / 10000n;

console.log(`Expected output: ${quote.amountOut} USDT`);
console.log(`Minimum output: ${minOut} USDT (${SLIPPAGE_BPS} bps slippage)`);
console.log(`Price impact: ${(quote.priceImpact * 100).toFixed(2)}%`);

// Warn if price impact is high
if (quote.priceImpact > 0.01) {  // > 1%
  alert('High price impact! Consider splitting into smaller trades.');
}

Debouncing Quotes

In a React UI, debounce user input to avoid excessive quote requests:

import { useState, useEffect, useCallback } from 'react';
import { getSwapQuote, debounce } from '@taurus-swap/sdk';

function SwapForm({ poolState }: { poolState: PoolState }) {
  const [amountIn, setAmountIn] = useState('');
  const [quote, setQuote] = useState<SwapQuote | null>(null);

  const fetchQuote = useCallback(
    debounce(async (input: string) => {
      if (!input || !poolState) return;

      const amountIn = parseAmountToMicrounits(input, 6);
      const q = await getSwapQuote(poolState, {
        tokenInIndex: 0,
        tokenOutIndex: 1,
        amountIn
      });
      setQuote(q);
    }, 300),
    [poolState]
  );

  useEffect(() => {
    fetchQuote(amountIn);
  }, [amountIn, fetchQuote]);

  return (
    <input
      value={amountIn}
      onChange={(e) => setAmountIn(e.target.value)}
      placeholder="Amount in"
    />
    {quote && <div>You receive: {formatAmount(quote.amountOut)}</div>}
  );
}

Handling Stale State

Quotes are valid only for the pool state they were computed from. If the pool changes between quote and execution, the transaction may fail:

try {
  const { txGroup } = await buildSwapTransactionGroup(
    algodClient,
    POOL_APP_ID,
    account,
    {
      ...tradeParams,
      claimedOut: quote.amountOut,
      minOut: quote.minOut
    }
  );
  await sendTransaction(txGroup);
} catch (err) {
  if (err.message.includes('invariant check failed')) {
    // Pool state changed - re-quote and retry
    const freshQuote = await getSwapQuote(freshPoolState, tradeParams);
    // Retry with fresh quote...
  }
}

Large Trades

For trades that cross many ticks, the quote includes a trade recipe:

const quote = await getSwapQuote(poolState, {
  tokenInIndex: 0,
  tokenOutIndex: 1,
  amountIn: 1_000_000_000n  // 1000 USDC - large trade
});

console.log(`Crossing ${quote.ticksCrossed} ticks`);
console.log('Trade segments:', quote.segments);

// Use swap_with_crossings instead of swap
const method = quote.ticksCrossed > 0
  ? 'swap_with_crossings'
  : 'swap';
Note: The SDK automatically selects the correct method when you use buildSwapTransactionGroup. You don't need to handle this manually.