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'); // 84532How 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'); // trueEvents
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_COMPLETEAlternative: 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
npm install vite-plugin-node-polyfillsStep 2: Update 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:
{
"scripts": {
"dev": "next dev"
}
}Option B: Configure Webpack fallbacks
/** @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:
npm install crypto-browserify buffer stream-browserifyQuick 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:
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');
}