Quoting Swaps
getSwapQuote is a synchronous, pure function. It runs the full Newton-bisection solver and tick-crossing logic off-chain, producing an exact quote that matches what the on-chain contract will compute. No Algod calls needed once you have a PoolState.
Signature
function getSwapQuote(
poolState: PoolState,
tokenInIdx: number, // index into pool.tokenAsaIds
tokenOutIdx: number,
amountInRaw: bigint, // raw microunits (1 USDC = 1_000_000n)
): SwapQuoteBasic Usage
import { readPoolState, getSwapQuote } from '@taurusswap/sdk';
const pool = await readPoolState(algod, POOL_APP_ID);
// Sell 10 USDC (token 0) → buy USDT (token 1)
const quote = getSwapQuote(pool, 0, 1, 10_000_000n);
console.log('You receive:', Number(quote.amountOut) / 1e6, 'USDT');
console.log('Effective rate:', quote.effectivePrice.toFixed(6));
console.log('Price impact:', (quote.priceImpact * 100).toFixed(4) + '%');
console.log('Ticks crossed:', quote.ticksCrossed);Via TaurusClient (async — fetches pool state from cache first):
const quote = await client.quote({
fromIndex: 0,
toIndex: 1,
amountIn: 10_000_000n,
});SwapQuote Type
interface SwapQuote {
amountIn: bigint; // raw microunits — the input you provided
amountOut: bigint; // raw microunits — what you receive
priceImpact: number; // 0.001 = 0.1% — how much the trade moves price
instantaneousPrice: number; // spot price before the trade (dimensionless ratio)
effectivePrice: number; // amountOut / amountIn — actual average rate
ticksCrossed: number; // 0 = simple swap; >0 = swap_with_crossings on-chain
route: TradeSegment[]; // internal segments (AMOUNT_SCALE units)
}
interface TradeSegment {
amountIn: bigint; // AMOUNT_SCALE units
amountOut: bigint; // AMOUNT_SCALE units
tickCrossedId: number | null; // which tick was exited (null = no crossing in this segment)
newTickState: TickState | null; // state the crossed tick transitions to
}Understanding Price Impact
priceImpact is computed as:
priceImpact = 1 − (effectivePrice / instantaneousPrice)For a 100 USDC → USDT swap, if the spot rate is 1.0000 and you receive 99.97 USDT, the effective price is 0.9997 and price impact is 0.03%.
Guidelines for stablecoin swaps:
| Price Impact | Interpretation |
|---|---|
| < 0.01% | Normal — deep pool, small trade |
| 0.01% – 0.1% | Acceptable — moderate-sized trade |
| 0.1% – 1% | Warn the user |
| > 1% | High — consider splitting the trade |
Tick Crossings
The pool uses concentrated liquidity ticks. A large swap may exhaust one tick and "cross" into the next. The SDK handles this automatically:
ticksCrossed === 0— Simple path. The contract callsswap().ticksCrossed > 0— Multi-segment path. The contract callsswap_with_crossings()with a serialisedtrade_recipe.
buildSwapTxns (and client.buildSwapTxns) automatically chooses the right contract method — you don't need to handle this manually.
Applying Slippage Tolerance
const quote = getSwapQuote(pool, 0, 1, 10_000_000n);
const SLIPPAGE_BPS = 50; // 0.5%
const minAmountOut = (quote.amountOut * BigInt(10_000 - SLIPPAGE_BPS)) / 10_000n;
console.log('Expected:', Number(quote.amountOut) / 1e6);
console.log('Minimum: ', Number(minAmountOut) / 1e6);When you pass slippageBps to buildSwapTxns, it applies this formula automatically and encodes minAmountOut into the transaction as a contract-enforced floor.
Spot Prices
// Get all token prices relative to token 0 (= 1.0)
const prices = await client.getAllPrices();
// prices[0] = 1.0 (base token)
// prices[1] = 0.9998 (USDT/USDC spot rate)
// prices[2] = 1.0001 etc.
// Or with explicit base:
const pricesRelativeToUSDT = await client.getAllPrices(1);Under the hood this calls getAllPrices(poolState, baseTokenIdx) which uses getPrice(reserves, rInt, i, j) from the sphere math module.
React Hook: Live Quote
import { useState, useEffect, useCallback } from 'react';
import { type SwapQuote, SwapTooSmallError, InsufficientLiquidityError } from '@taurusswap/sdk';
import { taurusClient } from '@/lib/taurus';
export function useSwapQuote(
fromIndex: number,
toIndex: number,
amountIn: bigint | null,
) {
const [quote, setQuote] = useState<SwapQuote | null>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (!amountIn || amountIn <= 0n) { setQuote(null); return; }
let cancelled = false;
setLoading(true);
taurusClient
.quote({ fromIndex, toIndex, amountIn })
.then((q) => { if (!cancelled) { setQuote(q); setError(null); } })
.catch((err) => {
if (cancelled) return;
if (err instanceof SwapTooSmallError) setError('Amount too small');
else if (err instanceof InsufficientLiquidityError) setError('Insufficient liquidity');
else setError('Quote failed');
setQuote(null);
})
.finally(() => { if (!cancelled) setLoading(false); });
return () => { cancelled = true; };
}, [fromIndex, toIndex, amountIn?.toString()]);
return { quote, error, loading };
}