Router Swap

Execute optimized swaps through Ferra's smart router for best rates.

Overview

The Router module automatically finds the optimal path for your swap by:

  • Path Discovery: Finds all possible routes between tokens

  • TVL Optimization: Prioritizes pools with higher liquidity

  • Multi-hop Support: Routes through intermediate tokens when beneficial

  • Best Rate Selection: Compares all paths to find optimal execution

Quick Start

// 1. Find best route
const bestRoute = await sdk.Router.getBestInternalRouter(
  '0x2::sui::SUI',           // From token
  '0x...::usdc::USDC',       // To token
  new BN('1000000000'),      // Amount (1 SUI)
  true,                      // Fix input amount
  0.01,                      // 1% slippage
  ''                         // No partner
)

// 2. Create swap transaction
if (bestRoute && !bestRoute.isExceed) {
  const tx = await TransactionUtil.buildRouterSwapTransaction(
    sdk,
    bestRoute.createTxParams,
    true,  // byAmountIn
    allCoinAssets
  )
  
  const result = await sdk.fullClient.signAndExecuteTransaction({
    transaction: tx,
    signer: keypair
  })
}

Find Optimal Route

Basic Route Finding

const bestRoute = await sdk.Router.getBestInternalRouter(
  fromCoin,      // Source token
  toCoin,        // Target token
  amount,        // Swap amount
  true,          // Fix input (true) or output (false)
  0.005,         // 0.5% slippage
  ''             // Partner ID (optional)
)

console.log({
  inputAmount: bestRoute.amountIn.toString(),
  outputAmount: bestRoute.amountOut.toString(),
  paths: bestRoute.paths.length,
  isExceed: bestRoute.isExceed
})

With Fallback Options

// Fallback params for direct swap if no route found
const fallbackParams = {
  poolAddresses: ['0xpool1...', '0xpool2...'],
  coinTypeA: fromCoin,
  coinTypeB: toCoin,
  a2b: true,
  byAmountIn: true,
  amount: swapAmount.toString()
}

const bestRoute = await sdk.Router.getBestInternalRouter(
  fromCoin,
  toCoin,
  swapAmount,
  true,
  0.01,
  '',
  fallbackParams  // Will use direct swap if no route
)

Route Types

Single-Hop Route

Direct swap in one pool:

// Route: SUI -> USDC
bestRoute.paths[0] = {
  poolAddress: ['0xpool...'],
  a2b: [true],
  coinType: ['0x2::sui::SUI', '0x...::usdc::USDC'],
  amountIn: BN,
  amountOut: BN
}

Multi-Hop Route

Through intermediate token:

// Route: SUI -> USDT -> USDC
bestRoute.paths[0] = {
  poolAddress: ['0xpool1...', '0xpool2...'],
  a2b: [true, true],
  coinType: ['0x2::sui::SUI', '0x...::usdt::USDT', '0x...::usdc::USDC'],
  amountIn: BN,
  amountOut: BN
}

Complete Swap Flow

async function smartRouterSwap() {
  try {
    // 1. Get user's coin assets
    const allCoinAssets = await sdk.getOwnerCoinAssets(userAddress)
    
    // 2. Define swap parameters
    const fromCoin = '0x2::sui::SUI'
    const toCoin = '0x...::usdc::USDC'
    const swapAmount = new BN('10000000000') // 10 SUI
    const slippage = 0.01 // 1%
    
    // 3. Find optimal route
    console.log('Finding best route...')
    const bestRoute = await sdk.Router.getBestInternalRouter(
      fromCoin,
      toCoin,
      swapAmount,
      true,     // Fix input
      slippage,
      ''        // No partner
    )
    
    // 4. Check if route is valid
    if (!bestRoute || bestRoute.isExceed) {
      throw new Error('No valid route found')
    }
    
    // 5. Display route info
    console.log('Route found:')
    console.log(`  Input: ${bestRoute.amountIn.toString()}`)
    console.log(`  Output: ${bestRoute.amountOut.toString()}`)
    console.log(`  Hops: ${bestRoute.paths[0].poolAddress.length}`)
    console.log(`  Pools: ${bestRoute.paths[0].poolAddress.join(' -> ')}`)
    
    // 6. Create swap transaction
    const tx = await TransactionUtil.buildRouterSwapTransaction(
      sdk,
      bestRoute.createTxParams,
      true,  // byAmountIn
      allCoinAssets
    )
    
    // 7. Execute swap
    const result = await sdk.fullClient.signAndExecuteTransaction({
      transaction: tx,
      signer: keypair
    })
    
    console.log('Swap completed:', result.digest)
    
  } catch (error) {
    console.error('Router swap failed:', error)
  }
}

Load Graph Data

Router needs graph data to find paths:

// Method 1: Auto-load from API
if (!sdk.Router.isGraphLoaded) {
  await sdk.Router.loadGraphData()
}

// Method 2: Manual load with custom data
const coinData = {
  coins: [
    { address: '0x2::sui::SUI', decimals: 9 },
    { address: '0x...::usdc::USDC', decimals: 6 },
    { address: '0x...::usdt::USDT', decimals: 6 }
  ]
}

const pathData = {
  paths: [
    {
      base: '0x2::sui::SUI',
      quote: '0x...::usdc::USDC',
      addressMap: new Map([[30, '0xpool1...']]) // 0.3% fee
    }
  ]
}

sdk.Router.loadGraph(coinData, pathData)

Advanced Features

TVL-Based Routing

Router prioritizes high-TVL pools:

// Router automatically sorts multi-hop paths by minimum TVL
const poolsWithTvl = await sdk.Router.getPoolWithTVL()

poolsWithTvl.forEach(pool => {
  console.log(`Pool ${pool.poolAddress}: ${pool.tvl}`)
})

Custom Path Preferences

// Find route with specific requirements
const bestRoute = await sdk.Router.getBestInternalRouter(
  fromCoin,
  toCoin,
  swapAmount,
  true,
  0.005,    // Tighter slippage for large trades
  partnerID // Route fees to partner
)

// Check route quality
if (bestRoute.paths[0].poolAddress.length > 2) {
  console.log('Route too complex, consider direct swap')
}

Fix Output Amount

// Want exactly 1000 USDC
const bestRoute = await sdk.Router.getBestInternalRouter(
  '0x2::sui::SUI',
  '0x...::usdc::USDC',
  new BN('1000000000'),  // 1000 USDC
  false,                 // Fix output
  0.01,
  ''
)

console.log(`Need ${bestRoute.amountIn} SUI`)

Error Handling

try {
  const bestRoute = await sdk.Router.getBestInternalRouter(
    fromCoin, toCoin, amount, true, 0.01, ''
  )
  
  if (!bestRoute) {
    console.log('No route available')
  } else if (bestRoute.isExceed) {
    console.log('Insufficient liquidity in pools')
  } else if (bestRoute.paths.length === 0) {
    console.log('No valid path found')
  }
  
} catch (error) {
  if (error.code === 'InvalidCoin') {
    console.log('Token not supported')
  } else if (error.code === 'NotFoundPath') {
    console.log('No trading path exists')
  }
}

Route Information

interface BestInternalRouterResult {
  amountIn: BN              // Input amount
  amountOut: BN             // Output amount
  paths: OnePath[]          // Route paths
  isExceed: boolean         // Exceeds liquidity
  createTxParams: SwapWithRouterParams  // Ready-to-use params
}

interface OnePath {
  poolAddress: string[]     // Pool addresses in order
  a2b: boolean[]           // Swap directions
  coinType: string[]       // Coin types in path
  amountIn: BN             // Path input
  amountOut: BN            // Path output
}

Important Notes

  • Router automatically loads graph data on first use

  • Multi-hop limited to 2 pools (3 tokens max)

  • TVL-based routing improves execution quality

  • Partner fees not supported with split paths

  • Always check isExceed before executing

  • Graph data cached for performance

Last updated