Skip to Content
Avail Nexus is now live! Check out our docs to get started.
Avail NexusCookbook Recipes

Cookbook

The cookbook hosts small code snippets in a question-answer format. It does not walk you through the whole setup like the “Build your First Liquid App” section does.

It rather aims to answer some questions around specific uses. It also does not replace FAQs that we may have rather this is focussed explicitly on code samples, instead of general trouble-shooting for common mishaps.

They show developer patterns on how the SDK is expected to be used by a developer or an LLM agent.

SDK operations

How to simulate an operation on Nexus?

Call sdk.simulateBridge(), sdk.simulateBridgeAndExecute(), or sdk.simulateBridgeAndTransfer() to preview an operation without sending a transaction. The result contains an intent with sources, fees, and token metadata.

import { sdk } from '@avail-project/nexus'; // 1. Simulate a bridge const bridgeSimulation = await sdk.simulateBridge({ token: 'USDC', amount: BigInt(100), toChainId: 421614, }); // 2. Simulate a bridgeAndExecute (bridge + contract call) const bridgeAndExecuteSimulation = await sdk.simulateBridgeAndExecute({ token: 'USDC', amount: BigInt(1_000_000), toChainId: 421614, execute: { to: '0xContractAddress', data: encodedCalldata, tokenApproval: { token: 'USDC', amount: BigInt(1_000_000), spender: '0xContractAddress', }, }, }); // 3. Simulate a bridgeAndTransfer (cross-chain send) const bridgeAndTransferSimulation = await sdk.simulateBridgeAndTransfer({ token: 'USDC', amount: BigInt(1_000_000), toChainId: 421614, recipient: '0xRecipientAddress', });

The response structure for the above example can be found here .

Sample response for simulateBridge()

{ "intent": { "allSources": [ { "amount": "6245.786635", "chainID": 11155111, "chainLogo": "https://assets.coingecko.com/asset_platforms/images/279/large/ethereum.png", "chainName": "Ethereum Sepolia", "contractAddress": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238" }, { "amount": "754.958732", "chainID": 84532, "chainLogo": "https://assets.coingecko.com/asset_platforms/images/131/large/base-network.png", "chainName": "Base Sepolia", "contractAddress": "0x036cbd53842c5426634e7929541ec2318f3dcf7e" }, { "amount": "750", "chainID": 11155420, "chainLogo": "https://assets.coingecko.com/coins/images/25244/large/Optimism.png", "chainName": "OP Sepolia", "contractAddress": "0x5fd84259d66cd46123540766be93dfe6d43130d7" }, { "amount": "1", "chainID": 421614, "chainLogo": "https://assets.coingecko.com/coins/images/16547/large/arb.jpg", "chainName": "Arbitrum Sepolia", "contractAddress": "0x75faf114eafb1bdbe2f0316df893fd58ce46aa4d" } ], "destination": { "amount": "0.0001", "chainID": 421614, "chainLogo": "https://assets.coingecko.com/coins/images/16547/large/arb.jpg", "chainName": "Arbitrum Sepolia" }, "fees": { "caGas": "0.000000", "gasSupplied": "0", "protocol": "0.0000005", "solver": "0.000001", "total": "0.000002" }, "sources": [ { "amount": "0.0001015", "chainID": 11155111, "chainLogo": "https://assets.coingecko.com/asset_platforms/images/279/large/ethereum.png", "chainName": "Ethereum Sepolia", "contractAddress": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238" } ], "sourcesTotal": "0.000102", "token": { "decimals": 6, "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "name": "USD Coin", "symbol": "USDC" } }, "token": { "contractAddress": "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d", "decimals": 6, "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "name": "USD Coin", "symbol": "USDC", "isNative": false } }

How do I get source chain and destination chain information from an intent?

After simulating, access intent.sources to see which chains the SDK chose to pull funds from. Each source has chainID,chainName, chainLogo, and amount. Use intent.allSources to see every chain that could be used.

const sim = await sdk.simulateBridge({ token: 'USDC', amount: BigInt(100), toChainId: 421614, }); // Sources chosen by the solver sim.intent.sources.forEach((source) => { console.log(source.chainName); // "Arbitrum Sepolia" console.log(source.chainID); // 421614 console.log(source.chainLogo); // "https://..." console.log(source.amount); // "100" console.log(source.contractAddress); // "0x..." }); // All chains that had balance (superset of sources) console.log(sim.intent.allSources); // Destination chain info console.log(sim.intent.destination.chainName); console.log(sim.intent.destination.chainID); // Total amount pulled across all sources console.log(sim.intent.sourcesTotal); // ─── Alternative: use the intent hook during execution ─── sdk.setOnIntentHook(({ intent, allow, deny, refresh }) => { // Same ReadableIntent structure as the simulation console.log(intent.sources); console.log(intent.destination); allow(); // or deny() to cancel });

Sample response

{ "sources": [ { "chainId": 11155111, "chainName": "Ethereum Sepolia", "chainLogo": "https://assets.coingecko.com/asset_platforms/images/279/large/ethereum.png", "amount": "0.0001015", "contractAddress": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238" } ], "allSourcesAvailable": [ { "amount": "6245.786635", "chainID": 11155111, "chainLogo": "https://assets.coingecko.com/asset_platforms/images/279/large/ethereum.png", "chainName": "Ethereum Sepolia", "contractAddress": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238" }, { "amount": "754.958732", "chainID": 84532, "chainLogo": "https://assets.coingecko.com/asset_platforms/images/131/large/base-network.png", "chainName": "Base Sepolia", "contractAddress": "0x036cbd53842c5426634e7929541ec2318f3dcf7e" }, { "amount": "750", "chainID": 11155420, "chainLogo": "https://assets.coingecko.com/coins/images/25244/large/Optimism.png", "chainName": "OP Sepolia", "contractAddress": "0x5fd84259d66cd46123540766be93dfe6d43130d7" }, { "amount": "1", "chainID": 421614, "chainLogo": "https://assets.coingecko.com/coins/images/16547/large/arb.jpg", "chainName": "Arbitrum Sepolia", "contractAddress": "0x75faf114eafb1bdbe2f0316df893fd58ce46aa4d" } ], "destination": { "amount": "0.0001", "chainID": 421614, "chainLogo": "https://assets.coingecko.com/coins/images/16547/large/arb.jpg", "chainName": "Arbitrum Sepolia" }, "sourcesTotal": "0.000102" }

How to fetch token logos and names?

Token metadata (name, symbol, decimals, logo) is available from multiple sources: the simulation result’sintent.token, the staticTOKEN_METADATA map, or sdk.utils.getSupportedChains() which returns each chain’s supported tokens.

import { TOKEN_METADATA, CHAIN_METADATA, } from '@avail-project/nexus'; // ─── A: From a simulation result ─── const sim = await sdk.simulateBridge({ token: 'USDC', amount: BigInt(100), toChainId: 421614 }); sim.intent.token.name; // "USD Coin" sim.intent.token.symbol; // "USDC" sim.intent.token.decimals; // 6 sim.intent.token.logo; // "https://..." // Source chain logos sim.intent.sources[0].chainLogo; // "https://..." sim.intent.sources[0].chainName; // "Base Sepolia" /*----------------------------------------*/ // ─── B: From static metadata constants ─── TOKEN_METADATA['USDC']; // { symbol: "USDC", name: "USD Coin", decimals: 6, icon: "https://...", coingeckoId: "usd-coin" } CHAIN_METADATA[421614]; // { id: 421614, name: "Arbitrum Sepolia", logo: "https://...", nativeCurrency: {...}, ... } /*----------------------------------------*/ // ─── C: getSupportedChains — chains with their token lists ─── const chains = sdk.utils.getSupportedChains(); // [{ id: 421614, name: "Arbitrum Sepolia", logo: "...", tokens: [{ symbol, name, logo, ... }] }]

How to get the fees incurred from an intent?

Every ReadableIntent includes a fees object with a full breakdown: total, protocol,solver, caGas, and gasSupplied. All values are human-readable strings denominated in the intent’s token (e.g. USDC).

const sim = await sdk.simulateBridge({ token: 'USDC', amount: BigInt(100), toChainId: 421614, }); const { fees } = sim.intent; console.log(fees.total); // Total fee in token units (e.g. "0.05") console.log(fees.protocol); // Protocol fee console.log(fees.solver); // Solver / fulfillment fee console.log(fees.caGas); // Chain-abstraction gas fee console.log(fees.gasSupplied); // Gas supplied by the solver

How to view a wallet’s intent history?

Call sdk.getMyIntents(page?) to fetch the connected wallet’s past intents. Each entry is anRFF(Request For Funds) object with sources, destinations, status flags, and explorer links.

// Fetch the first page of intents const intents = await sdk.getMyIntents(); // Paginate const page2 = await sdk.getMyIntents(2); // Check status intents.forEach((intent) => { if (intent.fulfilled) console.log(`Intent #${intent.id}: fulfilled`); else if (intent.refunded) console.log(`Intent #${intent.id}: refunded`); else console.log(`Intent #${intent.id}: pending (expires ${new Date(intent.expiry * 1000)})`); }); // Each intent (RFF) has this shape: // { // id: 42, // explorerUrl: "https://...", // deposited: true, // fulfilled: true, // refunded: false, // expiry: 1706000000, // destinationChain: { id: 421614, name: "Arbitrum Sepolia", logo: "...", universe: "..." }, // destinations: [{ // token: { address: "0x...", symbol: "USDC", decimals: 6 }, // value: "1.0", // valueRaw: 1000000n, // }], // sources: [{ // chain: { id: 84532, name: "Base Sepolia", logo: "...", universe: "..." }, // token: { address: "0x...", symbol: "USDC", decimals: 6 }, // value: "1.05", // valueRaw: 1050000n, // }], // }

Chain & Token Utilities

How to get chain metadata?

Use CHAIN_METADATA constant for static lookups or sdk.utils.getChainMetadata() for SDK-managed access. Both return chain information including name, logo, and native currency.

import { CHAIN_METADATA, NexusSDK } from '@avail-project/nexus-core'; // Option A: Static constant (no SDK initialization needed) const arbitrumSepolia = CHAIN_METADATA[421614]; console.log(arbitrumSepolia.name); // "Arbitrum Sepolia" console.log(arbitrumSepolia.logo); // "https://..." console.log(arbitrumSepolia.nativeCurrency); // { name: "ETH", symbol: "ETH", decimals: 18 } // Option B: SDK utility method const sdk = new NexusSDK({ network: 'testnet' }); const chainMeta = sdk.utils.getChainMetadata(421614); console.log(chainMeta?.name); // "Arbitrum Sepolia"

Sample CHAIN_METADATA structure

{ "421614": { "id": 421614, "name": "Arbitrum Sepolia", "logo": "https://assets.coingecko.com/coins/images/16547/large/arb.jpg", "nativeCurrency": { "name": "Ethereum", "symbol": "ETH", "decimals": 18 } } }

How to get the list of supported chains and tokens?

Call sdk.utils.getSupportedChains() to get all chains with their supported tokens. For swap-specific chains, use sdk.utils.getSwapSupportedChainsAndTokens().

import { NexusSDK } from '@avail-project/nexus-core'; const sdk = new NexusSDK({ network: 'mainnet' }); // Get all supported chains with their tokens const chains = sdk.utils.getSupportedChains(); chains.forEach((chain) => { console.log(`${chain.name} (${chain.id})`); console.log(` Logo: ${chain.logo}`); console.log(` Tokens:`); chain.tokens?.forEach((token) => { console.log(` - ${token.symbol}: ${token.name}`); }); }); // For swap-specific chains and tokens const swapOptions = sdk.utils.getSwapSupportedChainsAndTokens();

Sample getSupportedChains() response

[ { "id": 421614, "name": "Arbitrum Sepolia", "logo": "https://assets.coingecko.com/coins/images/16547/large/arb.jpg", "tokens": [ { "symbol": "USDC", "name": "USD Coin", "decimals": 6, "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "address": "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d" }, { "symbol": "ETH", "name": "Ethereum", "decimals": 18, "logo": "https://assets.coingecko.com/coins/images/279/large/ethereum.png", "isNative": true } ] } ]

How to get the chain ID of a supported chain?

Use sdk.utils.getSupportedChains() to find a chain by name, then access its id. You can also validate chain support with isSupportedChain() and convert between decimal and hex formats.

import { NexusSDK } from '@avail-project/nexus-core'; const sdk = new NexusSDK({ network: 'testnet' }); // Find chain ID by name const chains = sdk.utils.getSupportedChains(); const baseSepolia = chains.find((c) => c.name === 'Base Sepolia'); console.log(baseSepolia?.id); // 84532 // Validate if a chain ID is supported const isSupported = sdk.utils.isSupportedChain(84532); // true // Convert chain ID to hex (useful for wallet_switchEthereumChain) const hexChainId = sdk.utils.chainIdToHex(84532); // "0x14a34" // Convert hex back to decimal const decimalId = sdk.utils.hexToChainId('0x14a34'); // 84532

How to get the token address of a supported token?

Get token metadata with sdk.utils.getTokenMetadata() for base info, or iterate getSupportedChains() to find addresses on specific chains.

import { NexusSDK } from '@avail-project/nexus-core'; const sdk = new NexusSDK({ network: 'testnet' }); // Option A: Get token metadata (base info without chain-specific addresses) const usdcMeta = sdk.utils.getTokenMetadata('USDC'); console.log(usdcMeta?.symbol); // "USDC" console.log(usdcMeta?.decimals); // 6 console.log(usdcMeta?.icon); // "https://..." // Option B: Get token address for a SPECIFIC chain const chains = sdk.utils.getSupportedChains(); const arbitrumSepolia = chains.find((c) => c.id === 421614); const usdcOnArbitrum = arbitrumSepolia?.tokens?.find((t) => t.symbol === 'USDC'); console.log(usdcOnArbitrum?.address); // "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d" // Option C: Get all addresses for a token across chains const usdcAddresses = chains .map((chain) => { const token = chain.tokens?.find((t) => t.symbol === 'USDC'); return token ? { chainId: chain.id, chainName: chain.name, address: token.address } : null; }) .filter(Boolean); console.log(usdcAddresses); // [ // { chainId: 421614, chainName: "Arbitrum Sepolia", address: "0x75faf..." }, // { chainId: 84532, chainName: "Base Sepolia", address: "0x036cb..." }, // ... // ] // Validate if a token is supported const isSupported = sdk.utils.isSupportedToken('USDC'); // true

Events

How and when to listen to Nexus events?

Use the onEvent callback when calling bridge or swap methods. Events are emitted at key stages of the transaction lifecycle, allowing you to build progress UIs.

Bridge Events:

import { NexusSDK, NEXUS_EVENTS } from '@avail-project/nexus-core'; const sdk = new NexusSDK({ network: 'testnet' }); await sdk.initialize(window.ethereum); const result = await sdk.bridge( { token: 'USDC', amount: BigInt(1_000_000), // 1 USDC (6 decimals) toChainId: 421614, }, { onEvent: (event) => { switch (event.name) { case NEXUS_EVENTS.STEPS_LIST: // Emitted when intent is created - contains all steps console.log('All steps:', event.args); // Use this to initialize a progress stepper UI break; case NEXUS_EVENTS.STEP_COMPLETE: // Emitted for each completed step const step = event.args; console.log(`Step completed: ${step.type}`); console.log(` Status: ${step.status}`); if (step.explorerURL) { console.log(` Explorer: ${step.explorerURL}`); } break; } }, } );

Sample STEPS_LIST event output

{ "name": "STEPS_LIST", "args": [ { "type": "ALLOWANCE_APPROVAL_REQUEST", "typeID": "AUA_11155111", "status": "pending" }, { "type": "ALLOWANCE_APPROVAL_MINED", "typeID": "AAM_11155111", "status": "pending" }, { "type": "INTENT_DEPOSIT_REQUEST", "typeID": "ID_1", "status": "pending" }, { "type": "INTENT_SUBMITTED", "typeID": "IS", "status": "pending" }, { "type": "INTENT_FULFILLED", "typeID": "IF", "status": "pending" } ] }

Sample STEP_COMPLETE event output

{ "name": "STEP_COMPLETE", "args": { "type": "INTENT_FULFILLED", "typeID": "IF", "status": "complete", "explorerURL": "https://sepolia.arbiscan.io/tx/0x..." } }

Swap Events:

import { NexusSDK, NEXUS_EVENTS } from '@avail-project/nexus-core'; const sdk = new NexusSDK({ network: 'mainnet' }); await sdk.initialize(window.ethereum); const result = await sdk.swapWithExactIn( { fromToken: 'USDC', toToken: 'ETH', amount: BigInt(100_000_000), // 100 USDC toChainId: 42161, }, { onEvent: (event) => { if (event.name === NEXUS_EVENTS.SWAP_STEP_COMPLETE) { const step = event.args; console.log(`Swap step: ${step.type}`); // Get explorer URLs from specific steps if (step.type === 'SOURCE_SWAP_HASH' && step.explorerURL) { console.log(`Source swap tx: ${step.explorerURL}`); } if (step.type === 'DESTINATION_SWAP_HASH' && step.explorerURL) { console.log(`Destination swap tx: ${step.explorerURL}`); } } }, } );

Swap step types in order

SWAP_START DETERMINING_SWAP CREATE_PERMIT_EOA_TO_EPHEMERAL CREATE_PERMIT_FOR_SOURCE_SWAP SOURCE_SWAP_BATCH_TX SOURCE_SWAP_HASH ← Contains explorerURL BRIDGE_DEPOSIT RFF_ID DESTINATION_SWAP_BATCH_TX DESTINATION_SWAP_HASH ← Contains explorerURL SWAP_COMPLETE

Alternative: Global Hooks

For persistent event handling across multiple operations:

// Intent approval hook - review before execution sdk.setOnIntentHook(({ intent, allow, deny, refresh }) => { console.log('Sources:', intent.sources); console.log('Destination:', intent.destination); console.log('Fees:', intent.fees); // Programmatically approve or deny if (userConfirmed) { allow(); } else { deny(); } }); // Allowance approval hook - control approval amounts sdk.setOnAllowanceHook(({ sources, allow, deny }) => { allow(['min']); // Options: 'max', 'min', or custom bigint[] });

Troubleshooting

How to solve Vite polyfill issues?

If you see errors like ReferenceError: Buffer is not defined or process is not defined, Vite needs Node.js polyfills.

Step 1: Install the polyfill plugin

Terminal
npm install vite-plugin-node-polyfills

Step 2: Update vite.config.ts

vite.config.ts
import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { nodePolyfills } from 'vite-plugin-node-polyfills'; export default defineConfig({ plugins: [ react(), nodePolyfills({ include: ['buffer', 'crypto', 'stream', 'util'], globals: { Buffer: true, global: true, process: true, }, }), ], define: { global: 'globalThis', }, });

Quick start alternative: Clone the Nexus Vite Template  which has polyfills pre-configured.

How to solve Turbopack issues in Next.js?

Turbopack (Next.js experimental bundler) has limited support for Node.js polyfills. If you encounter module resolution errors:

Option A: Disable Turbopack (Recommended)

Remove the --turbo flag from your dev script:

package.json
{ "scripts": { "dev": "next dev" } }

Option B: Configure Webpack fallbacks

next.config.js
/** @type {import('next').NextConfig} */ const nextConfig = { webpack: (config, { isServer }) => { if (!isServer) { config.resolve.fallback = { ...config.resolve.fallback, crypto: require.resolve('crypto-browserify'), buffer: require.resolve('buffer/'), stream: require.resolve('stream-browserify'), }; } return config; }, }; module.exports = nextConfig;

Install required packages:

Terminal
npm install crypto-browserify buffer stream-browserify

Quick start alternative: Clone the Nexus Next.js Template  which has proper configuration.

How to fix “require is not defined” in browser?

This error occurs when code meant for Node.js runs in the browser. Solutions:

1. Use ES module imports instead of require():

// Bad const sdk = require('@avail-project/nexus-core'); // Good import { NexusSDK } from '@avail-project/nexus-core';

2. Move Node.js-specific code to API routes or Server Components:

app/api/data/route.ts
export async function GET() { // Node.js modules work here return Response.json({ data: 'ok' }); }

3. Use dynamic imports for server-only code:

if (typeof window === 'undefined') { const serverModule = await import('server-only-module'); }
Last updated on