Swap API

Nabla Finance API for token swaps.

Overview

The Nabla Finance Swap API provides an efficient on-chain routing system that enables users and developers to perform token swaps directly through the Nabla Portal smart contracts. Unlike centralized or custodial aggregators, Nabla operates entirely on-chain — ensuring transparent, verifiable execution and predictable pricing.

The API serves as a simple interface layer to generate calldata and quotes for Nabla Portal transactions. It leverages live price feeds streamed from the Nabla Antenna to ensure that all swaps are priced against the most recent on-chain oracle data.

Key benefits

  • Accurate real-time pricing: The API uses Nabla’s live price update feed to provide the freshest oracle-signed price data for all supported tokens.

  • Single-hop efficiency: Swaps are executed directly through the Nabla Portal without routing across multiple DEXes, minimizing gas usage and reducing latency.

  • Secure execution: All calldata is generated server-side using verified price data and passed directly to the Nabla smart contracts — ensuring transparent, deterministic execution.

  • Developer-friendly design: The REST interface provides standardized endpoints for quoting swaps, generating calldata, and retrieving supported tokens, making it easy to integrate Nabla into bots, frontends, or backends.

  • Predictable pricing and slippage control: The API includes built-in parameters for slippage tolerance and maximum trade deviation, ensuring consistent execution even in volatile markets.

Supported networks

  • Arbitrum One (chain id: 42161)

  • Base (chain id: 8453)

  • Berachain (chain id: 80094)

  • Hyperliquid (chain id: 999)

API reference

For detailed information about each endpoint, refer to the Nabla Finance Swap API swagger docs:


Quick Start

Script

Example swap from USDC to WETH on Arbitrum One.

// swap.js
const { ethers } = require("ethers");
const dotenv = require("dotenv");
const fetch = require("node-fetch"); // ensure installed: npm install node-fetch@2

// Load .env from the same directory
dotenv.config({ path: "./.env" });

// === ENVIRONMENT CHECKS ===
const required = [
  "WALLET_ADDRESS",
  "PRIVATE_KEY",
  "RPC_URL",
  "API_URL", // Nabla Finance Swap API (https://swap-api.nabla.fi)
];
for (const key of required) {
  if (!process.env[key]) {
    console.error(`Missing required environment variable: ${key}`);
    process.exit(1);
  }
}

// === CONFIG ===
const config = {
  apiBase: process.env.API_URL,
  chainId: 42161,
  walletAddress: process.env.WALLET_ADDRESS.toLowerCase(),
  privateKey: process.env.PRIVATE_KEY,
  rpcUrl: process.env.RPC_URL,

  // Tokens
  fromToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC on Arbitrum
  toToken: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // WETH on Arbitrum

  // Amounts & Slippage
  amountToSwap: 500_000_000, // 500 USDC (6 decimals)
  slippage: 0.5,
};

// === SETUP WALLET & PROVIDER ===
const provider = new ethers.JsonRpcProvider(config.rpcUrl);
const wallet = new ethers.Wallet(config.privateKey, provider);

// === HELPERS ===
function buildQuery(params) {
  return new URLSearchParams(params).toString();
}

async function fetchJson(url) {
  const response = await fetch(url);
  if (!response.ok) {
    const text = await response.text();
    throw new Error(`Fetch failed (${response.status}): ${text}`);
  }
  return response.json();
}

// === APPROVAL LOGIC ===
async function approveWhenNeeded(amount) {
  console.log("Checking approval...");

  const query = buildQuery({
    tokenAddress: config.fromToken,
    walletAddress: config.walletAddress,
  });
  const url = `${config.apiBase}/chains/${config.chainId}/approve/allowance?${query}`;
  
  const data = await fetchJson(url);

  if (data.allowance >= amount) {
    console.log("Sufficient allowance, no approval needed.");
    return;
  }
  
  console.log("Insufficient allowance — requesting approval...");
  await approve(amount);
}

async function approve(amount) {
  const query = buildQuery({
    tokenAddress: config.fromToken,
    amount: amount,
  });
  const url = `${config.apiBase}/chains/${config.chainId}/approve/transaction?${query}`;

  const data = await fetchJson(url);
 
  const tx = {
    to: data.tx.to,
    data: data.tx.data,
    value: data.tx.value || 0,
    gasLimit: data.tx.gas || undefined,
    gasPrice: data.tx.gasPrice || undefined,
  };

  const sentTx = await wallet.sendTransaction(tx);
  console.log("Approval TX Hash:", sentTx.hash);

  const receipt = await sentTx.wait();
  console.log("Approval confirmed in block:", receipt.blockNumber);
}

// === SWAP LOGIC ===
async function executeSwap(tx) {
  const transaction = {
    to: tx.to,
    data: tx.data,
    value: tx.value || 0,
    gasLimit: tx.gas || undefined,
    gasPrice: tx.gasPrice || undefined,
  };

  console.log("Sending swap transaction...");
  const sentTx = await wallet.sendTransaction(transaction);
  console.log("Swap TX Hash:", sentTx.hash);

  const receipt = await sentTx.wait();
  console.log("Swap confirmed in block:", receipt.blockNumber);
  
  return receipt;
}

async function performSwap() {
  console.log("Performing swap...");

  const query = buildQuery({
    tokenIn: config.fromToken,
    tokenOut: config.toToken,
    amountIn: config.amountToSwap,
    maxSlippage: config.slippage,
    fromAddress: config.walletAddress,
  });

  const url = `${config.apiBase}/chains/${config.chainId}/swap?${query}`;
  const data = await fetchJson(url);

  await executeSwap(data.tx);
}

// === MAIN ===
(async function main() {
  try {
    await approveWhenNeeded(config.amountToSwap);
    await performSwap();
  } catch (err) {
    console.error("Error:", err.message || err);
    process.exit(1);
  }
})();

.env (place in same directory)

WALLET_ADDRESS=
PRIVATE_KEY=
RPC_URL=https://arb1.arbitrum.io/rpc
API_URL=https://swap-api.nabla.fi

Last updated