Price Impact

Calculate and manage price impact for swaps in CLMM pools.

Quick Start

// Calculate price impact before swap
const pool = await sdk.Pool.getPool(poolId)
const ticks = await sdk.Pool.fetchTicks({
  pool_id: poolId,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB
})

const result = sdk.Swap.calculateRates({
  currentPool: pool,
  coinTypeA: pool.coinTypeA,
  coinTypeB: pool.coinTypeB,
  decimalsA: 9,
  decimalsB: 6,
  a2b: true,
  byAmountIn: true,
  amount: new BN('1000000000'), // 1 SUI
  swapTicks: ticks
})

console.log(`Price Impact: ${result.priceImpactPct.toFixed(2)}%`)

Understanding Price Impact

Price impact occurs when your trade moves the pool price:

  • Small trades: Minimal impact (<0.1%)

  • Medium trades: Noticeable impact (0.1-1%)

  • Large trades: Significant impact (>1%)

Calculate Methods

Method 1: Local Calculation

async function calculatePriceImpact(
  poolId: string,
  amount: string,
  a2b: boolean,
  byAmountIn: boolean
) {
  // 1. Get pool and ticks
  const pool = await sdk.Pool.getPool(poolId)
  const ticks = await sdk.Pool.fetchTicks({
    pool_id: poolId,
    coinTypeA: pool.coinTypeA,
    coinTypeB: pool.coinTypeB
  })
  
  // 2. Calculate swap result
  const result = sdk.Swap.calculateRates({
    currentPool: pool,
    coinTypeA: pool.coinTypeA,
    coinTypeB: pool.coinTypeB,
    decimalsA: 9,
    decimalsB: 6,
    a2b,
    byAmountIn,
    amount: new BN(amount),
    swapTicks: ticks
  })
  
  return {
    priceImpact: result.priceImpactPct,
    currentPrice: TickMath.sqrtPriceX64ToPrice(
      pool.current_sqrt_price, 9, 6
    ),
    endPrice: TickMath.sqrtPriceX64ToPrice(
      result.estimatedEndSqrtPrice, 9, 6
    ),
    isExceed: result.isExceed
  }
}

Method 2: From PreSwap

async function getImpactFromPreSwap(
  pool: Pool,
  amount: string,
  a2b: boolean
) {
  // Get current price
  const currentPrice = TickMath.sqrtPriceX64ToPrice(
    pool.current_sqrt_price,
    9, 6
  )
  
  // Simulate swap
  const preSwap = await sdk.Swap.preswap({
    pool,
    coinTypeA: pool.coinTypeA,
    coinTypeB: pool.coinTypeB,
    decimalsA: 9,
    decimalsB: 6,
    a2b,
    byAmountIn: true,
    amount,
    currentSqrtPrice: pool.current_sqrt_price
  })
  
  // Calculate end price
  const endPrice = TickMath.sqrtPriceX64ToPrice(
    new BN(preSwap.estimatedEndSqrtPrice),
    9, 6
  )
  
  // Price impact = |endPrice - currentPrice| / currentPrice * 100
  const impact = Math.abs(
    endPrice.minus(currentPrice).div(currentPrice).mul(100).toNumber()
  )
  
  return {
    impact,
    currentPrice: currentPrice.toNumber(),
    endPrice: endPrice.toNumber()
  }
}

Router Price Impact

Calculate impact across multiple pools:

async function calculateRouterImpact(splitPaths: SplitPath[]) {
  // Use SDK's built-in method
  const totalImpact = sdk.Swap.calculateSwapPriceImpact(splitPaths)
  
  console.log(`Total route impact: ${totalImpact}%`)
  
  // Or calculate manually for each path
  let weightedImpact = 0
  
  splitPaths.forEach(path => {
    const pathWeight = path.percent / 100
    let pathImpact = 0
    
    if (path.basePaths.length === 1) {
      // Single hop
      const exchangeRate = path.outputAmount / path.inputAmount
      const marketPrice = path.basePaths[0].currentPrice
      pathImpact = calculateSingleImpact(exchangeRate, marketPrice)
    } else {
      // Multi-hop
      const totalExchangeRate = path.outputAmount / path.inputAmount
      const marketPrice = path.basePaths.reduce(
        (acc, bp) => acc.mul(bp.currentPrice),
        new Decimal(1)
      )
      pathImpact = calculateSingleImpact(totalExchangeRate, marketPrice)
    }
    
    weightedImpact += pathImpact * pathWeight
  })
  
  return weightedImpact
}

Impact Thresholds

Set appropriate warnings based on impact:

function getPriceImpactSeverity(impactPercent: number) {
  if (impactPercent < 0.1) return { level: 'low', color: 'green' }
  if (impactPercent < 0.5) return { level: 'medium', color: 'yellow' }
  if (impactPercent < 1.0) return { level: 'high', color: 'orange' }
  return { level: 'severe', color: 'red' }
}

// Usage
const impact = await calculatePriceImpact(poolId, amount, true, true)
const severity = getPriceImpactSeverity(impact.priceImpact)

if (severity.level === 'severe') {
  console.warn('⚠️ High price impact! Consider reducing trade size.')
}

Minimize Price Impact

1. Split Large Trades

async function splitTradeForBetterPrice(
  totalAmount: BN,
  maxImpactPercent: number
) {
  const chunks = []
  let remainingAmount = totalAmount
  
  while (remainingAmount.gt(new BN(0))) {
    // Find chunk size with acceptable impact
    const chunkSize = await findMaxAmountForImpact(
      poolId,
      maxImpactPercent
    )
    
    if (chunkSize.gte(remainingAmount)) {
      chunks.push(remainingAmount)
      break
    }
    
    chunks.push(chunkSize)
    remainingAmount = remainingAmount.sub(chunkSize)
  }
  
  return chunks
}

2. Use Multiple Pools

async function findLowImpactRoute(
  amount: BN,
  fromCoin: string,
  toCoin: string
) {
  // Router automatically minimizes impact
  const bestRoute = await sdk.Router.getBestInternalRouter(
    fromCoin,
    toCoin,
    amount,
    true,
    0.005, // 0.5% slippage
    ''
  )
  
  // Check if multi-hop has lower impact
  if (bestRoute.paths[0].poolAddress.length > 1) {
    console.log('Using multi-hop route for lower impact')
  }
  
  return bestRoute
}

Complete Example

async function executeSwapWithImpactCheck(
  poolId: string,
  amount: string,
  maxImpactPercent: number
) {
  // 1. Calculate impact
  const impact = await calculatePriceImpact(
    poolId,
    amount,
    true,
    true
  )
  
  console.log(`Price Impact: ${impact.priceImpact.toFixed(3)}%`)
  console.log(`Price: ${impact.currentPrice} → ${impact.endPrice}`)
  
  // 2. Check threshold
  if (impact.priceImpact > maxImpactPercent) {
    throw new Error(
      `Price impact ${impact.priceImpact}% exceeds max ${maxImpactPercent}%`
    )
  }
  
  // 3. Warn user
  const severity = getPriceImpactSeverity(impact.priceImpact)
  if (severity.level !== 'low') {
    console.warn(`${severity.level.toUpperCase()} price impact detected`)
  }
  
  // 4. Execute swap with slippage
  const slippage = Math.max(0.5, impact.priceImpact * 1.5)
  
  const swapParams = {
    pool_id: poolId,
    coinTypeA: pool.coinTypeA,
    coinTypeB: pool.coinTypeB,
    a2b: true,
    by_amount_in: true,
    amount,
    amount_limit: calculateMinOutput(amount, slippage)
  }
  
  const tx = await sdk.Swap.createSwapTransactionPayload(swapParams)
  return tx
}

Price Impact Formula

// Basic formula
priceImpact = |endPrice - startPrice| / startPrice * 100

// For CLMM
function calculateCLMMImpact(
  startSqrtPrice: BN,
  endSqrtPrice: BN,
  decimalsA: number,
  decimalsB: number
): number {
  const startPrice = TickMath.sqrtPriceX64ToPrice(
    startSqrtPrice, decimalsA, decimalsB
  )
  const endPrice = TickMath.sqrtPriceX64ToPrice(
    endSqrtPrice, decimalsA, decimalsB
  )
  
  return Math.abs(
    endPrice.minus(startPrice)
      .div(startPrice)
      .mul(100)
      .toNumber()
  )
}

Important Notes

  • Price impact increases with trade size

  • Impact is higher in low liquidity pools

  • Concentrated liquidity affects impact differently

  • Always set appropriate slippage tolerance

  • Consider splitting large trades

  • Monitor impact in real-time for better UX

Last updated