Cookbook Recipes
The cookbook hosts small code snippets in a question-answer format. It does not walk you through the whole setup, it rather aims to answer some questions around specific uses.
You are viewing the Nexus SDK v2 docs. For the current SDK, see the v3 docs and the v2 → v3 migration guide.
Getting a client
NexusClient. Create one with createNexusClient(), then run the two-step init: initialize() loads deployment data (chains, tokens, vault contracts) and setEVMProvider() attaches a wallet. Call destroy() when you are done to flush analytics and release resources.
import { createNexusClient } from '@avail-project/nexus-core';
const client = createNexusClient({ network: 'testnet' });
await client.initialize(); // load deployment data
await client.setEVMProvider(window.ethereum); // attach an EIP-1193 wallet
// ... use the client ...
client.destroy(); // clean up when finishedNote
Create a fresh client on account change. Calling
setEVMProvider() again with the same provider instance is a no-op, so build a new client and re-run initialize() + setEVMProvider() when the connected account switches.SDK operations
How to simulate an operation on Nexus?
client.simulateBridge(), client.simulateBridgeAndExecute(), or client.simulateBridgeAndTransfer() to preview an operation without sending a transaction. The result contains an intent with selectedSources, availableSources, fees, and destination details.
// 1. Simulate a bridge
const bridgeSimulation = await client.simulateBridge({
toTokenSymbol: 'USDC',
toAmountRaw: 100_000_000n, // 100 USDC (6 decimals)
toChainId: 421614,
});
// 2. Simulate a bridgeAndExecute (bridge + contract call)
const bridgeAndExecuteSimulation = await client.simulateBridgeAndExecute({
toTokenSymbol: 'USDC',
toAmountRaw: 1_000_000n,
toChainId: 421614,
execute: {
to: '0xContractAddress',
data: encodedCalldata,
tokenApproval: {
toTokenSymbol: 'USDC',
amount: 1_000_000n,
spender: '0xContractAddress',
},
},
});
// 3. Simulate a bridgeAndTransfer (cross-chain send)
const bridgeAndTransferSimulation = await client.simulateBridgeAndTransfer({
toTokenSymbol: 'USDC',
toAmountRaw: 1_000_000n,
toChainId: 421614,
recipient: '0xRecipientAddress',
});
simulateBridge() returns { intent, token }. simulateBridgeAndExecute() and simulateBridgeAndTransfer() return { bridgeSimulation, executeSimulation }, where bridgeSimulation is null when no bridge is needed.
How do I get source chain and destination chain information from an intent?
intent.selectedSources to see which chains the SDK chose to pull funds from. Each source nests its chain and token: source.chain has id, name, and logo; source.token has symbol, decimals, logo, and contractAddress. Use intent.availableSources to see every chain that could be used.
const sim = await client.simulateBridge({
toTokenSymbol: 'USDC',
toAmountRaw: 100_000_000n,
toChainId: 421614,
});
// Sources chosen by the solver
sim.intent.selectedSources.forEach((source) => {
console.log(source.chain.name); // "Ethereum Sepolia"
console.log(source.chain.id); // 11155111
console.log(source.chain.logo); // "https://..."
console.log(source.amount); // "100.000102"
console.log(source.token.contractAddress); // "0x..."
});
// All chains that had balance (superset of selectedSources)
console.log(sim.intent.availableSources);
// Destination chain info
console.log(sim.intent.destination.chain.name);
console.log(sim.intent.destination.chain.id);
// Total amount pulled across all sources
console.log(sim.intent.sourcesTotal);
// ─── Alternative: use the intent hook during execution ───
await client.bridge(
{ toTokenSymbol: 'USDC', toAmountRaw: 100_000_000n, toChainId: 421614 },
{
hooks: {
onIntent: ({ intent, allow, deny, refresh }) => {
// Same BridgeIntent structure as the simulation
console.log(intent.selectedSources);
console.log(intent.destination);
allow(); // or deny() to cancel
},
},
}
);
How to fetch token logos and names?
token field, the nested token on each intent source/destination, or client.chainList.getTokenInfoBySymbol().
// ─── A: From a simulation result ───
const sim = await client.simulateBridge({
toTokenSymbol: 'USDC',
toAmountRaw: 100_000_000n,
toChainId: 421614,
});
sim.token.name; // "USD Coin"
sim.token.symbol; // "USDC"
sim.token.decimals; // 6
sim.token.logo; // "https://..."
// Source chain logos and token info
sim.intent.selectedSources[0].chain.logo; // "https://..."
sim.intent.selectedSources[0].chain.name; // "Ethereum Sepolia"
sim.intent.selectedSources[0].token.symbol; // "USDC"
/*----------------------------------------*/
// ─── B: From the chain catalogue on the client ───
const usdc = client.chainList.getTokenInfoBySymbol(421614, 'USDC');
// { contractAddress: "0x75faf...", decimals: 6, logo: "https://...", name: "USD Coin", symbol: "USDC" }
const native = client.chainList.getNativeToken(421614);
// native gas token info for the chain
/*----------------------------------------*/
// ─── C: getSupportedChains — chains with their token lists ───
const chains = client.getSupportedChains();
// [{ id: 421614, name: "Arbitrum Sepolia", logo: "...", swapSupported: true, tokens: [{ symbol, name, logo, contractAddress, ... }] }]Note
If a logo URL is missing or 404s,
getFallbackTokenLogoDataUri(symbol) (exported from the main entry) returns a deterministic gradient SVG data-URI as a stable placeholder.How to get the fees incurred from an intent?
BridgeIntent includes a fees object with a full breakdown: total, protocol, solver, caGas, and totalValue. All values are human-readable strings denominated in the intent's token (e.g. USDC), except totalValue which is the USD value of the total.
const sim = await client.simulateBridge({
toTokenSymbol: 'USDC',
toAmountRaw: 100_000_000n,
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.totalValue); // Total fee expressed in USD
How to view a wallet's intent history?
client.listIntents({ page?, status? }) to fetch the connected wallet's past intents. It returns { intents, total }, where each entry is an IntentRecord with sources, destinations, a status, and an explorer link. Page size is fixed at 20 records — paginate by incrementing page. Filter by status using the exported IntentStatus enum.
import { IntentStatus } from '@avail-project/nexus-core';
// Fetch the first page of intents
const { intents, total } = await client.listIntents();
// Paginate and/or filter by status
const page2 = await client.listIntents({ page: 2 });
const fulfilled = await client.listIntents({ status: IntentStatus.Fulfilled });
console.log(`Total intents: ${total}`);
// Check status ('created' | 'deposited' | 'fulfilled' | 'expired')
intents.forEach((intent) => {
if (intent.status === 'fulfilled') console.log(`${intent.requestHash}: fulfilled`);
else if (intent.status === 'expired') console.log(`${intent.requestHash}: expired`);
else console.log(`${intent.requestHash}: ${intent.status} (expires ${new Date(intent.expiry * 1000)})`);
console.log(intent.explorerUrl);
});
// Each IntentRecord has this shape:
// {
// requestHash: "0x...",
// explorerUrl: "https://...",
// status: "fulfilled",
// solver: "0x..." | null,
// expiry: 1706000000,
// recipientAddress: "0x...",
// destinationChain: { id: 421614, name: "Arbitrum Sepolia", logo: "...", universe: 0 },
// destinations: [{
// token: { contractAddress: "0x...", symbol: "USDC", name: "USD Coin", logo: "...", decimals: 6 },
// amount: "1.0",
// amountRaw: 1000000n,
// }],
// sources: [{
// chain: { id: 84532, name: "Base Sepolia", logo: "...", universe: 0 },
// token: { contractAddress: "0x...", symbol: "USDC", name: "USD Coin", logo: "...", decimals: 6 },
// amount: "1.05",
// amountRaw: 1050000n,
// fee: "0.05",
// feeRaw: 50000n,
// }],
// }Chain & Token Utilities
Note
The static metadata constants and helpers from v1 (
TOKEN_METADATA, CHAIN_METADATA, TOKEN_CONTRACT_ADDRESSES, getChainMetadata, getTokenMetadata, chainIdToHex, isSupportedToken, getSwapSupportedChainsAndTokens) have been removed. The SDK ships no hard-coded chain or token tables — everything is fetched from the live deployment. Use client.getSupportedChains(), client.isSupportedChain(), and the client.chainList.* lookups instead.How to get chain metadata?
client.chainList.getChainByID() for a full Chain object (name, native currency, block explorers) or client.getSupportedChains() for the lighter chain + token list.
// Full Chain object — name, native currency, block explorers
const chain = client.chainList.getChainByID(421614);
console.log(chain.name); // "Arbitrum Sepolia"
console.log(chain.custom.icon); // chain logo URL
console.log(chain.nativeCurrency); // { name: "ETH", symbol: "ETH", decimals: 18, logo: "..." }
console.log(chain.blockExplorers?.default?.url); // explorer base URL (guard: optional)
// Lighter list entry from getSupportedChains()
const entry = client.getSupportedChains().find((c) => c.id === 421614);
console.log(entry?.name); // "Arbitrum Sepolia"
console.log(entry?.logo); // "https://..."How to get the list of supported chains and tokens?
client.getSupportedChains() to get all chains with their supported tokens. Each entry carries a swapSupported boolean — filter on it to show only the chains a swap can use as a source or destination.
// Get all supported chains with their tokens
const chains = client.getSupportedChains();
chains.forEach((chain) => {
console.log(`${chain.name} (${chain.id}) — swapSupported: ${chain.swapSupported}`);
console.log(` Logo: ${chain.logo}`);
console.log(` Tokens:`);
chain.tokens.forEach((token) => {
console.log(` - ${token.symbol}: ${token.name} @ ${token.contractAddress}`);
});
});
// Chains a swap can use
const swapChains = chains.filter((chain) => chain.swapSupported);Note
Prefer the async standalone helper when you need this before a client exists:
import { getSupportedChains } from '@avail-project/nexus-core/utils' then await getSupportedChains('mainnet'). The client method is synchronous and uses cached deployment data.How to get the chain ID of a supported chain?
client.getSupportedChains() to find a chain by name, then access its id. You can also validate chain support with client.isSupportedChain().
// Find chain ID by name
const chains = client.getSupportedChains();
const baseSepolia = chains.find((c) => c.name === 'Base Sepolia');
console.log(baseSepolia?.id); // 84532
// Validate if a chain ID is supported
const isSupported = client.isSupportedChain(84532); // true
// Convert chain ID to hex (useful for wallet_switchEthereumChain)
const hexChainId = `0x${(84532).toString(16)}`; // "0x14a34"How to get the token address of a supported token?
client.chainList.getTokenInfoBySymbol(chainId, symbol) for a token on a specific chain, or iterate getSupportedChains() to collect a token's address across chains.
// Option A: Token info for a SPECIFIC chain
const usdcOnArbitrum = client.chainList.getTokenInfoBySymbol(421614, 'USDC');
console.log(usdcOnArbitrum.contractAddress); // "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d"
console.log(usdcOnArbitrum.decimals); // 6
// Option B: Look up a token by its on-chain address
const byAddress = client.chainList.getTokenByAddress(421614, '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d');
console.log(byAddress.symbol); // "USDC"
// Option C: Get all addresses for a token across chains
const chains = client.getSupportedChains();
const usdcAddresses = chains
.map((chain) => {
const token = chain.tokens.find((t) => t.symbol === 'USDC');
return token ? { chainId: chain.id, chainName: chain.name, address: token.contractAddress } : null;
})
.filter(Boolean);
console.log(usdcAddresses);
// [
// { chainId: 421614, chainName: "Arbitrum Sepolia", address: "0x75faf..." },
// { chainId: 84532, chainName: "Base Sepolia", address: "0x036cb..." },
// ...
// ]How to get the vault contract address for a chain?
client.chainList.getVaultContractAddress(chainId) to get the Nexus vault contract deployed on a chain.
const vault = client.chainList.getVaultContractAddress(421614);
console.log(vault); // "0x..."Balances
How to fetch a wallet's unified balances?
client.getBalancesForBridge() (or client.getBalancesForSwap() for the swap balance pipeline). Both return TokenBalance[] — each entry aggregates a token across chains with a per-chain breakdown in chainBalances.
const assets = await client.getBalancesForBridge();
assets.forEach((asset) => {
console.log(`${asset.symbol} (${asset.name})`);
console.log(` Total: ${asset.balance} (~$${asset.value})`);
console.log(` Decimals: ${asset.decimals}, Logo: ${asset.logo}`);
asset.chainBalances.forEach((cb) => {
console.log(` ${cb.chain.name}: ${cb.balance} @ ${cb.contractAddress}`);
});
});Events
How and when to listen to Nexus events?
onEvent callback in the options of a bridge or swap method. Events are a typed discriminated union keyed on event.type ('status', 'plan_preview', 'plan_confirmed', 'plan_progress'), emitted at key stages of the transaction lifecycle so you can build progress UIs.
Bridge Events:
const result = await client.bridge(
{
toTokenSymbol: 'USDC',
toAmountRaw: 1_000_000n, // 1 USDC (6 decimals)
toChainId: 421614,
},
{
onEvent: (event) => {
switch (event.type) {
case 'status':
// Lifecycle phase: intent_building → intent_ready → awaiting_approval → executing → completed
console.log('Status:', event.status);
break;
case 'plan_preview':
// Emitted once with the planned steps before execution
console.log('Planned steps:', event.plan.steps);
// Use this to initialize a progress stepper UI
break;
case 'plan_confirmed':
// Emitted after user approval with the final steps
console.log('Confirmed steps:', event.plan.steps);
break;
case 'plan_progress':
// Per-step progress. chain lives on event.step.chain, NOT event.chain
console.log(`Step ${event.stepType}: ${event.state}`);
if ((event.state === 'submitted' || event.state === 'confirmed') && 'txHash' in event) {
console.log(` Tx: ${event.txHash} (${event.explorerUrl})`);
}
if (event.state === 'failed' && 'error' in event) {
console.error(` Failed: ${event.error}`);
}
break;
}
},
hooks: {
onIntent: ({ allow }) => allow(),
onAllowance: ({ allow }) => allow(['min']),
},
}
);Note
Both
'confirmed' and 'completed' are terminal-success states depending on the step type. On-chain transaction steps settle on confirmed; off-chain orchestration steps (and vault_deposit) settle on completed. For a robust progress UI, treat either as success.hasBridge / hasDestinationSwap flags and swap-specific step types.
const result = await client.swapWithExactIn(
{
sources: [{ chainId: 10, amountRaw: 100_000_000n, tokenAddress: '0xUSDC...' }],
toChainId: 8453,
toTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
},
{
onEvent: (event) => {
if (event.type === 'plan_preview') {
console.log('Has bridge:', event.plan.hasBridge);
console.log('Has destination swap:', event.plan.hasDestinationSwap);
}
if (event.type === 'plan_progress') {
console.log(`Swap step ${event.stepType}: ${event.state}`);
// Grab explorer URLs from on-chain swap steps
if (
(event.stepType === 'source_swap' || event.stepType === 'destination_swap') &&
'txHash' in event
) {
console.log(` Swap tx: ${event.explorerUrl}`);
}
}
},
hooks: {
onIntent: ({ allow }) => allow(),
},
}
);How to review and approve intents and allowances?
options.hooks (there are no global setOnIntentHook / setOnAllowanceHook setters). Use them to review a bridge/transfer intent before execution and to control approval amounts.
await client.bridge(params, {
hooks: {
// Intent approval hook — review before execution
onIntent: async ({ intent, allow, deny, refresh }) => {
console.log('Selected sources:', intent.selectedSources);
console.log('Destination:', intent.destination);
console.log('Fees:', intent.fees);
// Optionally re-quote against different source chains
// const updated = await refresh([8453, 42161]);
if (userConfirmed) allow();
else deny();
},
// Allowance approval hook — control approval amounts
onAllowance: ({ sources, allow, deny }) => {
// Options: 'max', 'min', a bigint, or a per-source array
allow(['min']);
},
},
});Note
bridgeAndExecute() and swapAndExecute() use a top-level options.onIntent hook (not nested under hooks), and their intent data is a composite type that also reports whether a bridge/swap is actually needed. If no onAllowance hook is provided for a bridge operation, the SDK auto-approves with 'min'.Troubleshooting
How to solve Vite polyfill issues?
ReferenceError: Buffer is not defined or process is not defined, Vite needs Node.js polyfills.
Step 1: Install the polyfill plugin
pnpm add vite-plugin-node-polyfills
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',
},
});How to solve Turbopack issues in Next.js?
--turbo flag from your dev script:
{
"scripts": {
"dev": "next dev"
}
}/** @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;pnpm add crypto-browserify buffer stream-browserify
How to fix "require is not defined" in browser?
// Bad
const { createNexusClient } = require('@avail-project/nexus-core');
// Good
import { createNexusClient } from '@avail-project/nexus-core';export async function GET() {
// Node.js modules work here
return Response.json({ data: 'ok' });
}if (typeof window === 'undefined') {
const serverModule = await import('server-only-module');
}How is this guide?
Getting a clientSDK operationsHow to simulate an operation on Nexus?How do I get source chain and destination chain information from an intent?How to fetch token logos and names?How to get the fees incurred from an intent?How to view a wallet's intent history?Chain & Token UtilitiesHow to get chain metadata?How to get the list of supported chains and tokens?How to get the chain ID of a supported chain?How to get the token address of a supported token?How to get the vault contract address for a chain?BalancesHow to fetch a wallet's unified balances?EventsHow and when to listen to Nexus events?How to review and approve intents and allowances?TroubleshootingHow to solve Vite polyfill issues?How to solve Turbopack issues in Next.js?How to fix "require is not defined" in browser?