TypeScript Examples - EVM

Complete TypeScript examples for trading on EVM chains (Base, Ethereum, BSC, Arbitrum, etc.)

Prerequisites

npm install ethers

Environment Variables

export NAOS_API_KEY="your-api-key"
export NAOS_API_DOMAIN="https://api.naos.trade"
export TRADING_EVM_PKEY="your-evm-private-key"

Setup

import { ethers } from 'ethers'

const API_BASE_URL = process.env.NAOS_API_DOMAIN || 'http://localhost:3001'
const API_KEY = process.env.NAOS_API_KEY || 'your-api-key-here'
const TRADING_EVM_PKEY = process.env.TRADING_EVM_PKEY

interface SwapStep {
  input: string
  output: string
  router: string
  quoter: string
  fee: number
  tick: number
  hook: string
  poolType: string
  address: string
}

interface QuoteResponse {
  success: boolean
  path?: Array<{ step: SwapStep }>
  stats?: {
    valueAmountInUsd: number
    amountOut: string
    amountOutMin: string
    priceImpact?: number
    txCostUsd?: number
  }
  transaction?: {
    trade: string
    approve?: string
    quoteId: string
  }
  error?: string
}

async function makeRequest<T>(endpoint: string, options?: RequestInit): Promise<T> {
  const response = await fetch(`${API_BASE_URL}${endpoint}`, {
    method: options?.method || 'GET',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json',
      ...(options?.headers as Record<string, string> || {})
    },
    body: options?.body
  })

  if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`)
  return await response.json() as T
}

Get Quote

async function getQuote(params: {
  token: string
  amount: number
  side: 'BUY' | 'SELL'
  slippage: number
  trader: string
  chain: string
  priority?: 'LOW' | 'MEDIUM' | 'HIGH' | 'INSTANT'
  integrator?: string
  fee?: number
}): Promise<QuoteResponse> {
  const queryParams = new URLSearchParams({
    token: params.token,
    amount: params.amount.toString(),
    side: params.side,
    slippage: params.slippage.toString(),
    trader: params.trader,
    chain: params.chain
  })

  if (params.priority) queryParams.append('priority', params.priority)
  if (params.integrator) queryParams.append('integrator', params.integrator)
  if (params.fee) queryParams.append('fee', params.fee.toString())

  const response = await makeRequest<QuoteResponse>(`/api/v1/quote?${queryParams.toString()}`)

  if (response.success && response.stats) {
    console.log('Quote received:')
    console.log(`  Value: $${response.stats.valueAmountInUsd.toFixed(2)}`)
    console.log(`  Amount Out: ${response.stats.amountOut}`)
    console.log(`  Price Impact: ${response.stats.priceImpact?.toFixed(2) || 'N/A'}%`)
    console.log(`  TX Cost: $${response.stats.txCostUsd?.toFixed(4) || 'N/A'}`)
    console.log(`  Quote ID: ${response.transaction?.quoteId}`)
  }

  return response
}

Execute Transaction

async function executeTransaction(params: {
  txData: string
  network: string
  mev: boolean
  skipSimulation: boolean
  quoteId: string
  approveTxData?: string
}): Promise<any> {
  const response = await makeRequest<any>('/api/v1/execute', {
    method: 'POST',
    body: JSON.stringify(params)
  })

  if (response.success) {
    console.log('Transaction executed:')
    console.log(`  Status: ${response.status}`)
    console.log(`  TX Hash: ${response.txRes?.txHash}`)
    console.log(`  From: ${response.txRes?.fromToken}`)
    console.log(`  To: ${response.txRes?.toToken}`)
    console.log(`  Spent: ${response.txRes?.spent}`)
    console.log(`  Received: ${response.txRes?.received}`)
  }

  return response
}

Simple Buy Example

async function simpleBuyEVM(): Promise<void> {
  const wallet = new ethers.Wallet(TRADING_EVM_PKEY!)

  const params = {
    token: '0xFb31f85A8367210B2e4Ed2360D2dA9Dc2D2Ccc95',
    chain: 'BASE',
    trader: wallet.address,
    amount: 0.01,
    slippage: 500
  }

  console.log('Getting quote...')
  const quote = await getQuote({
    token: params.token,
    amount: params.amount,
    side: 'BUY',
    slippage: params.slippage,
    trader: params.trader,
    chain: params.chain
  })

  if (!quote.success || !quote.transaction) {
    throw new Error(quote.error || 'Quote failed')
  }

  console.log('Signing transaction...')
  const tx = ethers.Transaction.from(quote.transaction.trade)
  const signedTx = await wallet.signTransaction(tx)

  let signedApproveTx
  if (quote.transaction.approve) {
    console.log('Signing approval transaction...')
    const approveTx = ethers.Transaction.from(quote.transaction.approve)
    signedApproveTx = await wallet.signTransaction(approveTx)
  }

  console.log('Executing transaction...')
  const result = await executeTransaction({
    txData: signedTx,
    network: params.chain,
    mev: false,
    skipSimulation: false,
    quoteId: quote.transaction.quoteId,
    approveTxData: signedApproveTx
  })

  console.log('Trade completed!')
  console.log(`TX Hash: ${result.txRes?.txHash}`)
  console.log(`Explorer: https://basescan.org/tx/${result.txRes?.txHash}`)
}

Full Trading Flow (Buy → Sell)

Complete example showing buy and then sell 50% of holdings:

async function evmFullTradingFlow(): Promise<void> {
  const wallet = new ethers.Wallet(TRADING_EVM_PKEY!)
  const traderAddress = wallet.address

  const params = {
    token: '0xFb31f85A8367210B2e4Ed2360D2dA9Dc2D2Ccc95',
    chain: 'BASE',
    amount: 0.001,
    slippage: 500
  }

  console.log('='.repeat(80))
  console.log('BUY STEP')
  console.log('='.repeat(80))
  console.log(`Token: ${params.token}`)
  console.log(`Chain: ${params.chain}`)
  console.log(`Trader: ${traderAddress}`)
  console.log(`Amount: ${params.amount} ETH`)
  console.log(`Slippage: ${params.slippage / 100}%`)

  const buyQuote = await getQuote({
    token: params.token,
    amount: params.amount,
    side: 'BUY',
    slippage: params.slippage,
    trader: traderAddress,
    chain: params.chain
  })

  if (!buyQuote.success || !buyQuote.transaction) {
    throw new Error(buyQuote.error || 'Failed to get buy quote')
  }

  console.log('\nSigning buy transaction...')
  const buyTx = ethers.Transaction.from(buyQuote.transaction.trade)
  const signedBuyTx = await wallet.signTransaction(buyTx)

  let signedApproveTx
  if (buyQuote.transaction.approve) {
    console.log('Signing approval transaction...')
    const approveTx = ethers.Transaction.from(buyQuote.transaction.approve)
    signedApproveTx = await wallet.signTransaction(approveTx)
  }

  console.log('\nExecuting buy transaction...')
  const buyResult = await executeTransaction({
    txData: signedBuyTx,
    network: params.chain,
    mev: false,
    skipSimulation: false,
    quoteId: buyQuote.transaction.quoteId,
    approveTxData: signedApproveTx
  })

  if (!buyResult.success) throw new Error(buyResult.error || 'Buy failed')

  const tokensReceived = buyResult.txRes?.received || '0'
  console.log('\nBUY COMPLETED!')
  console.log(`TX Hash: ${buyResult.txRes?.txHash}`)
  console.log(`Tokens Received: ${tokensReceived}`)
  console.log(`ETH Spent: ${buyResult.txRes?.spent}`)
  console.log(`Explorer: https://basescan.org/tx/${buyResult.txRes?.txHash}`)

  console.log('\nWaiting 20 seconds before selling...')
  await new Promise(resolve => setTimeout(resolve, 20000))

  console.log('\n' + '='.repeat(80))
  console.log('SELL STEP (50% of holdings)')
  console.log('='.repeat(80))

  const tokenInfo = await makeRequest<any>(`/api/v1/token-info?tokenAddress=${params.token}&chain=${params.chain}`)
  const tokenDecimals = tokenInfo.decimals || 18
  const balanceFormatted = Number(tokensReceived) / Math.pow(10, tokenDecimals)
  const sellAmount50Percent = balanceFormatted * 0.5

  console.log(`Token Balance: ${balanceFormatted.toFixed(6)} tokens`)
  console.log(`Selling 50%: ${sellAmount50Percent.toFixed(6)} tokens`)

  const sellQuote = await getQuote({
    token: params.token,
    amount: sellAmount50Percent,
    side: 'SELL',
    slippage: params.slippage,
    trader: traderAddress,
    chain: params.chain
  })

  if (!sellQuote.success || !sellQuote.transaction) {
    throw new Error(sellQuote.error || 'Failed to get sell quote')
  }

  console.log('\nSigning sell transaction...')
  const sellTx = ethers.Transaction.from(sellQuote.transaction.trade)
  const signedSellTx = await wallet.signTransaction(sellTx)

  let signedSellApproveTx
  if (sellQuote.transaction.approve) {
    console.log('Signing sell approval transaction...')
    const sellApproveTx = ethers.Transaction.from(sellQuote.transaction.approve)
    signedSellApproveTx = await wallet.signTransaction(sellApproveTx)
  }

  console.log('\nExecuting sell transaction...')
  const sellResult = await executeTransaction({
    txData: signedSellTx,
    network: params.chain,
    mev: false,
    skipSimulation: false,
    quoteId: sellQuote.transaction.quoteId,
    approveTxData: signedSellApproveTx
  })

  if (!sellResult.success) throw new Error(sellResult.error || 'Sell failed')

  console.log('\nSELL COMPLETED!')
  console.log(`TX Hash: ${sellResult.txRes?.txHash}`)
  console.log(`ETH Received: ${sellResult.txRes?.received}`)
  console.log(`Tokens Spent: ${sellResult.txRes?.spent}`)
  console.log(`Explorer: https://basescan.org/tx/${sellResult.txRes?.txHash}`)

  console.log('\n' + '='.repeat(80))
  console.log('TRADING FLOW COMPLETED SUCCESSFULLY!')
  console.log('='.repeat(80))
  console.log(`Buy TX: https://basescan.org/tx/${buyResult.txRes?.txHash}`)
  console.log(`Sell TX: https://basescan.org/tx/${sellResult.txRes?.txHash}`)
}

Advanced: With Custom Options

async function customBuyEVM(): Promise<void> {
  const wallet = new ethers.Wallet(TRADING_EVM_PKEY!)

  const params = {
    token: '0xFb31f85A8367210B2e4Ed2360D2dA9Dc2D2Ccc95',
    chain: 'BASE',
    trader: wallet.address,
    amount: 0.01,
    slippage: 800,
    priority: 'MEDIUM' as const,
    integrator: '0x4Ff5f9bC75C29903b0EABdEc445A6621556feF73',
    fee: 150
  }

  const quote = await getQuote({
    token: params.token,
    amount: params.amount,
    side: 'BUY',
    slippage: params.slippage,
    trader: params.trader,
    chain: params.chain,
    priority: params.priority,
    integrator: params.integrator,
    fee: params.fee
  })

  if (!quote.success || !quote.transaction) {
    throw new Error(quote.error || 'Quote failed')
  }

  const tx = ethers.Transaction.from(quote.transaction.trade)
  const signedTx = await wallet.signTransaction(tx)

  const result = await executeTransaction({
    txData: signedTx,
    network: params.chain,
    mev: false,
    skipSimulation: true,
    quoteId: quote.transaction.quoteId
  })

  console.log('Trade completed with custom options!')
  console.log(`TX Hash: ${result.txRes?.txHash}`)
}

Priority Fees

Get and use priority fee information:

async function getPriority(chain: string): Promise<any> {
  const response = await makeRequest<any>(`/api/v1/priority?network=${chain}`)
  
  console.log('Priority fees:')
  response.priorities.forEach((p: any) => {
    console.log(`  ${p.level}: ${p.params.maxFeePerGas || p.params.gasPrice}`)
  })
  
  return response
}

const priorityInfo = await getPriority('BASE')

const quote = await getQuote({
  token: '0x...',
  amount: 0.01,
  side: 'BUY',
  slippage: 500,
  trader: wallet.address,
  chain: 'BASE',
  priority: 'HIGH'
})

Token Information

Get token details before trading:

async function getTokenInfo(tokenAddress: string, chain: string): Promise<any> {
  const response = await makeRequest<any>(
    `/api/v1/token-info?tokenAddress=${tokenAddress}&chain=${chain}`
  )
  
  if (response.success) {
    console.log('Token Info:')
    console.log(`  Name: ${response.name || 'N/A'}`)
    console.log(`  Symbol: ${response.symbol || 'N/A'}`)
    console.log(`  Decimals: ${response.decimals}`)
    console.log(`  Price: $${response.priceUsd?.toFixed(6) || 'N/A'}`)
    console.log(`  Liquidity: $${response.liquidityUsd?.toLocaleString() || 'N/A'}`)
    console.log(`  Pool: ${response.poolAddress || 'N/A'}`)
  }
  
  return response
}

Balance Checking

Check native and token balances:

async function getBalance(walletAddress: string, network: string): Promise<any> {
  const response = await makeRequest<any>(
    `/api/v1/balance?walletAddress=${walletAddress}&network=${network}`
  )
  
  if (response.success) {
    const formatted = Number(response.balance) / Math.pow(10, response.decimals || 18)
    console.log(`Balance: ${formatted.toFixed(6)} ${network === 'SOL' ? 'SOL' : 'ETH'}`)
  }
  
  return response
}

async function getTokenBalance(
  walletAddress: string,
  tokenAddress: string,
  network: string
): Promise<any> {
  const response = await makeRequest<any>(
    `/api/v1/token-balance?walletAddress=${walletAddress}&tokenAddress=${tokenAddress}&network=${network}`
  )
  
  if (response.success) {
    const formatted = Number(response.balance) / Math.pow(10, response.decimals || 18)
    console.log(`Token Balance: ${formatted.toFixed(6)}`)
  }
  
  return response
}

Error Handling

async function safeTrade() {
  try {
    await simpleBuyEVM()
  } catch (error: any) {
    if (error.message.includes('Pool not found')) {
      console.error('Token not found or has no liquidity')
    } else if (error.message.includes('Insufficient balance')) {
      console.error('Not enough ETH in wallet')
    } else if (error.message.includes('Slippage')) {
      console.error('Price moved too much, increase slippage tolerance')
    } else if (error.message.includes('HTTP 401')) {
      console.error('Invalid API key')
    } else if (error.message.includes('HTTP 429')) {
      console.error('Rate limit exceeded')
    } else {
      console.error('Unexpected error:', error.message)
    }
  }
}

Running the Examples

Create a file evm-trading-test.ts:

import * as dotenv from 'dotenv'
dotenv.config()

async function main() {
  console.log('Testing EVM Trading Flow...')
  await evmFullTradingFlow()
}

main().catch(console.error)

Run it:

export NAOS_API_KEY="your-api-key"
export NAOS_API_DOMAIN="https://api.naos.trade"
export TRADING_EVM_PKEY="your-evm-private-key"

npx ts-node evm-trading-test.ts

Supported Networks

  • Base (8453) - Primary testnet

  • Ethereum (1)

  • BSC (56)

  • Arbitrum (42161)

  • Avalanche (43114)

  • Abstract

  • HyperEVM

  • Ink

  • Story

  • X Layer

  • Plasma

  • UniChain

Best Practices

Transaction Signing

  1. Always check for approval transactions: Handle approve if present

  2. Sign both transactions: Trade transaction and approval (if needed)

  3. Order matters: Pass approval as approveTxData parameter

Gas Management

  1. Use MEDIUM priority for most trades (good balance of speed/cost)

  2. Use HIGH for time-sensitive trades during volatile markets

  3. Monitor gas costs: Check txCostUsd in quote stats before executing

Slippage Settings

  1. 2-5% (200-500 bps) for liquid tokens

  2. 5-10% (500-1000 bps) for less liquid tokens

  3. Higher during volatility: Increase if trades keep failing

Security

  1. Store private keys securely: Use environment variables, never hardcode

  2. Validate quotes: Always check success field before executing

  3. Test with small amounts: Start with minimal values on testnet

  4. Monitor transactions: Track TX hashes on block explorer

Error Recovery

  1. Implement retries: With exponential backoff for network errors

  2. Check wallet balance: Before attempting trades

  3. Verify token liquidity: Use token-info endpoint first

  4. Handle approval failures: Retry or skip if already approved

Chain Detection

Auto-detect chain from token address:

async function detectChain(tokenAddress: string): Promise<string> {
  const response = await makeRequest<any>(
    `/api/v1/token-info?tokenAddress=${tokenAddress}`
  )
  return response.chain || 'BASE'
}

const chain = await detectChain('0xFb31f85A8367210B2e4Ed2360D2dA9Dc2D2Ccc95')
console.log(`Detected chain: ${chain}`)

MEV Protection

For large trades on Ethereum mainnet:

const result = await executeTransaction({
  txData: signedTx,
  network: 'ETH',
  mev: true,
  skipSimulation: false,
  quoteId: quote.transaction.quoteId
})

For complete playground with all scenarios, see: src/trading-suite/playground.ts

Last updated