# Avail Nexus Documentation > Cross-chain unification — SDK, concepts, contracts, and UI elements. Full SDK API reference (TypeDoc): https://availproject.github.io/nexus-sdk/ ## Avail Nexus - [**Get Started**](/docs/nexus/get-started) — Understand what Nexus does, how it works, and choose the right integration path for your app. - [**Concepts**](/docs/nexus/concepts) — Learn about chain abstraction, intents, solvers, and cross-chain operations. - [**Nexus SDK**](/docs/nexus/nexus-sdk/get-started/installation) — TypeScript API reference for bridging, swaps, intent management, and balance queries. - [**Widgets**](/docs/nexus/widgets) — Pre-built React components for cross-chain bridging, swaps, and transaction management. - [**Cookbook Recipes**](/docs/nexus/cookbook-recipes) — Ready-to-use code snippets and patterns for common SDK use cases and integrations. ## Concepts - [**What is Chain Abstraction?**](/docs/nexus/concepts/chain-abstraction) — Eliminate manual bridging, chain switching, & wallet complexity when transacting across chains. - [**What is an Intent?**](/docs/nexus/concepts/intent) — A signed Request For Funds describing sources, destination, and fees — filled by solvers on the destination chain. - [**What are Solvers?**](/docs/nexus/concepts/solvers) — Market makers that front their own capital to fill intents in seconds, and are reimbursed at settlement. - [**How Intents are Processed**](/docs/nexus/concepts/intent-lifecycle) — The journey of an intent from quote to settlement — one signature, source deposits, solver fulfilment. - [**Bridge, Transfer & Execute**](/docs/nexus/concepts/nexus-ops) — Move tokens to yourself, send to another address, or bridge and call any smart contract in one action. - [**Crosschain Swaps**](/docs/nexus/concepts/xcs-swaps) — Swap any token on any chain to any other token on any chain using ExactIn or ExactOut methods. - [**Liquidity Routing**](/docs/nexus/concepts/source-chain-selection) — Control which chains your tokens are sourced from, or let Nexus optimize the route automatically. - [**Token Allowances**](/docs/nexus/concepts/allowances) — Approve tokens for Nexus smart contracts to enable cross-chain operations on your behalf. - [**Understanding Balance Types**](/docs/nexus/concepts/bridge-v-swap) — Why Bridge and Swap operations use different token balances, and when to use each. ## Contract Addresses Avail Nexus deploys Vault contracts on each supported chain. These contracts escrow user funds during cross-chain operations. ### Vault Contracts — Mainnet | Chain | Chain ID | Vault Contract Address | |-------|----------|----------------------| | Arc Mainnet | 5042 | 0xa009A9Fdc0eF474e7A3938a89f80329139291568 | | Ethereum | 1 | 0xa009A9Fdc0eF474e7A3938a89f80329139291568 | | Optimism | 10 | 0xa009A9Fdc0eF474e7A3938a89f80329139291568 | | BNB Chain | 56 | 0xa009A9Fdc0eF474e7A3938a89f80329139291568 | | Polygon | 137 | 0xa009A9Fdc0eF474e7A3938a89f80329139291568 | | Monad | 143 | 0xa009A9Fdc0eF474e7A3938a89f80329139291568 | | Hyperliquid (HyperEVM) | 999 | 0xa009A9Fdc0eF474e7A3938a89f80329139291568 | | Citrea | 4114 | 0xa009A9Fdc0eF474e7A3938a89f80329139291568 | | MegaETH | 4326 | 0xa009A9Fdc0eF474e7A3938a89f80329139291568 | | Base | 8453 | 0xa009A9Fdc0eF474e7A3938a89f80329139291568 | | Arbitrum One | 42161 | 0xa009A9Fdc0eF474e7A3938a89f80329139291568 | | Avalanche C-Chain | 43114 | 0xa009A9Fdc0eF474e7A3938a89f80329139291568 | ### Vault Contracts — Testnet | Chain | Chain ID | Vault Contract Address | |-------|----------|----------------------| | Arc Testnet | 5042002 | 0x86B60E813f9b739516dDbDc443526be5Ef8336aa | | Polygon Amoy | 80002 | 0x86B60E813f9b739516dDbDc443526be5Ef8336aa | | Monad Testnet | 10143 | 0x86B60E813f9b739516dDbDc443526be5Ef8336aa | | Base Sepolia | 84532 | 0x86B60E813f9b739516dDbDc443526be5Ef8336aa | | Ethereum Sepolia | 11155111 | 0x86B60E813f9b739516dDbDc443526be5Ef8336aa | | OP Sepolia | 11155420 | 0x86B60E813f9b739516dDbDc443526be5Ef8336aa | | Arbitrum Sepolia | 421614 | 0x86B60E813f9b739516dDbDc443526be5Ef8336aa | | Citrea Testnet | 5115 | 0x86B60E813f9b739516dDbDc443526be5Ef8336aa | ### USDC Token Addresses — Mainnet | Chain | Token | Contract Address | |-------|-------|-----------------| | Ethereum | USDC | 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 | | Base | USDC | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 | | Polygon | USDC | 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359 | | Arbitrum One | USDC | 0xaf88d065e77c8cC2239327C5EDb3A432268e5831 | | Optimism | USDC | 0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85 | | Avalanche C-Chain | USDC | 0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E | | BNB Chain | Binance-Peg USDC | 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d | | Hyperliquid (HyperEVM) | USDC | 0xb88339CB7199b77E23DB6E890353E22632Ba630f | | Monad | USDC | 0x754704Bc059F8C67012fEd69BC8A327a5aafb603 | | Citrea | USDC.e | 0xE045e6c36cF77FAA2CfB54466D71A3aEF7bbE839 | | MegaETH | USDm | 0xFAfDdbb3FC7688494971a79cc65DCa3EF82079E7 | ### USDC Token Addresses — Testnet | Chain | Token | Contract Address | |-------|-------|-----------------| | Ethereum Sepolia | USDC | 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 | | Base Sepolia | USDC | 0x036CbD53842c5426634e7929541eC2318f3dCF7e | | Arbitrum Sepolia | USDC | 0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d | | OP Sepolia | USDC | 0x5fd84259d66Cd46123540766Be93DFE6D43130D7 | | Polygon Amoy | USDC | 0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582 | | Monad Testnet | USDC | 0x534b2f3A21130d7a60830c2Df862319e593943A3 | | Citrea Testnet | USDC | 0xb669dC8cC6D044307Ba45366C0c836eC3c7e31AA | ### USDT Token Addresses — Mainnet | Chain | Token | Contract Address | |-------|-------|-----------------| | Ethereum | USDT | 0xdAC17F958D2ee523a2206206994597C13D831ec7 | | Polygon | USDT0 | 0xc2132D05D31c914a87C6611C10748AEb04B58e8F | | Arbitrum One | USDT0 | 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9 | | Optimism | USDT | 0x94b008aA00579c1307B0EF2c499aD98a8ce58e58 | | Avalanche C-Chain | USDT | 0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7 | | BNB Chain | Binance-Peg USDT | 0x55d398326f99059fF775485246999027B3197955 | | Hyperliquid (HyperEVM) | USDT0 | 0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb | | Monad | USDT0 | 0xe7cd86e13AC4309349F30B3435a9d337750fC82D | | MegaETH | USDT0 | 0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb | | Citrea | USDT.e | 0x9f3096Bac87e7F03DC09b0B416eB0DF837304dc4 | | Base | USDT | 0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2 | ### USDT Token Addresses — Testnet | Chain | Token | Contract Address | |-------|-------|-----------------| | Arbitrum Sepolia | USDT | 0xF954d4A5859b37De88a91bdbb8Ad309056FB04B1 | | OP Sepolia | USDT | 0x6462693c2F21AC0E517f12641D404895030F7426 | ## Cookbook Recipes The Cookbook Recipes are written to show developer patterns on how the SDK is expected to be used by a developer or an LLM agent. ## Getting a client Every recipe assumes an initialized `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. ```typescript 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 finished ``` > **Note** > > 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? Call `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. ```typescript // 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. ```json { "intent": { "provider": "nexus", "availableSources": [ { "amount": "6245.786635", "amountRaw": "6245786635", "chain": { "id": 11155111, "name": "Ethereum Sepolia", "logo": "https://assets.coingecko.com/asset_platforms/images/279/large/ethereum.png" }, "token": { "decimals": 6, "symbol": "USDC", "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238" }, "value": "6245.79" }, { "amount": "754.958732", "amountRaw": "754958732", "chain": { "id": 84532, "name": "Base Sepolia", "logo": "https://assets.coingecko.com/asset_platforms/images/131/large/base-network.png" }, "token": { "decimals": 6, "symbol": "USDC", "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x036cbd53842c5426634e7929541ec2318f3dcf7e" }, "value": "754.96" } ], "selectedSources": [ { "amount": "100.000102", "amountRaw": "100000102", "chain": { "id": 11155111, "name": "Ethereum Sepolia", "logo": "https://assets.coingecko.com/asset_platforms/images/279/large/ethereum.png" }, "token": { "decimals": 6, "symbol": "USDC", "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238" }, "value": "100.00" } ], "destination": { "amount": "100", "amountRaw": "100000000", "chain": { "id": 421614, "name": "Arbitrum Sepolia", "logo": "https://assets.coingecko.com/coins/images/16547/large/arb.jpg" }, "token": { "decimals": 6, "symbol": "USDC", "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x75faf114eafb1bdbe2f0316df893fd58ce46aa4d" }, "value": "100.00", "nativeAmount": "0", "nativeAmountRaw": "0", "nativeAmountValue": "0", "nativeAmountInToken": "0", "nativeToken": { "decimals": 18, "symbol": "ETH", "logo": "https://assets.coingecko.com/coins/images/279/large/ethereum.png", "contractAddress": "0x0000000000000000000000000000000000000000" } }, "fees": { "caGas": "0.000000", "protocol": "0.0000005", "solver": "0.000001", "total": "0.000002", "totalValue": "0.000002" }, "sourcesTotal": "100.000102", "sourcesTotalValue": "100.00" }, "token": { "contractAddress": "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d", "decimals": 6, "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "name": "USD Coin", "symbol": "USDC" } } ``` ### How do I get source chain and destination chain information from an intent? After simulating, access `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. ```typescript 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 }, }, } ); ``` ```json { "selectedSources": [ { "amount": "100.000102", "amountRaw": "100000102", "chain": { "id": 11155111, "name": "Ethereum Sepolia", "logo": "https://assets.coingecko.com/asset_platforms/images/279/large/ethereum.png" }, "token": { "decimals": 6, "symbol": "USDC", "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238" }, "value": "100.00" } ], "availableSources": [ { "amount": "6245.786635", "amountRaw": "6245786635", "chain": { "id": 11155111, "name": "Ethereum Sepolia", "logo": "https://assets.coingecko.com/asset_platforms/images/279/large/ethereum.png" }, "token": { "decimals": 6, "symbol": "USDC", "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238" }, "value": "6245.79" } ], "destination": { "amount": "100", "amountRaw": "100000000", "chain": { "id": 421614, "name": "Arbitrum Sepolia", "logo": "https://assets.coingecko.com/coins/images/16547/large/arb.jpg" }, "token": { "decimals": 6, "symbol": "USDC", "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x75faf114eafb1bdbe2f0316df893fd58ce46aa4d" }, "value": "100.00" }, "sourcesTotal": "100.000102" } ``` ### How to fetch token logos and names? Token metadata (name, symbol, decimals, logo) is available from multiple sources: the simulation result's `token` field, the nested `token` on each intent source/destination, or `client.chainList.getTokenInfoBySymbol()`. ```typescript // ─── 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? Every `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. ```typescript 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? Call `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. ```typescript 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? Use `client.chainList.getChainByID()` for a full `Chain` object (name, native currency, block explorers) or `client.getSupportedChains()` for the lighter chain + token list. ```typescript // 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://..." ``` ```json { "id": 421614, "name": "Arbitrum Sepolia", "logo": "https://assets.coingecko.com/coins/images/16547/large/arb.jpg", "swapSupported": true, "tokens": [ { "symbol": "USDC", "name": "USD Coin", "decimals": 6, "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d" } ] } ``` ### How to get the list of supported chains and tokens? Call `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. ```typescript // 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. ```json [ { "id": 421614, "name": "Arbitrum Sepolia", "logo": "https://assets.coingecko.com/coins/images/16547/large/arb.jpg", "swapSupported": true, "tokens": [ { "symbol": "USDC", "name": "USD Coin", "decimals": 6, "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d" } ] } ] ``` ### How to get the chain ID of a supported chain? Use `client.getSupportedChains()` to find a chain by name, then access its `id`. You can also validate chain support with `client.isSupportedChain()`. ```typescript // 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? Use `client.chainList.getTokenInfoBySymbol(chainId, symbol)` for a token on a specific chain, or iterate `getSupportedChains()` to collect a token's address across chains. ```typescript // 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? Use `client.chainList.getVaultContractAddress(chainId)` to get the Nexus vault contract deployed on a chain. ```typescript const vault = client.chainList.getVaultContractAddress(421614); console.log(vault); // "0x..." ``` ## Balances ### How to fetch a wallet's unified balances? Use `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`. ```typescript 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}`); }); }); ``` ```json { "symbol": "USDC", "name": "USD Coin", "balance": "1250.50", "value": "1250.50", "decimals": 6, "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "currencyId": 1, "chainBalances": [ { "balance": "500.00", "value": "500.00", "symbol": "USDC", "chain": { "id": 1, "name": "Ethereum", "logo": "https://..." }, "contractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "decimals": 6, "universe": 0 } ] } ``` ## Events ### How and when to listen to Nexus events? Pass an `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:** ```typescript 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. ``` allowance_approval Token allowance approval on a source chain request_signing User signs the intent request request_submission Intent submitted to the network vault_deposit Deposit into vault on a source chain bridge_fill Fill received on destination chain ``` **Swap Events:** Swaps emit the same typed event union. Swap plans carry `hasBridge` / `hasDestinationSwap` flags and swap-specific step types. ```typescript 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(), }, } ); ``` ``` source_swap Execute a swap on a source chain eoa_to_ephemeral_transfer Transfer funds from EOA to ephemeral wallet on a source chain bridge_deposit Deposit into vault for the cross-chain bridge bridge_intent_submission Submit the bridge intent to the network bridge_fill Wait for bridge fill on the destination chain destination_swap Execute a swap on the destination chain ``` ### How to review and approve intents and allowances? Hooks are passed per-operation via `options.hooks` (there are no global `setOnIntentHook` / `setOnAllowanceHook` setters). Use them to review a bridge/transfer intent before execution and to control approval amounts. ```typescript 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? 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** ```bash filename="Terminal" npm install vite-plugin-node-polyfills ``` **Step 2: Update vite.config.ts** ```typescript filename="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](https://github.com/availproject/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: ```json filename="package.json" { "scripts": { "dev": "next dev" } } ``` **Option B: Configure Webpack fallbacks** ```javascript filename="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: ```bash filename="Terminal" npm install crypto-browserify buffer stream-browserify ``` **Quick start alternative:** Clone the [Nexus Next.js Template](https://github.com/availproject/nexus-nextjs-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():** ```typescript // Bad const { createNexusClient } = require('@avail-project/nexus-core'); // Good import { createNexusClient } from '@avail-project/nexus-core'; ``` **2. Move Node.js-specific code to API routes or Server Components:** ```typescript filename="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:** ```typescript if (typeof window === 'undefined') { const serverModule = await import('server-only-module'); } ``` ## FastBridge [FastBridge](https://fastbridge.availproject.org/) is a unified cross-chain bridge built by Avail. Send tokens from multiple source chains and receive a single output on any supported destination, all in one transaction. It supports swaps natively, so you can send one token and receive a completely different one at the destination. Unlike traditional bridges, FastBridge never requires native gas tokens. Fees are paid directly from your stablecoin balance (USDC or USDT), and transactions settle in approximately 10 to 20 seconds through a solver-fronted liquidity model. Launch FastBridge > **Note** > > FastBridge is available today as a standalone interface, and is also built into the [FastBridge Widget](/docs/nexus/widgets) and the [Nexus SDK](/docs/nexus/nexus-sdk/get-started/installation) for embedding into any app. ## How it works FastBridge uses intent-based bridging. You declare which assets to send from which source chains, and the protocol determines the best route to deliver your chosen output token to the destination chain. Solvers compete to fill the intent using native assets, with no wrapping or synthetic tokens. * **Multi-source input** — Combine balances across multiple chains and tokens into a single transaction. * **Exact-In and Exact-Out** — Specify either the amount you want to send, or the exact amount you want to receive. FastBridge calculates the rest. * **Gas abstraction** — Gas is deducted from your stablecoin balance. No ETH, AVAX, or other native gas tokens are needed. * **Zero slippage on stablecoins** — Stablecoin-to-stablecoin transfers settle at exactly the quoted amount. Other swaps show price impact up front before confirmation. ## Supported networks, tokens, and wallets | | | | -------------- | ----------------------------------------------------------------------------------------------------------- | | **Chains** | Monad, MegaETH, Citrea, Arbitrum, Avalanche, Ethereum, Optimism, Base, Arc Mainnet, Polygon, BNB, HyperEVM. | | **Tokens** | All tokens on all supported chains. | | **Amounts** | No minimum. Maximum $10M per transfer. | | **Wallets** | Any EVM-compatible wallet (MetaMask, Rabby, Coinbase Wallet, WalletConnect). | | **Settlement** | \~10–20 seconds. | ## Security model FastBridge is fully non-custodial. Funds are never held by a centralised party; the protocol routes and settles trustlessly without taking custody at any point. If a transaction fails to settle at the destination, it reverts and your assets remain on the source chain. Security is backed by [Avail Nexus](/docs/nexus), which provides unified proof verification and intent settlement across chains via a decentralised coordination layer rather than a multisig or federated validator set. ## What makes FastBridge different | | | | --------------------------- | --------------------------------------------------------------------------------------------------------------------- | | **Multi-source with swaps** | Send multiple different tokens from multiple chains and receive a single token at the destination in one transaction. | | **Unified balance view** | Balances across all connected chains are read simultaneously and shown as a single consolidated total. | | **Gas abstraction** | Pay gas in stablecoins. No native gas tokens required on any source chain. | | **Exact-In and Exact-Out** | Choose the send or receive amount and FastBridge handles multi-source aggregation automatically. | ## Developer integrations - [**FastBridge Widget**](/docs/nexus/widgets/components/swaps) — Pre-built React component for cross-chain bridging and swaps. - [**Nexus SDK**](/docs/nexus/nexus-sdk/get-started/installation) — TypeScript SDK for building custom multi-chain experiences. ## FastBridge at a glance | | | | ---------------- | ---------------------------------------------------------------------------------------------------------- | | **Developer** | Avail Project | | **Type** | Unified bridge and swap | | **Architecture** | Intent-based, solver-fronted | | **Tokens** | All tokens on all supported chains | | **Custody** | Non-custodial | | **Settlement** | \~10–20 seconds | | **Built on** | Avail Nexus Protocol | | **Chains** | Ethereum, Optimism, Base, Monad, MegaETH, Citrea, Arbitrum, Avalanche, Arc Mainnet, Polygon, BNB, HyperEVM | | **Min / Max** | No minimum, $10M max per transfer | | **Wallets** | MetaMask, Rabby, Coinbase Wallet, WalletConnect | | **Multi-source** | Yes | | **Gas fees** | Paid in stablecoins (USDC / USDT) | | **Wrapping** | None, native assets only | ## Get Started with Avail Nexus Nexus lets your app move assets and execute transactions across chains without requiring users to bridge manually. Your users see one unified balance across every connected chain and sign a single message — Nexus turns it into an intent that solvers fill on the destination chain, usually in seconds. ## Quick Look — How the SDK Works ```ts import { createNexusClient } from '@avail-project/nexus-core'; // 1. Create and initialize the client const client = createNexusClient({ network: 'mainnet' }); await client.initialize(); await client.setEVMProvider(window.ethereum); // 2. See balances across all chains const bridgeBalances = await client.getBalancesForBridge(); const swapBalances = await client.getBalancesForSwap(); // 3. Bridge to any chain — one signature, filled by a solver await client.bridge( { toTokenSymbol: 'USDC', toAmountRaw: 100_000_000n, // 100 USDC (6 decimals) toChainId: 42161, // Arbitrum }, { hooks: { // Show the user what they'll pay and receive, then confirm onIntent: ({ intent, allow, deny }) => allow(), }, } ); // 4. Or swap into any token on any chain await client.swapWithExactOut({ toChainId: 8453, // Base toTokenAddress: '0x4200000000000000000000000000000000000006', // WETH on Base toAmountRaw: 10_000_000_000_000_000n, // 0.01 WETH }); ``` ## What Changes for Your Users | Without Nexus | With Nexus | | ------------------------------------------------------------- | --------------------------------------------------------- | | User bridges manually, switches chains, manages gas per chain | User signs one message — Nexus routes funds across chains | | Dev writes bridge logic per chain pair | Dev calls a single SDK method with token + destination | | Balances scattered across a dozen chains | One unified balance | ## Choose Your Path - [**Add Cross-Chain to My App**](/docs/nexus/nexus-sdk/get-started/installation) — Use the Nexus SDK directly — a TypeScript API for bridging, swaps, and intent management. ~15 min. - [**Drop in Widgets**](/docs/nexus/widgets/get-started/installation) — A pre-built UI element for bridging, swaps, and balances — shadcn-based. ~10 min. - [**Cookbook & Recipes**](/docs/nexus/cookbook-recipes) — Copy-paste patterns for common cross-chain flows — payments, swaps, and more. ## Prerequisites Node.js 18+, a package manager (npm/pnpm/yarn), and an EVM wallet (MetaMask, WalletConnect, etc.). ## How Nexus Works When a user initiates a cross-chain action, Nexus handles the entire flow behind a single signature: ### The SDK Requests a Quote The user says "Move 100 USDC to Arbitrum" — the SDK asks Nexus for a quote: which source chains to draw funds from and what the fees are. ### The User Approves and Signs Once Your app shows the [intent](/docs/nexus/concepts/intent) — sources, destination, and fees — via the `onIntent` hook. The user approves and signs one message covering the whole operation. No chain switching, no per-chain bridging. ### Funds Are Locked, a Solver Fills The signed intent is submitted to the Nexus network, and the user's funds are deposited into Vault contracts on each source chain. A [solver](/docs/nexus/concepts/solvers) fronts its own capital and delivers the funds on the destination chain — this is why the user receives tokens in seconds. ### Settlement Happens in the Background The solver is later reimbursed from the locked source funds through an on-chain settlement authorized by Nexus validators. If no solver fills the intent before it expires, the user is refunded. > **Note** > > Some routes are served by the native Nexus flow (intents filled by solvers) and others by Mayan, a third-party bridge provider. The SDK selects the provider automatically — your integration code stays the same. ## Supported Chains & Tokens Nexus currently supports bridging and swaps across major EVM chains including Ethereum, Arbitrum, Base, Optimism, Arc Mainnet, and more. Bridge operations support canonical tokens (ETH, USDC, USDT), while swap operations support a broader range of tokens through DEX aggregator partners. > **Note** > > For the full list, see [Supported Chains & Tokens](/docs/nexus/supported-chains-and-tokens). ## Next Steps - [**Explore Concepts**](/docs/nexus/concepts) — Chain abstraction, intents, solvers — understand the full system. - [**SDK Reference**](/docs/nexus/nexus-sdk/reference) — Complete API docs for Bridge Methods, Swap Methods, and more. - [**Cookbook**](/docs/nexus/cookbook-recipes) — Practical recipes for common cross-chain patterns. ## Migration Guides Each guide covers one upgrade step — what changed, why, and the exact renames and rewrites to apply. New guides are added here as new major versions ship. ## Current versions | Surface | Package / registry | Current | | ------------- | -------------------------------------------------------------------------------------- | ------------ | | Nexus SDK | [`@avail-project/nexus-core`](https://www.npmjs.com/package/@avail-project/nexus-core) | 2.x | | Avail Widgets | `availproject/widgets/nexus` (shadcn registry) | Nexus Widget | ## Guides - [**Migrate to Nexus SDK v2**](/docs/nexus/migration/sdk-v1-to-v2) — nexus-core 1.x to 2.x — factory client, param renames, per-operation hooks, plan-based events. - [**Migrate to Nexus Widget**](/docs/nexus/migration/widgets-legacy-to-nexus-widget) — Standalone SwapWidget/FastBridge/FastTransfer/Deposit components to the single unified Nexus Widget. ## Supported Chains & Tokens ### Mainnet Chains | Network | Chain ID | Native | Supported Tokens | Swaps | |---------|----------|--------|-----------------|-------| | Arc Mainnet | 5042 | USDC | USDC | Yes | | Ethereum | 1 | ETH | ETH, USDC, USDT | Yes | | Optimism | 10 | ETH | ETH, USDC, USDT | Yes | | Polygon | 137 | POL | POL, USDC, USDT | Yes | | Arbitrum One | 42161 | ETH | ETH, USDC, USDT | Yes | | Avalanche C-Chain | 43114 | AVAX | AVAX, USDC, USDT | Yes | | Base | 8453 | ETH | ETH, USDC, USDT | Yes | | BNB Chain | 56 | BNB | BNB, USDC, USDT | Yes | | HyperEVM | 999 | HYPE | HYPE, USDC, USDT | Yes | | Monad | 143 | MON | MON, USDC, USDT | Yes | | Citrea | 4114 | cBTC | cBTC, USDC.e, USDT.e | Yes | | MegaETH | 4326 | ETH | ETH, USDM, USDT | Yes | ### Testnet Chains > Swaps are not supported in testnet environments. | Network | Chain ID | Native | Supported Tokens | |---------|----------|--------|-----------------| | Arc Testnet | 5042002 | USDC | USDC | | Ethereum Sepolia | 11155111 | ETH | ETH, USDC | | Optimism Sepolia | 11155420 | ETH | ETH, USDC, USDT | | Polygon Amoy | 80002 | POL | POL, USDC | | Arbitrum Sepolia | 421614 | ETH | ETH, USDC, USDT | | Base Sepolia | 84532 | ETH | ETH, USDC | | Monad Testnet | 10143 | MON | MON, USDC | | Citrea Testnet | 5115 | cBTC | cBTC, USDC | ### Token Reference | Token | Name | |-------|------| | ETH | Ethereum | | USDC | USD Coin | | USDT | Tether USD | | USDC.e | Bridged USDC (Citrea) | | USDT.e | Bridged USDT (Citrea) | | USDM | USDm | | POL | Polygon | | AVAX | Avalanche | | BNB | BNB | | HYPE | Hyperliquid | | MON | Monad | | cBTC | Citrea BTC | ## Widgets Avail Widgets are shadcn-based components built on the Nexus SDK. **Nexus Widget** — the single unified component — handles swap, send, and deposit across chains. - [**Swap and Bridge**](/docs/nexus/widgets/components/swaps) — Swap and bridge assets across chains with the unified Nexus intent flow. - [**Deposit**](/docs/nexus/widgets/components/deposit) — Route funds into configured protocol opportunities with Nexus intents. - [**Send**](/docs/nexus/widgets/components/transfer) — Send assets to any recipient on any chain with built-in progress tracking. ## Try It Live No install needed — the configurator runs the real widget in your browser. Switch modes, set prefills and appearance, then copy the resulting `config` into your app. - [**Widget Configurator**](https://configurator.availproject.org/) — Configure the widget live and copy the resulting config into your app. ## Get Started Install and set up your first component with the [Manual Setup](/docs/nexus/widgets/get-started/installation) guide. ## Token Allowances The Nexus SDK requires allowances. Through allowances, a user permits the Nexus Vault contracts on the source chains to collect their funds. The protocol uses these funds to settle the transaction delivered on the destination chain. * Allowances are chosen **per operation**, via the `onAllowance` hook passed in `options.hooks`. * The hook receives `{ sources, allow, deny }` — one entry per source chain, each showing the current and minimum required allowance. * Calling `allow([...])` sets the approval per source: `'min'` (exactly what this operation needs), `'max'` (unlimited), or an exact amount. * If no hook is provided, the SDK defaults to **minimal (`'min'`) approvals** — it never grants unlimited approvals on its own. ```typescript await client.bridge(params, { hooks: { onAllowance: ({ sources, allow, deny }) => { allow(sources.map(() => 'min')); }, }, }); ``` Until the required allowance is in place, the Nexus Vault contracts on the source chains cannot collect the user funds for Nexus operations. By approving allowances for chain abstraction on the source chains, users can: * Spend anywhere with funds from two or more source chains. * Bypass bridging to enable liquidity on the destination chain. * Transact on newer chains with zero or insufficient liquidity. > **Warning** > > Setting an allowance is normally an on-chain transaction, so users **pay a gas fee** for it. Where a token supports EIP-2612 permits, the SDK uses a signed permit instead — a gasless signature — so no separate approval transaction is needed. ## How Allowances Work Here is how allowances enable chain abstracted transactions: 1. The SDK builds the operation and presents the user with an intent: the amounts to collect per source chain, the destination amount, and fee details. The user approves or rejects it via the `onIntent` hook. 2. The `onAllowance` hook fires for any source chain where the current allowance is below what the operation needs, and the approval amounts are chosen (`'min'` by default). 3. Approvals are granted to the Nexus Vault contracts on the source chains — via a regular ERC-20 approval, or a gasless EIP-2612 permit where the token supports it. 4. The user signs the intent. Solvers then supply liquidity on the destination chain. 5. The protocol settles with the solvers using the funds collected from the source chains. See [Hooks](/docs/nexus/nexus-sdk/reference/hooks-and-errors) for the full `onAllowance` and `onIntent` shapes. ## Understanding Balance Types Nexus allows you to query 2 types of balances for different use cases. These balances determine the sourcing of liquidity when doing certain operations. * Bridge Balance * Swap Balance Both queries return the same `TokenBalance[]` shape — a unified per-token total with a per-chain breakdown: ```typescript type TokenBalance = { balance: string; value: string; // USD value chainBalances: ChainBalance[]; // per-chain breakdown decimals: number; logo: string; name: string; symbol: string; }; ``` ## Bridge Balance Use [Fetch Bridge Balance](/docs/nexus/nexus-sdk/reference/bridge-methods/fetch-bridge-balances) to get all token balances available for bridge operations across supported chains in a single call. Any Bridge Method you call will source liquidity from these balances. ```typescript client.getBalancesForBridge(): Promise ``` ## Swap Balance Use [Fetch Swap Balance](/docs/nexus/nexus-sdk/reference/swap-methods/fetch-swap-balances) to get all token balances available for swap operations across supported chains in a single call. Any Swap Method you call will source liquidity from these balances. ```typescript client.getBalancesForSwap(): Promise ``` ## Why two different balances? Nexus solvers only manage liquidity for canonical bridge tokens — currently ETH, USDC, and USDT (see [Supported Chains & Tokens](/docs/nexus/supported-chains-and-tokens)). Bridge balances reflect what solvers can move across chains directly. A bridge route may be served natively by Nexus or via Mayan — the SDK selects the provider automatically per route. For crosschain swaps, Nexus orchestrates the intent but the actual swaps are executed by partner DEX aggregators (such as LiFi, Bebop, and 0x). This means swap operations can access a much wider range of tokens, since they aren't limited by solver liquidity. Because the available tokens differ between bridging and swapping, each operation needs its own balance query — your "swappable" balances are not the same as your "bridgeable" assets. > **Note** > > [Learn more about Bridge methods.](/docs/nexus/nexus-sdk/reference/bridge-methods/fetch-bridge-balances) ## What is Chain Abstraction? Managing multiple chains, tokens, and accounts fragments wallet liquidity for Web3 users. Even with enough assets overall, funds scattered across chains make transacting difficult and require complex, time-consuming conversions. Chain abstraction lets users access a unified balance across all wallet-linked chains, enabling transactions anywhere. Instead of bridging manually chain by chain, users express a [clear intent](/docs/nexus/concepts/intent): the SDK requests a quote (which source chains to draw from, and the fees), the user reviews it and signs a single message covering the whole operation. The signed intent is submitted to the Nexus network, where a [solver](/docs/nexus/concepts/solvers) delivers the funds on the destination chain — usually within seconds — and is reimbursed from the user's deposited funds at settlement. See the [intent lifecycle](/docs/nexus/concepts/intent-lifecycle) for the full flow. *Showing a subset of supported chains. See [Supported Chains & Tokens](/docs/nexus/supported-chains-and-tokens) for the full list.* As part of setup, devs configure [allowances](/docs/nexus/concepts/allowances) per chain and token. Users approve or reject these allowances. For supported chains and tokens, the protocol uses these allowances to deposit the required funds into the source-chain Vault contracts. > **Note** > > **Why Chain Abstraction?** > > Chain abstraction enables unified balance to simplify and streamline Web3 transactions. It offers: > > 1. **Fast Fulfilment**: Solvers front liquidity on the destination chain, so users receive funds in seconds instead of waiting for bridge finality. > 2. **Unified Liquidity**: Provides a consolidated view of user assets across wallet-linked chains with no manual bridging. > 3. **Simplified UX**: One signature replaces multi-step bridging, chain switching, and per-chain gas management. > 4. **Streamlined DX**: Developers can add unified balances to dApps with minimal changes and no smart contract updates for new chains. ## What is an Intent? An intent is a user's request to receive funds on a destination chain, paid for with tokens they hold on one or more source chains. In Nexus, an intent is a **Request For Funds (RFF)**: a single structured message describing the sources, the destination, and the fees. The user signs this message **once** (an EIP-191 signature) — no per-chain bridging, no chain switching. The signed RFF is submitted to the Nexus network, the user's funds are deposited into Vault contracts on each source chain, and a [solver](/docs/nexus/concepts/solvers) fills the request by delivering funds on the destination chain. ## What does an Intent look like? The following information is part of an intent: * **Selected Sources**: The chains and tokens the funds will be drawn from, and how much from each. * **Available Sources**: Everywhere the user *could* source funds from, so your UI can offer alternatives. * **Destination**: The chain, token, and amount the user will receive. * **Fees**: The CA gas fee (for cross-chain operations), protocol fee, solver fee, and the total. * **Sources Total**: The full amount drawn from source chains, combining the transaction amount and all fees. * **Provider**: Whether the route is served by the native Nexus flow (`nexus`) or by Mayan (`mayan`), a third-party bridge provider. The SDK selects this automatically. This is a simplified view of the `BridgeIntent` object the SDK passes to your `onIntent` hook — see the [full type](https://github.com/availproject/nexus-sdk/blob/main/src/domain/types/index.ts) for every field: ```ts type BridgeIntent = { provider: 'nexus' | 'mayan'; selectedSources: { amount: string; amountRaw: bigint; chain: { id: number; name: string; logo: string }; token: { decimals: number; symbol: string; logo: string; contractAddress: `0x${string}` }; value: string; // USD value }[]; availableSources: { /* same shape as selectedSources */ }[]; destination: { amount: string; amountRaw: bigint; chain: { id: number; name: string; logo: string }; token: { decimals: number; symbol: string; logo: string; contractAddress: `0x${string}` }; value: string; // USD value }; fees: { caGas: string; protocol: string; solver: string; total: string; totalValue: string; // USD value }; sourcesTotal: string; }; ``` > **Note** > > * Learn about the [complete lifecycle of an intent](/docs/nexus/concepts/intent-lifecycle). ## How Intents are Processed An intent moves through a short, well-defined lifecycle: the user signs it once, funds are locked on the source chains, a [solver](/docs/nexus/concepts/solvers) fills it on the destination chain, and the protocol settles with the solver afterwards. The intent details show which solver provided liquidity once the intent is successfully processed. > **Note** > > You can check out your Intents on Nexus with the [Nexus Explorer](https://nexus-v2.mainnet.avail.so/) ## Stages *Showing a subset of supported chains. See [Supported Chains & Tokens](/docs/nexus/supported-chains-and-tokens) for the full list.* ### Quote & Intent Construction The SDK requests a quote from Nexus: which source chains to draw funds from, and the fees. From this it builds the [intent](/docs/nexus/concepts/intent) — sources, destination, and fees. ### User Approval & Single Signature Your app presents the intent to the user via the `onIntent` hook (`allow`, `deny`, or `refresh` with different sources). On approval, the user signs **one** message covering the whole operation. ### Submission to the Nexus Network The signed intent is submitted to the Nexus network and recorded with status `created`. ### Source Deposits The user's funds are deposited into Vault contracts on each source chain — the solver submits token deposits using the user's signed intent and [allowances](/docs/nexus/concepts/allowances), while native-token deposits are sent directly from the user's wallet. When every source deposit is confirmed, the intent becomes `deposited`. ### Solver Fulfilment A solver fronts its own capital and fills the intent on the destination chain — **the user receives their funds here**, in seconds. The intent becomes `fulfilled`. ### Settlement The solver is reimbursed from the locked source-chain funds through an on-chain settlement authorized by Nexus validators. This happens asynchronously in the background — the user's transfer is already complete. > **Note** > > **If the intent isn't filled in time:** every intent carries an expiry. If no solver fills it before then, the intent becomes `expired` and the user's deposited funds are refunded on the source chains. These are the same statuses you see as an integrator: intents returned by the SDK's `listIntents()` have a status of `'created' | 'deposited' | 'fulfilled' | 'expired'`. > **Note** > > You might be interested in: > > * [Intents on Nexus](/docs/nexus/concepts/intent) > * [Solvers on Nexus](/docs/nexus/concepts/solvers) ## Bridge, Transfer & Execute In Nexus, apart from [crosschain swaps](/docs/nexus/concepts/xcs-swaps), users can do **Bridge**, **Transfer**, **Execute**, and **Bridge & Execute** which use Nexus chain abstraction (aka **Bridge Methods**). All of these operations share the same core parameters: `toTokenSymbol` (the token to receive), `toAmountRaw` (the amount as a `bigint` in the token's smallest units), `toChainId` (the destination chain), and an optional `sources` array to [limit which chains liquidity comes from](/docs/nexus/concepts/source-chain-selection). Let's understand what each Nexus operation is and what it does. ## Bridge During the `Bridge` operation, the funds are bridged from N source chains to the user's preferred destination chain. `Sender` = `Recipient` *Showing a subset of supported chains. See [Supported Chains & Tokens](/docs/nexus/supported-chains-and-tokens) for the full list.* ```typescript await client.bridge({ toTokenSymbol: 'USDC', toAmountRaw: 1000000n, // 1 USDC toChainId: 8453, }); ``` This operation uses the `BridgeParams` type and the [`bridge()`](/docs/nexus/nexus-sdk/reference/bridge-methods/bridge) function from the Bridge Methods in the SDK. ## Transfer During the `Transfer` operation, the funds are bridged from N source chains from the `Sender` to the user's preferred destination chain at a different `Recipient` address. This is a compound operation: funds are bridged from multiple source chains to a destination chain, then transferred to the `Recipient` address. `Sender` != `Recipient` *Showing a subset of supported chains. See [Supported Chains & Tokens](/docs/nexus/supported-chains-and-tokens) for the full list.* ```typescript await client.bridgeAndTransfer({ toTokenSymbol: 'USDC', toAmountRaw: 1000000n, toChainId: 8453, recipient: '0x...', }); ``` This operation uses the `TransferParams` type and the [`bridgeAndTransfer()`](/docs/nexus/nexus-sdk/reference/bridge-methods/transfer) function from the Bridge Methods in the SDK. ## Execute The `Execute` operation calls any smart contract function on a destination chain using funds the user already holds there. All the developer needs is the contract address and calldata. It is the building block that `Bridge & Execute` composes with a bridge. This operation uses the `ExecuteParams` type and the `execute()` function in the SDK. ## Bridge & Execute The `Bridge & Execute` operation is a more complex compound operation in Nexus. In this operation, the user bridges liquidity from N source chains if there is a lack of enough liquidity on the destination chain, and then executes any arbitrary smart contract function. Note that the smart contract can be any onchain contract on the destination chain. All the developer needs is the contract address and calldata. If the user already holds enough funds on the destination chain, the SDK skips the bridge entirely and only runs the contract call — the result reports this via `bridgeSkipped: true`. Either way, the operation's intent hook still lets the user review and approve before anything executes. For users, this means they don't have to do a separate bridge action to use a `Liquid App` (Nexus powered application), and just use the app as they would if they had liquidity onchain. *Showing a subset of supported chains. See [Supported Chains & Tokens](/docs/nexus/supported-chains-and-tokens) for the full list.* ```typescript await client.bridgeAndExecute({ toTokenSymbol: 'USDC', toAmountRaw: 1000000n, toChainId: 8453, execute: { to: '0x...', data: '0x...' }, }); ``` This operation uses the `BridgeAndExecuteParams` type and the [`bridgeAndExecute()`](/docs/nexus/nexus-sdk/reference/bridge-methods/bridge-and-execute) function from the Bridge Methods in the SDK. > **Note** > > * [Learn more about the difference between Bridge and Swap methods.](/docs/nexus/concepts/bridge-v-swap) > * [SDK reference](/docs/nexus/nexus-sdk/reference/bridge-methods/fetch-bridge-balances) (Bridge Methods) ## What are Solvers? In the Nexus protocol, solvers are professional market makers that supply liquidity on the destination chain to fill user [intents](/docs/nexus/concepts/intent). Once the user's funds are deposited into the Vault contracts on the source chains, a solver fronts its **own capital** on the destination chain and fills the intent — this is why users receive funds in seconds, without waiting for cross-chain finality. The solver takes on the fill and is made whole afterwards: the protocol settles with solvers who have completed intents, releasing the user's locked source-chain funds to them through an on-chain settlement. Settlements are authorized by Nexus validators — the network settles on a sovereign rollup sequenced on Avail DA, where validators independently re-execute and verify state, and sign settlements with threshold signatures. Currently, Avail runs the solvers for Nexus, but the solver network will be progressively decentralized in time. In a decentralized setup, multiple solvers compete to fill published intents, and competition keeps solver fees low. > **Note** > > To `Fill` here means the action of the solver providing the liquidity to the user, basically "filling" the intent's request for funds. ## Liquidity Routing When a user does a [Nexus operation](/docs/nexus/concepts/nexus-ops), it involves the sourcing of liquidity by Nexus from wherever they have it. Source chain selection determines which chains liquidity is routed through to reach the destination chain. Every operation accepts an optional `sources` parameter for this. *Showing a subset of supported chains. See [Supported Chains & Tokens](/docs/nexus/supported-chains-and-tokens) for the full list.* ### Bridge Methods For [Bridge Methods](/docs/nexus/nexus-sdk/reference/bridge-methods/fetch-bridge-balances), `sources` is simply an array of chain IDs to limit sourcing liquidity from. ```typescript sources?: number[]; ``` If omitted, the SDK selects sources automatically: it accumulates the user's balances across chains until the requested amount plus fees is covered. If the combined balances can't cover it, the operation fails with an `INSUFFICIENT_BALANCE` error. But if needed, the developer can pass `sources` to limit liquidity to be sourced from the specified chains **ONLY**. ### Swap Methods For [Swap Methods](/docs/nexus/concepts/xcs-swaps), sources are specific tokens on specific chains, so each entry names the `chainId` and `tokenAddress`. For `ExactIn` swaps, each source can also carry an `amountRaw` to fix how much of it to spend. ```typescript sources?: { chainId: number; tokenAddress: Hex; amountRaw?: bigint; // ExactIn only }[]; ``` As with bridges, omitting `sources` lets the SDK pick from all available swap balances. ### Re-planning from the intent hook Before an operation executes, the `onIntent` hook presents the intent. For bridge operations the intent includes both `selectedSources` (what the SDK picked) and `availableSources` (everything it could have picked), and calling `refresh(selectedSources)` from the hook rebuilds the intent with a different source selection — so users can override the route without restarting the operation. Swap intents offer the same via `refresh(sources)`. See [Hooks](/docs/nexus/nexus-sdk/reference/hooks-and-errors) for details. > **Note** > > [Learn more about the difference between Bridge and Swap methods.](/docs/nexus/concepts/bridge-v-swap) ## Crosschain Swaps In Nexus, users can do crosschain swaps (aka **Swap Methods**) which allows them to go **from any token on any source chain(s), to any token on any destination chain.** Swaps are routed through multiple DEX aggregators (such as LiFi, Bebop, and 0x), which are quoted in parallel, with the best quote winning. When the swap crosses chains, the aggregator legs are combined with the Nexus bridge leg. Canonical assets like ETH, USDC and USDT are used as the "interim" or "express" tokens to make routing simpler and cheaper for users. The pipeline is: **optional source swap → bridge leg → optional destination swap**. On supported chains, the SDK may automatically execute swap legs through a short-lived smart account (via EIP-7702) so multiple steps can be batched — no configuration needed. *Showing a subset of supported chains. See [Supported Chains & Tokens](/docs/nexus/supported-chains-and-tokens) for the full list.* ## Routing possibilities The SDK manages the full transaction lifecycle and selects the route with the best quote. Three routings are possible: 1. **Bridge → Destination Swap:** A user sends a canonical asset like USDT from chain A, and gets a custom asset like DEGEN on chain B. 2. **Source Swap → Bridge:** A user sends a custom asset like OP from chain A, and gets a canonical asset like USDT on chain B. 3. **Source Swap → Bridge → Destination Swap:** A user sends a custom asset like OP from chain A, and gets a custom asset like PEPE on chain B. *Showing a subset of supported chains. See [Supported Chains & Tokens](/docs/nexus/supported-chains-and-tokens) for the full list.* ## Exact In & Exact Out Swaps There are essentially 2 types of Swap actions a user can do with Nexus. An `ExactIn` swap and an `ExactOut` swap. In both cases, the destination is addressed by the token's contract address (`toTokenAddress`) and chain ID (`toChainId`) — swaps reach far more tokens than the canonical bridge set, so tokens are identified by address rather than symbol. Let's dive into each one in detail. ## `ExactIn` swap The `ExactIn` swap method is for performing exact input swaps, which swaps a fixed amount of one or more tokens on N number of source chains for a maximum possible (variable) amount of another token on the destination chain. User decides exactly which tokens and their amounts that they want to swap. The user decides the exact input amount for the swap. The output amount here may vary based on slippage, fees, etc. This operation uses the `SwapExactInParams` type and the [`swapWithExactIn()`](/docs/nexus/nexus-sdk/reference/swap-methods/swap-exact-in) function from the Swap Methods in the SDK. ## `ExactOut` swap The `ExactOut` swap method is for performing exact output swaps, which swap a minimum possible amount of one or more tokens on N source chains for a fixed amount of another token. User decides the exact output amount they would like to receive at the end of the action. Nexus does the hard work of figuring out which tokens and balances it can use and executes the swap for the desired output amount. This operation uses the `SwapExactOutParams` type and the [`swapWithExactOut()`](/docs/nexus/nexus-sdk/reference/swap-methods/swap-exact-out) function from the Swap Methods in the SDK. ## Swap & Execute There is also [`swapAndExecute()`](/docs/nexus/nexus-sdk/reference/swap-methods/swap-and-execute), which swaps into the destination token and then calls a smart contract on the destination chain in one flow — the swap counterpart of [Bridge & Execute](/docs/nexus/concepts/nexus-ops). > **Note** > > All swap methods accept a `slippageTolerance` option (default 0.5%) to bound how much the received amount may drift from the quote. > **Note** > > * [Learn more about the difference between Bridge and Swap methods.](/docs/nexus/concepts/bridge-v-swap) > * [SDK reference](/docs/nexus/nexus-sdk/reference/swap-methods/fetch-swap-balances) (Swap Methods) ## Migrate to Nexus SDK v2 `@avail-project/nexus-core` 2.0.0 is a full rewrite of the SDK surface. The package name is unchanged — upgrading the dependency is the whole install step — but most call sites need mechanical renames. This page walks you through the breaking changes and how to update for each. The [full migration reference](https://github.com/availproject/nexus-sdk/blob/main/MIGRATION.md) is the complete, version-pinned list — including simulation and event-level detail, every changed field, and a step-by-step checklist. ## Installation ```bash npm install @avail-project/nexus-core@^2 ``` ## Client lifecycle v1 used a class constructor with a single `initialize(provider)`. v2 uses a factory function with a two-step init, so you can fetch deployment data before a wallet connects: ```ts // v1 const sdk = new NexusSDK({ network: "mainnet" }); await sdk.initialize(provider); await sdk.deinit(); // v2 const client = createNexusClient({ network: "mainnet" }); await client.initialize(); // fetches deployment info await client.setEVMProvider(provider); // connects wallet (can be called later) client.destroy(); // synchronous ``` On account change, create a fresh client and re-run `initialize()` + `setEVMProvider()` — `triggerAccountChange()` is removed. ## Parameter renames The most common breaking change, applied consistently across `bridge`, `bridgeAndTransfer`, and `bridgeAndExecute`: | v1 field | v2 field | | -------------- | --------------- | | `token` | `toTokenSymbol` | | `amount` | `toAmountRaw` | | `sourceChains` | `sources` | ```ts // v1 await sdk.bridge({ token: "USDC", amount: 1000000n, toChainId: 8453, sourceChains: [1, 42161] }); // v2 await client.bridge({ toTokenSymbol: "USDC", toAmountRaw: 1000000n, toChainId: 8453, sources: [1, 42161] }); ``` `toAmountRaw` is in raw base units as a `bigint`, not a human amount — 1 USDC (6 decimals) is `1000000n`, not `1`. Convert with `client.convertTokenReadableAmountToBigInt(amount, tokenSymbol, chainId)`, whose signature is unchanged from v1. In `bridgeAndExecute`, the `execute` block no longer takes its own `toChainId` — it inherits the top-level one. Swap inputs were renamed too, and each variant has its own field changes: | v1 type | v2 type | field changes | | ------------------- | -------------------- | --------------------------------------------------------------------------------------------- | | `ExactInSwapInput` | `SwapExactInParams` | `from` → `sources`, `from[].amount` → `sources[].amountRaw` | | `ExactOutSwapInput` | `SwapExactOutParams` | `fromSources` → `sources`, `toAmount` → `toAmountRaw`, `toNativeAmount` → `toNativeAmountRaw` | | `MaxSwapInput` | `SwapMaxParams` | `fromSources` → `sources` | ## Hooks: global setters → per-operation options v1 registered hooks globally on the SDK instance; v2 passes them per operation. When no hooks are provided, v2 auto-approves (intents allowed, allowances set to `'min'`). ```ts // v1 sdk.setOnIntentHook((data) => data.allow()); sdk.setOnAllowanceHook((data) => data.allow(data.sources.map(() => "min"))); await sdk.bridge(params); // v2 await client.bridge(params, { hooks: { onIntent: (data) => data.allow(), onAllowance: (data) => data.allow(data.sources.map(() => "min")), }, }); ``` See [Hooks and Errors](/docs/nexus/nexus-sdk/reference/hooks-and-errors) for the v2 hook contract. ## Events: step callbacks → plan-based events v1's flat `{ name, args }` union (`STEPS_LIST`, `STEP_COMPLETE`, …) is replaced by a typed discriminated union: `status`, `plan_preview`, `plan_confirmed`, and `plan_progress`. Detect a skipped bridge via `result.bridgeSkipped` — there are no `*_SKIPPED` events. See [Bridge Events](/docs/nexus/nexus-sdk/reference/bridge-methods/bridge-events) for the full v2 event reference. ## Results and errors * Operations **throw typed `NexusError` subclasses** on failure instead of returning `{ success: false }` — use try/catch and branch on `error.category` / `error.code`. * Operation results nest transactions: `executeResponse` → `execute`, `executeTransactionHash` → `execute.txHash`, `explorerUrl` → `intentExplorerUrl`. * Bridge `result.sourceTxs` is now an **array** (was a single object): `sourceTxs.hash` → `sourceTxs[].txHash`, `sourceTxs.explorerUrl` → `sourceTxs[].txExplorerUrl`. * User-denial error codes were renamed. Match with `error instanceof UserActionError`, or branch on the specific `error.code`: | v1 code | v2 code | | ------------------------------ | -------------------------------- | | `USER_DENIED_INTENT` | `USER_INTENT_HOOK_DENIED` | | `USER_DENIED_ALLOWANCE` | `USER_ALLOWANCE_APPROVAL_DENIED` | | `USER_DENIED_INTENT_SIGNATURE` | `USER_INTENT_SIGNATURE_DENIED` | | `USER_DENIED_SIWE_SIGNATURE` | `USER_SIWE_SIGNATURE_DENIED` | ## Balance APIs The balance methods keep their names — what changed is the return type and one dropped parameter. * `getBalancesForBridge()` — same call shape; elements are now `TokenBalance` instead of `UserAssetDatum`. * `getBalancesForSwap()` — the `onlyNativesAndStables` boolean was removed; call it with no arguments. See the `UserAsset` → `TokenBalance` row below for the field renames (`icon` → `logo`, `balanceInFiat` → `value`, `breakdown` → `chainBalances`). ## Type and API renames | v1 | v2 | | --------------------------------------------- | ------------------------------------------------------------------------------------------ | | `getMyIntents(page)` | `listIntents({ page, status })` → `{ intents, total }` (page size fixed at 20) | | `ReadableIntent` | `BridgeIntent` (`sources` → `selectedSources`, `allSources` → `availableSources`) | | `UserAsset` / `UserAssetDatum` | `TokenBalance` (`icon` → `logo`, `balanceInFiat` → `value`, `breakdown` → `chainBalances`) | | `RFF` / `RequestForFunds` | `IntentRecord` (`id` → `requestHash`, status booleans collapsed into `status`) | | `SuccessfulSwapResult` | `SwapResult` | | `getSwapSupportedChains()` | removed — `getSupportedChains()` includes per-chain token info | | `CHAIN_METADATA` / `TOKEN_METADATA` constants | removed — use `client.chainList` | | `sdk.utils.isSupportedChain(...)` | `client.isSupportedChain(chainId)` | ## Full reference The repository's [MIGRATION.md](https://github.com/availproject/nexus-sdk/blob/main/MIGRATION.md) covers every breaking change — simulation result shapes, swap option additions, event step-type mappings, and a complete migration checklist. The [SDK reference](/docs/nexus/nexus-sdk/reference) documents the v2 surface in full. ## Migrate to Nexus Widget Avail Widgets consolidates all standalone cross-chain widgets into a single component: **Nexus Widget**. If your app uses `SwapWidget`, `FastBridge`, `FastTransfer`, `NexusDeposit`, `UnifiedBalance`, or `ViewHistory`, this guide maps each to its replacement. ## What changed | Legacy component | Status | Replacement | | -------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------ | | `SwapWidget` | Removed | `NexusWidget` with `config.mode = "swap"` | | `FastBridge` | Removed | `NexusWidget` with `config.mode = "swap"` | | `FastTransfer` | Removed | `NexusWidget` with `config.mode = "send"` | | `NexusDeposit` / `BridgeDeposit` | Removed | `NexusWidget` with `config.mode = "deposit"` | | `UnifiedBalance` | Removed | Inline balance views inside Nexus Widget | | `ViewHistory` | Removed | For programmatic intent history, use the SDK's [`listIntents`](/docs/nexus/nexus-sdk/reference/list-intents) | ## 1. Install Nexus Widget ```bash npx shadcn@latest add availproject/widgets/nexus ``` The install places the widget under `components/nexus/` and pins `@avail-project/nexus-core` v2. If you are also upgrading direct SDK usage, see the [SDK v1 → v2 guide](/docs/nexus/migration/sdk-v1-to-v2). ## 2. Replace components ### Swaps and bridges ```diff -import { SwapWidget } from "@/components/avail-widgets/swaps"; +import { NexusWidget } from "@/components/nexus/nexus"; - + ``` ### Sends and transfers ```diff -import { FastTransfer } from "@/components/avail-widgets/transfer"; +import { NexusWidget } from "@/components/nexus/nexus"; - + ``` ### Protocol deposits ```diff -import { NexusDeposit } from "@/components/avail-widgets/deposit"; +import { NexusWidget } from "@/components/nexus/nexus"; - + ({ + to: "0xContract...", + data: "0xCalldata...", + tokenApproval: { toTokenAddress: tokenAddress, amount, spender: "0xContract..." }, + }), + }} +/> ``` See the [Deposit page](/docs/nexus/widgets/components/deposit) for the full `executeDeposit` contract. ## 3. Provider updates `NexusProvider` is still required. Update its import to the new unified location: ```tsx import NexusProvider from "@/components/nexus/NexusProvider"; ``` SDK initialization on wallet connect is unchanged in shape — resolve the EIP-1193 provider from your wallet library and pass it to `handleInit` from `useNexus()`. See [NexusProvider Setup](/docs/nexus/widgets/get-started/nexus-provider-setup). ## 4. Custom modifications > **Local modifications are not carried over** > > The install writes new files under `components/nexus/`. Customizations you made inside the legacy `components/avail-widgets/` folders (theme colors, analytics hooks, transaction overrides) are **not** transferred automatically. Back up the legacy folders before deleting them. Recommended workflow: 1. **Back up** your existing legacy component folders outside the git workspace. 2. **Install** Nexus Widget with the command above. 3. **Prefer callbacks over forks** — `onStart`, `onComplete(explorerUrl)`, `onError(message)`, and `onConnectClick` cover the common customization points without modifying widget source. 4. **Port UI styles** into `components/nexus/nexus.tsx` and its child files using your Tailwind theme. 5. **Delete** the legacy folders once the new widget is verified. ## Cookbook Recipes The Cookbook Recipes are written to show developer patterns on how the SDK is expected to be used by a developer or an LLM agent. ## Getting a client Every recipe assumes an initialized `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. ```typescript 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 finished ``` > **Note** > > 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? Call `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. ```typescript // 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. ```json { "intent": { "provider": "nexus", "availableSources": [ { "amount": "6245.786635", "amountRaw": "6245786635", "chain": { "id": 11155111, "name": "Ethereum Sepolia", "logo": "https://assets.coingecko.com/asset_platforms/images/279/large/ethereum.png" }, "token": { "decimals": 6, "symbol": "USDC", "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238" }, "value": "6245.79" }, { "amount": "754.958732", "amountRaw": "754958732", "chain": { "id": 84532, "name": "Base Sepolia", "logo": "https://assets.coingecko.com/asset_platforms/images/131/large/base-network.png" }, "token": { "decimals": 6, "symbol": "USDC", "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x036cbd53842c5426634e7929541ec2318f3dcf7e" }, "value": "754.96" } ], "selectedSources": [ { "amount": "100.000102", "amountRaw": "100000102", "chain": { "id": 11155111, "name": "Ethereum Sepolia", "logo": "https://assets.coingecko.com/asset_platforms/images/279/large/ethereum.png" }, "token": { "decimals": 6, "symbol": "USDC", "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238" }, "value": "100.00" } ], "destination": { "amount": "100", "amountRaw": "100000000", "chain": { "id": 421614, "name": "Arbitrum Sepolia", "logo": "https://assets.coingecko.com/coins/images/16547/large/arb.jpg" }, "token": { "decimals": 6, "symbol": "USDC", "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x75faf114eafb1bdbe2f0316df893fd58ce46aa4d" }, "value": "100.00", "nativeAmount": "0", "nativeAmountRaw": "0", "nativeAmountValue": "0", "nativeAmountInToken": "0", "nativeToken": { "decimals": 18, "symbol": "ETH", "logo": "https://assets.coingecko.com/coins/images/279/large/ethereum.png", "contractAddress": "0x0000000000000000000000000000000000000000" } }, "fees": { "caGas": "0.000000", "protocol": "0.0000005", "solver": "0.000001", "total": "0.000002", "totalValue": "0.000002" }, "sourcesTotal": "100.000102", "sourcesTotalValue": "100.00" }, "token": { "contractAddress": "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d", "decimals": 6, "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "name": "USD Coin", "symbol": "USDC" } } ``` ### How do I get source chain and destination chain information from an intent? After simulating, access `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. ```typescript 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 }, }, } ); ``` ```json { "selectedSources": [ { "amount": "100.000102", "amountRaw": "100000102", "chain": { "id": 11155111, "name": "Ethereum Sepolia", "logo": "https://assets.coingecko.com/asset_platforms/images/279/large/ethereum.png" }, "token": { "decimals": 6, "symbol": "USDC", "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238" }, "value": "100.00" } ], "availableSources": [ { "amount": "6245.786635", "amountRaw": "6245786635", "chain": { "id": 11155111, "name": "Ethereum Sepolia", "logo": "https://assets.coingecko.com/asset_platforms/images/279/large/ethereum.png" }, "token": { "decimals": 6, "symbol": "USDC", "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238" }, "value": "6245.79" } ], "destination": { "amount": "100", "amountRaw": "100000000", "chain": { "id": 421614, "name": "Arbitrum Sepolia", "logo": "https://assets.coingecko.com/coins/images/16547/large/arb.jpg" }, "token": { "decimals": 6, "symbol": "USDC", "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x75faf114eafb1bdbe2f0316df893fd58ce46aa4d" }, "value": "100.00" }, "sourcesTotal": "100.000102" } ``` ### How to fetch token logos and names? Token metadata (name, symbol, decimals, logo) is available from multiple sources: the simulation result's `token` field, the nested `token` on each intent source/destination, or `client.chainList.getTokenInfoBySymbol()`. ```typescript // ─── 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? Every `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. ```typescript 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? Call `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. ```typescript 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? Use `client.chainList.getChainByID()` for a full `Chain` object (name, native currency, block explorers) or `client.getSupportedChains()` for the lighter chain + token list. ```typescript // 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://..." ``` ```json { "id": 421614, "name": "Arbitrum Sepolia", "logo": "https://assets.coingecko.com/coins/images/16547/large/arb.jpg", "swapSupported": true, "tokens": [ { "symbol": "USDC", "name": "USD Coin", "decimals": 6, "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d" } ] } ``` ### How to get the list of supported chains and tokens? Call `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. ```typescript // 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. ```json [ { "id": 421614, "name": "Arbitrum Sepolia", "logo": "https://assets.coingecko.com/coins/images/16547/large/arb.jpg", "swapSupported": true, "tokens": [ { "symbol": "USDC", "name": "USD Coin", "decimals": 6, "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "contractAddress": "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d" } ] } ] ``` ### How to get the chain ID of a supported chain? Use `client.getSupportedChains()` to find a chain by name, then access its `id`. You can also validate chain support with `client.isSupportedChain()`. ```typescript // 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? Use `client.chainList.getTokenInfoBySymbol(chainId, symbol)` for a token on a specific chain, or iterate `getSupportedChains()` to collect a token's address across chains. ```typescript // 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? Use `client.chainList.getVaultContractAddress(chainId)` to get the Nexus vault contract deployed on a chain. ```typescript const vault = client.chainList.getVaultContractAddress(421614); console.log(vault); // "0x..." ``` ## Balances ### How to fetch a wallet's unified balances? Use `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`. ```typescript 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}`); }); }); ``` ```json { "symbol": "USDC", "name": "USD Coin", "balance": "1250.50", "value": "1250.50", "decimals": 6, "logo": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png", "currencyId": 1, "chainBalances": [ { "balance": "500.00", "value": "500.00", "symbol": "USDC", "chain": { "id": 1, "name": "Ethereum", "logo": "https://..." }, "contractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "decimals": 6, "universe": 0 } ] } ``` ## Events ### How and when to listen to Nexus events? Pass an `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:** ```typescript 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. ``` allowance_approval Token allowance approval on a source chain request_signing User signs the intent request request_submission Intent submitted to the network vault_deposit Deposit into vault on a source chain bridge_fill Fill received on destination chain ``` **Swap Events:** Swaps emit the same typed event union. Swap plans carry `hasBridge` / `hasDestinationSwap` flags and swap-specific step types. ```typescript 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(), }, } ); ``` ``` source_swap Execute a swap on a source chain eoa_to_ephemeral_transfer Transfer funds from EOA to ephemeral wallet on a source chain bridge_deposit Deposit into vault for the cross-chain bridge bridge_intent_submission Submit the bridge intent to the network bridge_fill Wait for bridge fill on the destination chain destination_swap Execute a swap on the destination chain ``` ### How to review and approve intents and allowances? Hooks are passed per-operation via `options.hooks` (there are no global `setOnIntentHook` / `setOnAllowanceHook` setters). Use them to review a bridge/transfer intent before execution and to control approval amounts. ```typescript 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? 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** ```bash filename="Terminal" npm install vite-plugin-node-polyfills ``` **Step 2: Update vite.config.ts** ```typescript filename="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](https://github.com/availproject/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: ```json filename="package.json" { "scripts": { "dev": "next dev" } } ``` **Option B: Configure Webpack fallbacks** ```javascript filename="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: ```bash filename="Terminal" npm install crypto-browserify buffer stream-browserify ``` **Quick start alternative:** Clone the [Nexus Next.js Template](https://github.com/availproject/nexus-nextjs-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():** ```typescript // Bad const { createNexusClient } = require('@avail-project/nexus-core'); // Good import { createNexusClient } from '@avail-project/nexus-core'; ``` **2. Move Node.js-specific code to API routes or Server Components:** ```typescript filename="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:** ```typescript if (typeof window === 'undefined') { const serverModule = await import('server-only-module'); } ``` ## SDK Reference This section contains dedicated pages for the main functions exposed by the Avail Nexus SDK (`@avail-project/nexus-core`). - [**Bridge Methods**](/docs/nexus/nexus-sdk/reference/bridge-methods/fetch-bridge-balances) — bridge(), bridgeAndTransfer(), bridgeAndExecute(), calculateMaxForBridge(), and getBalancesForBridge(). - [**Swap Methods**](/docs/nexus/nexus-sdk/reference/swap-methods/fetch-swap-balances) — swapWithExactIn(), swapWithExactOut(), swapAndExecute(), calculateMaxForSwap(), and getBalancesForSwap(). - [**List Intents**](/docs/nexus/nexus-sdk/reference/list-intents) — listIntents() — fetch a user's historical intents with pagination and status filters. - [**Hooks and Errors**](/docs/nexus/nexus-sdk/reference/hooks-and-errors) — Per-operation hooks and the typed NexusError hierarchy. - [**Utility Reference**](/docs/nexus/nexus-sdk/reference/utility-reference) — Client creation, lifecycle, chain/token lookups, and stateless helpers. ## External links - [**GitHub Repository**](https://github.com/availproject/nexus-sdk/) — View the Nexus SDK source code and contribute. - [**npm Package**](https://www.npmjs.com/package/@avail-project/nexus-core) — @avail-project/nexus-core on npm. - [**v1 → v2 Migration Guide**](/docs/nexus/migration/sdk-v1-to-v2) — Every breaking change between nexus-core v1 and v2. ## Agent Skills Install [skills.sh](https://skills.sh) skills to give AI coding agents deep context about the Nexus SDK and Widgets. - [**Nexus SDK Skill**](https://skills.sh/availproject/nexus-sdk) — Client lifecycle, operations, hooks, events, errors, and v1 → v2 migration. - [**Widgets Skills**](/docs/nexus/widgets/mcp-and-agent-skills#agent-skills) — Provider setup plus swap, send, and deposit mode integration. See the [Skills Integration section](/docs/nexus/nexus-sdk/reference/utility-reference#skills-integration) for install commands and the full list of available skills. ## SDK helper functions and constants The Nexus SDK exposes a suite of helper functions and constants used throughout the SDK. - [**Utils**](https://github.com/availproject/nexus-sdk/blob/main/src/core/utils.ts) — Formatting and utility functions on GitHub. - [**Types & Errors**](https://github.com/availproject/nexus-sdk/blob/main/src/domain/errors.ts) — Public types and the NexusError hierarchy on GitHub. ## Widgets Reference Widgets are shadcn-based components built on top of the Nexus SDK that let you drop working cross-chain UI into your project. The standalone per-feature widgets (unified balance, fast bridge, fast transfer, deposit, swaps, view history) have been consolidated into a single **widget**. One component covers swap, send, and deposit modes. ## Install Install the widget with the shadcn CLI: ```bash title="npm" npx shadcn@latest add availproject/widgets/nexus ``` The single `NexusWidget` component brings in its own dependencies and source files, and exposes `swap`, `send`, and `deposit` modes. ## Read the full docs The Widgets documentation now lives on-site. Head there for installation, configuration, and per-mode usage. - [**Widgets**](/docs/nexus/widgets) - [**Manual Setup**](/docs/nexus/widgets/get-started/installation) ## Deposit > **Network support:** Mainnet only. Testnet is not supported at the moment. ## Configurator Generate the exact basic configuration for Deposit mode using the Widget Configurator . ## Installation #### NPM (Recommended) ```bash npm install @avail-project/widgets ``` #### shadcn/ui (Power Users) ```bash npx shadcn@latest add availproject/widgets/nexus ``` ## Usage Wrap your app with the Nexus provider first. See [NexusProvider Setup](/docs/nexus/widgets/get-started/nexus-provider-setup) for provider setup. Deposit requires a fixed destination chain, at least one destination token, a deposit contract address, and an `executeDeposit` builder describing the app call to run after Nexus delivers funds. The user picks the amount and pay-with sources; Nexus routes funds cross-chain and executes your contract call on the destination. #### NPM (Recommended) ```tsx showLineNumbers import { NexusWidget } from "@avail-project/widgets"; import { encodeFunctionData } from "viem"; const AAVE_POOL_ARBITRUM = "0x794a61358D6845594F94dc1DB02A252b5b4814aD"; const USDT_ARBITRUM = "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"; const AAVE_ABI = [ { inputs: [ { internalType: "address", name: "asset", type: "address" }, { internalType: "uint256", name: "amount", type: "uint256" }, { internalType: "address", name: "onBehalfOf", type: "address" }, { internalType: "uint16", name: "referralCode", type: "uint16" }, ], name: "supply", outputs: [], stateMutability: "nonpayable", type: "function", }, ] as const; export function DepositExample({ address }: { address?: `0x${string}` }) { return ( ({ to: AAVE_POOL_ARBITRUM, data: encodeFunctionData({ abi: AAVE_ABI, functionName: "supply", args: [tokenAddress, amount, user, 0], }), gas: 400_000n, tokenApproval: { toTokenAddress: tokenAddress, amount, spender: AAVE_POOL_ARBITRUM, }, }), appearance: { appName: "Aave", heading: "Deposit into Aave", mode: "system", }, }} /> ); } ``` #### shadcn/ui (Power Users) ```tsx showLineNumbers import { NexusWidget } from "@/components/nexus/nexus"; import { encodeFunctionData } from "viem"; const AAVE_POOL_ARBITRUM = "0x794a61358D6845594F94dc1DB02A252b5b4814aD"; const USDT_ARBITRUM = "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"; const AAVE_ABI = [ { inputs: [ { internalType: "address", name: "asset", type: "address" }, { internalType: "uint256", name: "amount", type: "uint256" }, { internalType: "address", name: "onBehalfOf", type: "address" }, { internalType: "uint16", name: "referralCode", type: "uint16" }, ], name: "supply", outputs: [], stateMutability: "nonpayable", type: "function", }, ] as const; export function DepositExample({ address }: { address?: `0x${string}` }) { return ( ({ to: AAVE_POOL_ARBITRUM, data: encodeFunctionData({ abi: AAVE_ABI, functionName: "supply", args: [tokenAddress, amount, user, 0], }), gas: 400_000n, tokenApproval: { toTokenAddress: tokenAddress, amount, spender: AAVE_POOL_ARBITRUM, }, }), appearance: { appName: "Aave", heading: "Deposit into Aave", mode: "system", }, }} /> ); } ``` ## Configuration Deposit uses `mode: "deposit"`. Unlike send and swap, the destination is required and locked: the user only chooses the amount and the sources to pay with. | Prop | Required | Description | | --------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `config.mode` | Yes | Must be `"deposit"`. | | `config.destination.chain` | Yes | The destination chain the deposit executes on. | | `config.destination.tokens` | Yes | At least one destination token: `{ address, symbol, decimals, logo? }`. Multiple entries let the user pick which asset to deposit. | | `config.depositAddress` | Yes | The contract address the deposit ultimately targets. Shown in the review UI. | | `config.executeDeposit` | Yes | Builder invoked at execution time. Returns the destination call — see below. | | `config.prefill.amount` | Optional | Prefills the deposit amount. Must be greater than `0`. | | `config.validation.minAmount`, `config.validation.maxAmount` | Optional | Bounds on the deposit amount. | | `config.appearance` | Optional | Branding used in the deposit UI: `appName`, `appLogoURL`, `heading`, `primaryColor`, `mode`. | | `connectedAddress` | Optional | Wallet address to use. If omitted, the connected wagmi account is used. | | `embed` | Optional | Defaults to `true`. Set `false` to render as a modal surface, controlled with `open`, `onOpenChange`, and `defaultOpen`. | | `onStart`, `onComplete`, `onError`, `onClose`, `onConnectClick` | Optional | Host app callbacks. `onComplete` receives the destination explorer URL. | ### The `executeDeposit` builder `executeDeposit` is called when the user confirms, with the resolved token and amount. It returns the destination transaction Nexus executes after funds arrive: ```ts executeDeposit: ( tokenSymbol: string, tokenAddress: `0x${string}`, amount: bigint, // raw units of the destination token chainId: number, user: `0x${string}`, // the depositor ) => { to: `0x${string}`; // contract to call data?: `0x${string}`; // encoded calldata value?: bigint; // native value to attach gas?: bigint; // optional gas override tokenApproval?: { // ERC-20 approval executed before the call toTokenAddress: `0x${string}`; amount: bigint; spender: `0x${string}`; }; }; ``` Include `tokenApproval` whenever the target contract pulls ERC-20 funds via `transferFrom` — the widget executes the approval before your deposit call. ```ts title="NexusWidgetDepositConfig" type NexusWidgetDepositConfig = { mode: "deposit"; destination: { chain: number; tokens: { address: `0x${string}`; symbol: string; decimals: number; logo?: string; }[]; }; depositAddress: `0x${string}`; executeDeposit: ( tokenSymbol: string, tokenAddress: `0x${string}`, amount: bigint, chainId: number, user: `0x${string}`, ) => { to: `0x${string}`; data?: `0x${string}`; value?: bigint; gas?: bigint; tokenApproval?: { toTokenAddress: `0x${string}`; amount: bigint; spender: `0x${string}`; }; }; prefill?: { amount?: string }; validation?: { minAmount?: string; maxAmount?: string }; appearance?: { heading?: string; appName?: string; appLogoURL?: string; primaryColor?: string; mode?: "system" | "light" | "dark"; }; }; ``` ## Modal usage Render the deposit flow as a modal instead of an embedded card: ```tsx import { useState } from "react"; import { NexusWidget } from "@avail-project/widgets"; export function DepositModal({ config, address, }: { config: NexusWidgetDepositConfig; address?: `0x${string}`; }) { const [open, setOpen] = useState(false); return ( <> ); } ``` ## Swap and Bridge > **Network support:** Mainnet only. Testnet is not supported at the moment. ## Configurator Generate the exact basic configuration for Swap mode using the Widget Configurator . ## Installation #### NPM (Recommended) ```bash npm install @avail-project/widgets ``` #### shadcn/ui (Power Users) ```bash npx shadcn@latest add availproject/widgets/nexus ``` ## Usage Wrap your app with the Nexus provider first. See [NexusProvider Setup](/docs/nexus/widgets/get-started/nexus-provider-setup) for provider setup. #### NPM (Recommended) ```tsx showLineNumbers import { NexusWidget } from "@avail-project/widgets"; const USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; export function SwapAndBridgeExample({ address, }: { address?: `0x${string}`; }) { return ( ); } ``` #### shadcn/ui (Power Users) ```tsx showLineNumbers import { NexusWidget } from "@/components/nexus/nexus"; const USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; export function SwapAndBridgeExample({ address, }: { address?: `0x${string}`; }) { return ( ); } ``` ## Configuration Swap and Bridge uses `mode: "swap"`. Users pick a source asset, the quote determines the receive amount, and the flow submits through the Nexus swap execution path. | Prop | Required | Description | | --------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | `config.mode` | Yes | Must be `"swap"`. | | `config.destination.chain` | Optional | Restricts the destination chain selection. | | `config.destination.tokens` | Optional | Restricts the receive-token selector. When supplied with `destination.chain`, users can only pick from this list. | | `config.prefill.token` | Optional | Initial receive token: `{ chain, address, symbol, decimals, logo? }`. Unlike `destination.tokens`, it does not restrict later token choices. | | `config.recipientAddress` | Optional | Prefills the recipient of the swap output. | | `config.appearance` | Optional | Branding overrides: `heading`, `appName`, `appLogoURL`, `primaryColor`, `mode`. | | `connectedAddress` | Optional | Wallet address to use. If omitted, the connected wagmi account is used. | | `embed` | Optional | Defaults to `true`. Set `false` to render as a modal surface. | | `open`, `onOpenChange`, `defaultOpen` | Optional | Control the modal when `embed={false}`. | | `onStart`, `onComplete`, `onError`, `onClose`, `onConnectClick` | Optional | Host app callbacks. Use `onConnectClick` to route the widget's Connect Wallet CTA to your app-level wallet flow. | If both `destination.tokens` and `prefill.token` are supplied, `destination.tokens[0]` is honored and `prefill.token` is ignored. Swap mode does not support amount prefill or min/max validation yet. ```ts title="NexusWidgetSwapConfig" interface NexusWidgetDestinationToken { address: `0x${string}`; symbol: string; decimals: number; logo?: string; } interface NexusWidgetSwapConfig { mode: "swap"; destination?: { chain?: number; tokens?: NexusWidgetDestinationToken[]; }; prefill?: { token?: NexusWidgetDestinationToken & { chain: number }; }; recipientAddress?: `0x${string}`; appearance?: { heading?: string; appName?: string; appLogoURL?: string; primaryColor?: string; mode?: "system" | "light" | "dark"; }; } ``` ## Send > **Network support:** Mainnet only. Testnet is not supported at the moment. ## Configurator Generate the exact basic configuration for Send mode using the Widget Configurator . ## Installation #### NPM (Recommended) ```bash npm install @avail-project/widgets ``` #### shadcn/ui (Power Users) ```bash npx shadcn@latest add availproject/widgets/nexus ``` ## Usage Wrap your app with the Nexus provider first. See [NexusProvider Setup](/docs/nexus/widgets/get-started/nexus-provider-setup) for provider setup. #### NPM (Recommended) ```tsx showLineNumbers import { NexusWidget } from "@avail-project/widgets"; const USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; const RECIPIENT = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; export function SendExample({ address }: { address?: `0x${string}` }) { return ( ); } ``` #### shadcn/ui (Power Users) ```tsx showLineNumbers import { NexusWidget } from "@/components/nexus/nexus"; const USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; const RECIPIENT = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; export function SendExample({ address }: { address?: `0x${string}` }) { return ( ); } ``` ## Configuration Send uses `mode: "send"`. This mode is exact-out: users enter the token amount to send, pay-with sources are selected, and the flow submits through the Nexus transfer execution path. | Prop | Required | Description | | --------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `config.mode` | Yes | Must be `"send"`. | | `config.destination.chain` | Optional | Restricts the destination chain selection. | | `config.destination.tokens` | Optional | Restricts the send-token selector. When supplied with `destination.chain`, users can only pick from this list. | | `config.recipientAddress` | Optional | Prefills the recipient. A supplied recipient is locked; otherwise the user enters one before submitting. | | `config.prefill.amount` | Optional | Prefills the exact output send amount. Must be greater than `0`. | | `config.prefill.token` | Optional | Initial send token: `{ chain, address, symbol, decimals, logo? }`. Unlike `destination.tokens`, it does not restrict later token choices. | | `config.validation.minAmount`, `config.validation.maxAmount` | Optional | Bounds on the send amount. | | `config.appearance` | Optional | Branding overrides: `heading`, `appName`, `appLogoURL`, `primaryColor`, `mode`. | | `connectedAddress` | Optional | Wallet address to use. If omitted, the connected wagmi account is used. | | `embed` | Optional | Defaults to `true`. Set `false` to render as a modal surface. | | `open`, `onOpenChange`, `defaultOpen` | Optional | Control the modal when `embed={false}`. | | `onStart`, `onComplete`, `onError`, `onClose`, `onConnectClick` | Optional | Host app callbacks. Use `onConnectClick` to route the widget's Connect Wallet CTA to your app-level wallet flow. | ```ts title="NexusWidgetSendConfig" interface NexusWidgetDestinationToken { address: `0x${string}`; symbol: string; decimals: number; logo?: string; } interface NexusWidgetSendConfig { mode: "send"; destination?: { chain?: number; tokens?: NexusWidgetDestinationToken[]; }; recipientAddress?: `0x${string}`; prefill?: { amount?: string; token?: NexusWidgetDestinationToken & { chain: number }; }; validation?: { minAmount?: string; maxAmount?: string; }; appearance?: { heading?: string; appName?: string; appLogoURL?: string; primaryColor?: string; mode?: "system" | "light" | "dark"; }; } ``` ## Unified Balance > **Warning** > > **Deprecated.** The standalone Unified Balance component has been removed in favor of > **Nexus Widget**. The cross-chain unified balance is rendered inline as part of the > swap, send, and deposit flows — see [Swap and Bridge](/docs/nexus/widgets/components/swaps). ## Migrating Remove the standalone `` component and install Nexus Widget instead: ```bash npx shadcn@latest add availproject/widgets/nexus ``` The unified balance view (per-token, per-chain breakdown across connected chains) is shown automatically inside the widget. See [NexusProvider Setup](/docs/nexus/widgets/get-started/nexus-provider-setup) to wire it up, and the [Widgets migration guide](/docs/nexus/migration/widgets-legacy-to-nexus-widget) for the full legacy-to-widget mapping. For programmatic access to balances, use the SDK directly — see [Fetch Bridge Balances](/docs/nexus/nexus-sdk/reference/bridge-methods/fetch-bridge-balances). ## MCP & Agent Skills This page covers two ways AI assistants can work with Widgets: **Agent Skills** for giving coding agents deep context about each component, and the **MCP server** for browsing and installing components via natural language. > **LLM & Coding Agent Default** > > When integrating Avail Widgets, AI coding assistants should default to installing via npm (`npm install @avail-project/widgets`). Use the shadcn MCP approach only when explicit source-level control over component primitives is requested. ## Agent Skills [skills.sh](https://skills.sh) skills give AI coding agents (Codex, Claude Code, Cursor, Antigravity, etc.) deep context about Widgets components, enabling them to scaffold and integrate pre-built React components for bridging, swaps, deposits, and more. ### Install Skills ```bash # Install all Widgets skills npx skills add availproject/nexus-elements # Install a single skill npx skills add https://github.com/availproject/nexus-elements --skill nexus-widget-swaps ``` ### Available Skills | Skill | Description | | ------------------------------ | ---------------------------------------------------- | | `avail-widgets-overview` | Overview of Avail Widgets | | `avail-widgets-common` | Shared patterns and conventions | | `avail-widgets-nexus-provider` | NexusProvider setup and configuration | | `nexus-widget-swaps` | Nexus Widget swap mode integration | | `nexus-widget-send` | Nexus Widget send mode integration | | `nexus-widget-deposit` | Nexus Widget deposit mode integration | | `avail-widgets-swaps` | Swap and bridge component patterns | | `avail-widgets-deposit` | Deposit component patterns | | `avail-widgets-transfer` | Transfer / send component patterns | | `avail-widgets-bridge-deposit` | Bridge & Deposit execution patterns | | `nexus-migration-guide` | Migrating legacy standalone elements to Nexus Widget | Browse all available skills at [skills.sh/availproject/nexus-elements](https://skills.sh/availproject/nexus-elements). > **Looking for SDK skills?** > > The Nexus SDK has its own set of agent skills for bridge flows, swap flows, hooks, and more. See the [SDK Skills Integration](/docs/nexus/nexus-sdk/reference/utility-reference#skills-integration) section. *** ## MCP Server The shadcn MCP Server lets Cursor (and other MCP-compatible IDEs) talk directly to your Widgets registry so you can browse, search, and install UI packages with natural language. Detailed platform-agnostic docs live at [ui.shadcn.com/docs/mcp](https://ui.shadcn.com/docs/mcp), but this page focuses on the Widgets setup. ## Build Options ### Option 1: NPM Package (Recommended for LLMs & Agents) The fastest and most robust way to integrate: ```bash npm install @avail-project/widgets viem wagmi @tanstack/react-query ``` ```tsx import { NexusWidget, NexusProvider } from "@avail-project/widgets"; ``` ### Option 2: shadcn/ui Registry (Power Users) Install from the public registry for source-level code access: ```bash npx shadcn@latest add availproject/widgets/nexus ``` All legacy standalone elements (`FastBridge`, `FastTransfer`, `SwapWidget`, `Deposit`, `BridgeDeposit`, `UnifiedBalance`, `ViewHistory`) have been deprecated and removed in favor of Nexus Widget. *** ## Nexus Registry Setup Add the Widgets registry to your local `components.json`. This gives any MCP client access to the published components. ```json title="components.json" { "registries": { "@avail-widgets": "https://widgets.availproject.org/r/{name}.json" } } ``` *** ## Quick Start Select your MCP client and run the matching init command. Once the server is connected, the prompts below will install Widgets components straight from the registry. #### Cursor **Run the following command** in your project: ```bash npx shadcn@latest mcp init --client cursor ``` Open **Cursor Settings** and enable the shadcn MCP server. Then try prompts like: * Add Nexus Widget to my project using npm install * Install @avail-project/widgets and set up NexusProvider * Add deposit mode to my app using Avail Widgets #### Claude Code **Run the following command** in your project: ```bash npx shadcn@latest mcp init --client claude ``` Restart Claude Code and try the prompts above. Use `/mcp` to inspect the server state if needed. #### VS Code **Run the following command** in your project: ```bash npx shadcn@latest mcp init --client vscode ``` Open `.vscode/mcp.json`, click **Start** next to the shadcn server, then try the prompts with GitHub Copilot Chat. #### Codex > **Note** > > **Note:** The `shadcn` CLI cannot automatically update `~/.codex/config.toml`. Add the config manually. **Run the following command** in your project: ```bash npx shadcn@latest mcp init --client codex ``` **Then, append this snippet** to `~/.codex/config.toml`: ```toml [mcp_servers.shadcn] command = "npx" args = ["shadcn@latest", "mcp"] ``` Restart Codex and try the same prompts. *** ## What is MCP? [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is an open protocol that lets AI assistants securely connect to external tools. When paired with shadcn MCP: * **Browse components** across every configured registry (public or private) * **Search by intent** (“find a swaps widget”) instead of memorizing package names * **Install with natural language** without leaving your IDE * **Work across registries** via `@namespace/component` syntax and per-registry auth *** ## How It Works 1. **Registry connection** – MCP reads your `components.json` and surfaces every registry entry. 2. **Natural language** – You describe what you want in Cursor/Claude/etc. 3. **AI processing** – The assistant maps that intent to registry items. 4. **Component delivery** – The shadcn CLI installs files directly into your repo. *** ## Supported Registries * **shadcn/ui registry** – ships with every MCP install for the canonical set of components. * **Avail Widgets registry** – `@avail-widgets` namespace for the Avail-specific UI kit. * **Third-party registries** – any endpoint that follows the shadcn spec. * **Private registries** – internal libraries with optional auth headers or tokens. *** ## Configuration Each client needs a small JSON/TOML stub pointing to the shadcn MCP binary. ### Cursor ```json title=".cursor/mcp.json" { "mcpServers": { "shadcn": { "command": "npx", "args": ["shadcn@latest", "mcp"] } } } ``` After saving, enable the server in Cursor Settings → MCP. ### Claude Code ```json title=".mcp.json" { "mcpServers": { "shadcn": { "command": "npx", "args": ["shadcn@latest", "mcp"] } } } ``` Restart Claude Code, run `/mcp`, and confirm the server shows as `Connected`. ### VS Code ```json title=".vscode/mcp.json" { "servers": { "shadcn": { "command": "npx", "args": ["shadcn@latest", "mcp"] } } } ``` Open the file in VS Code, click **Start** next to the server, and Copilot Chat will gain the new tools. ### Codex ```toml title="~/.codex/config.toml" [mcp_servers.shadcn] command = "npx" args = ["shadcn@latest", "mcp"] ``` Restart Codex so it reloads the config. *** ## Example Prompts ### NPM Install * Install @avail-project/widgets and set up NexusProvider in my Next.js app * Add Nexus Widget in deposit mode for my DeFi protocol using npm ### Browse & Search (shadcn registry) * Show me all available components in the @avail-widgets registry * Add @avail-widgets/nexus to my project using shadcn *** ## Troubleshooting ### MCP Not Responding 1. Check your MCP config file (`.cursor/mcp.json`, `.mcp.json`, etc.). 2. Restart the IDE after any config edits. 3. Ensure `npx shadcn@latest mcp` runs without errors. 4. Confirm your network can reach the registry URLs. ### Registry Access Issues 1. Double-check registry URLs inside `components.json`. 2. Verify required env vars are loaded (restart `pnpm dev`/IDE if needed). 3. Confirm the registry endpoint is online. 4. Ensure namespace syntax matches (`@namespace/component`). ### Installation Failures 1. Validate that `components.json` exists at the repo root. 2. Check that CLI has write access to `components/` and `lib/` directories. 3. Make sure underlying dependencies (React, Tailwind, etc.) are installed. 4. Inspect CLI output for missing peer deps or TypeScript errors. *** ### Learn More * [Registry documentation](https://ui.shadcn.com/docs/registry) – overview of shadcn-compatible registries. * [Namespaces](https://ui.shadcn.com/docs/registry/namespace) – configuring multiple sources. * [Authentication](https://ui.shadcn.com/docs/registry/authentication) – securing private registries. * [MCP specification](https://modelcontextprotocol.io/) – full protocol details. ## Hooks and Errors The Nexus SDK ships with per-operation hooks for gating and observing an operation, and a typed `NexusError` hierarchy for handling failures. ## Hooks In v2 there are **no global hook setters** (`setOnIntentHook`, `setOnAllowanceHook`, `setOnSwapIntentHook` were removed). Hooks are passed **per operation** through the second `options` argument: ```typescript title="TypeScript" await client.bridge(params, { hooks: { onIntent: ({ intent, allow }) => allow(), onAllowance: ({ allow }) => allow(['min']), }, }); ``` > **Note** > > If you don't provide any hooks, the SDK **auto-approves**: the intent is allowed automatically and allowances default to `'min'` for every source. Where each hook lives per method: | Method | `onEvent` | `onIntent` | `onAllowance` | | ------------------------------------------ | --------- | -------------------- | ------------------- | | `bridge()` / `bridgeAndTransfer()` | top-level | `hooks.onIntent` | `hooks.onAllowance` | | `swapWithExactIn()` / `swapWithExactOut()` | top-level | `hooks.onIntent` | — | | `bridgeAndExecute()` | top-level | top-level `onIntent` | automatic (`min`) | | `swapAndExecute()` | top-level | top-level `onIntent` | — | ### onEvent Supported by every operation, `onEvent` streams progress as the operation runs. The v2 event is a typed union discriminated by `type`: ```typescript title="TypeScript" type Event = | { type: 'status'; status: string } | { type: 'plan_preview'; plan: /* plan */ } | { type: 'plan_confirmed'; plan: /* plan */ } | { type: 'plan_progress'; stepType: string; state: string; step: /* step */ }; type OnEventParam = { onEvent?: (event: TEvent) => void; }; ``` ```typescript title="TypeScript" await client.bridge(params, { onEvent: (event) => { if (event.type === 'plan_progress') { console.log('Step:', event.stepType, event.state); } }, }); ``` > **Note** > > Each method emits its own typed event union. See the [Bridge Events](/docs/nexus/nexus-sdk/reference/bridge-methods/bridge-events) and [Swap Events](/docs/nexus/nexus-sdk/reference/swap-methods/swap-events) pages for the full step-by-step definitions. ### onIntent Gate the intent before execution. Render the plan and fees, then call `allow()` to continue or `deny()` to abort. `refresh(selectedSources?)` re-plans (for example after the user picks different source chains) and resolves to the updated intent. #### Signature ```typescript title="TypeScript" type OnIntentHookData = { allow: () => void; deny: () => void; intent: BridgeIntent; refresh: (selectedSources?: number[]) => Promise; }; ``` `BridgeIntent` is the human-readable breakdown of `availableSources`, `selectedSources`, `destination`, and `fees`: ```typescript title="TypeScript" type BridgeIntent = { provider: 'nexus' | 'mayan'; availableSources: Array<{ amount: string; amountRaw: bigint; chain: { id: number; name: string; logo: string }; token: { decimals: number; symbol: string; logo: string; contractAddress: Hex }; value: string; }>; selectedSources: Array<{ amount: string; amountRaw: bigint; chain: { id: number; name: string; logo: string }; token: { decimals: number; symbol: string; logo: string; contractAddress: Hex }; value: string; }>; destination: { amount: string; amountRaw: bigint; chain: { id: number; name: string; logo: string }; token: { decimals: number; symbol: string; logo: string; contractAddress: Hex }; value: string; nativeAmount: string; nativeAmountRaw: bigint; nativeAmountValue: string; nativeAmountInToken: string; nativeToken: { decimals: number; symbol: string; logo: string; contractAddress: Hex }; }; fees: { caGas: string; protocol: string; solver: string; total: string; totalValue: string; }; sourcesTotal: string; sourcesTotalValue: string; }; ``` #### Example ```typescript title="TypeScript" await client.bridge(params, { hooks: { onIntent: async ({ intent, allow, deny, refresh }) => { // Render the plan and fees from `intent` if (userChangedSources) { const updated = await refresh([10, 42161]); // re-plan against Optimism + Arbitrum // re-render with `updated` } if (userApproves) allow(); else deny(); }, }, }); ``` ### onAllowance Fires when one or more sources need an ERC-20 approval before the deposit. Call `allow(values)` with one entry per source, or `deny()` to abort. #### Signature ```typescript title="TypeScript" type OnAllowanceHookData = { allow: (values: Array<'max' | 'min' | bigint | string>) => void; deny: () => void; sources: AllowanceHookSource[]; }; type AllowanceHookSource = { allowance: { current: string; // Current allowance (human-readable) currentRaw: bigint; // Current allowance (raw) minimum: string; // Minimum required (human-readable) minimumRaw: bigint; // Minimum required (raw) }; chain: { id: number; logo: string; name: string; }; holderAddress?: Hex; token: { contractAddress: `0x${string}`; decimals: number; logo: string; name: string; symbol: string; }; }; ``` * `allow(values)` — `values.length` must equal `sources.length`. Each entry is one of: * `'min'` — the minimum required allowance for that source. * `'max'` — an unlimited (`maxUint256`) allowance for that source. * a `bigint` or numeric `string` — a custom raw amount for that source. * `deny()` — stops the flow. #### Example ```typescript title="TypeScript" await client.bridge(params, { hooks: { onAllowance: ({ sources, allow, deny }) => { // `sources` has one entry per approval required if (userApproves) allow(sources.map(() => 'min')); // or 'max' / custom per source else deny(); }, }, }); ``` > **Note** > > If you don't provide `onAllowance`, the SDK approves `'min'` for every source automatically. Approval progress surfaces through `onEvent`. ### Swap onIntent `swapWithExactIn()` and `swapWithExactOut()` take `hooks.onIntent`, but the intent shape is `SwapIntent` (route, destination, and gas), and `refresh` accepts `Source[]`: ```typescript title="TypeScript" type OnIntentHookData = { allow: () => void; deny: () => void; intent: SwapIntent; refresh: (sources?: Source[]) => Promise; }; ``` ```typescript title="TypeScript" await client.swapWithExactIn(input, { hooks: { onIntent: ({ intent, allow }) => { // Render the route and destination amount from `intent` allow(); }, }, }); ``` See the full `SwapIntent` definition in the [SDK source](https://github.com/availproject/nexus-sdk/blob/main/src/swap/types.ts). ### Composite operations `bridgeAndExecute()` and `swapAndExecute()` take a **top-level** `onIntent` (not nested under `hooks`). Its `intent` describes both the requirement and any bridge/swap needed to satisfy it. ```typescript title="TypeScript" type BridgeAndExecuteOnIntentHookData = { allow: () => void; deny: () => void; intent: BridgeAndExecuteIntent; refresh: (selectedSources?: number[]) => Promise; }; type SwapAndExecuteOnIntentHookData = { allow: () => void; deny: () => void; intent: SwapAndExecuteIntent; refresh: (sources?: Source[]) => Promise; }; ``` ```typescript title="TypeScript" await client.bridgeAndExecute(params, { onIntent: ({ intent, allow, deny }) => { // `intent.bridgeRequired` tells you whether a bridge leg is needed if (userApproves) allow(); else deny(); }, }); ``` ## Error Handling Every SDK operation throws a typed `NexusError` subclass on failure rather than returning an error result. ### The NexusError hierarchy `NexusError` is the abstract base. Each concrete subclass pins a `category`: ```typescript title="TypeScript" abstract class NexusError extends Error { readonly category: ErrorCategory; // set by each subclass readonly code: ErrorCode; // namespaced string, e.g. 'validation/insufficient_balance' readonly context: ErrorContext; // { operation?, service?, stepId?, stepType?, chainId? } readonly details?: Record; toJSON(): object; // serializable { name, message, category, code, context, details } } type ErrorCategory = | 'validation' | 'user_action' | 'simulation' | 'execution' | 'backend' | 'external_service' | 'internal'; ``` | Subclass | `category` | Meaning | | ---------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------- | | `ValidationError` | `validation` | Caller input or precondition failure (bad params, unsupported token/chain, insufficient balance, SDK not initialized). | | `UserActionError` | `user_action` | User rejected a prompt (intent hook, signature, allowance approval, tx send). | | `SimulationError` | `simulation` | A pre-execution `eth_call` / simulation failed. | | `ExecutionError` | `execution` | Runtime failure at the wallet/RPC boundary (gas estimate, revert, receipt timeout). | | `BackendError` | `backend` | Avail middleware HTTP/WS failure. | | `ExternalServiceError` | `external_service` | A third-party dependency failed (LiFi, Bebop, Fibrous, 0x, Mystic, Relay, Coinbase). | | `InternalError` | `internal` | A true SDK invariant was violated. | > **Note** > > `code` holds a namespaced string of the shape `category/specific_noun`. The exported `ERROR_CODES` map gives you friendly aliases — e.g. `ERROR_CODES.INSUFFICIENT_BALANCE === 'validation/insufficient_balance'`. Branch on `error.category` (or `instanceof`) for coarse handling and on `error.code === ERROR_CODES.*` for specific cases. There is no `NexusStepError` in v2. Step-scoped failures are thrown as the applicable subclass, carrying `context.stepId`, `context.stepType`, and `context.chainId`. ### Recommended pattern ```typescript title="TypeScript" import { NexusError, UserActionError, ValidationError, ERROR_CODES, } from '@avail-project/nexus-core'; try { await client.bridge({ toTokenSymbol: 'USDC', toAmountRaw: 1_000_000n, toChainId: 137 }); } catch (error) { if (error instanceof NexusError) { console.error(`[${error.category}] ${error.code}: ${error.message}`); // Coarse branching on category if (error instanceof UserActionError) { // User cancelled a prompt — usually nothing to show return; } if (error instanceof ValidationError) { switch (error.code) { case ERROR_CODES.INSUFFICIENT_BALANCE: showInsufficientBalanceUI(); break; case ERROR_CODES.TOKEN_NOT_SUPPORTED: showUnsupportedTokenUI(); break; default: showGenericError(error.message); } } // Log the structured form for telemetry logErrorToService(error.toJSON()); } else { // Non-Nexus errors (network, library, etc.) console.error('Unexpected error:', error); } } ``` ### Error codes reference The full list lives in [`src/domain/errors.ts`](https://github.com/availproject/nexus-sdk/blob/main/src/domain/errors.ts). Representative codes by category: | `ERROR_CODES` key | `code` value | Category | | ----------------------------------- | ------------------------------------------- | ----------------- | | `INVALID_INPUT` | `validation/invalid_input` | validation | | `INSUFFICIENT_BALANCE` | `validation/insufficient_balance` | validation | | `TOKEN_NOT_SUPPORTED` | `validation/token_not_supported` | validation | | `CHAIN_NOT_FOUND` | `validation/chain_not_found` | validation | | `SDK_NOT_INITIALIZED` | `validation/sdk_not_initialized` | validation | | `WALLET_NOT_CONNECTED` | `validation/wallet_not_connected` | validation | | `VAULT_CONTRACT_NOT_FOUND` | `validation/vault_contract_not_found` | validation | | `INVALID_VALUES_ALLOWANCE_HOOK` | `validation/invalid_allowance_hook` | validation | | `USER_INTENT_HOOK_DENIED` | `user_action/intent_hook_denied` | user\_action | | `USER_INTENT_SIGNATURE_DENIED` | `user_action/intent_signature_denied` | user\_action | | `USER_ALLOWANCE_APPROVAL_DENIED` | `user_action/allowance_approval_denied` | user\_action | | `USER_TX_SEND_DENIED` | `user_action/tx_send_denied` | user\_action | | `SIMULATION_ETH_CALL_FAILED` | `simulation/eth_call_failed` | simulation | | `EXEC_GAS_ESTIMATE_FAILED` | `execution/gas_estimate_failed` | execution | | `EXEC_TX_ONCHAIN_REVERTED` | `execution/tx_onchain_reverted` | execution | | `EXEC_TX_RECEIPT_WAIT_TIMEOUT` | `execution/tx_receipt_wait_timeout` | execution | | `EXEC_SLIPPAGE_EXCEEDED` | `execution/slippage_exceeded` | execution | | `BACKEND_RFF_SUBMIT_FAILED` | `backend/rff_submit_failed` | backend | | `BACKEND_FULFILMENT_WAIT_TIMEOUT` | `backend/fulfilment_wait_timeout` | backend | | `BACKEND_FEE_GRANT_REQUESTED` | `backend/fee_grant_requested` | backend | | `EXTERNAL_SOURCE_SWAP_QUOTE_FAILED` | `external_service/source_swap_quote_failed` | external\_service | | `EXTERNAL_RATES_DRIFT_EXCEEDED` | `external_service/rates_drift_exceeded` | external\_service | | `INTERNAL_ERROR` | `internal/error` | internal | > **Note** > > See the [full error definitions](https://github.com/availproject/nexus-sdk/blob/main/src/domain/errors.ts) on GitHub for the exhaustive `ERROR_CODES` map, the named `Errors.*` factories, and every `code`. ## List Intents > **Note** > > **SET UP THE SDK BEFORE YOU START:** > > Install and initialize the client first — see [Installation](/docs/nexus/nexus-sdk/get-started/installation) and [SDK Setup](/docs/nexus/nexus-sdk/get-started/nexus-sdk-setup). Use the `listIntents()` function to fetch a user's historical intents, with pagination and an optional status filter. > **Note** > > `listIntents()` replaces the v1 `getMyIntents(page)` method. It returns a structured `{ intents, total }` result instead of a bare array, and accepts an optional `status` filter. ## Method signature ```typescript title="TypeScript" listIntents( params?: ListIntentsParams, ): Promise ``` ## Parameters ```typescript title="TypeScript" export type ListIntentsParams = { page?: number; status?: IntentStatus; }; export type IntentStatus = 'created' | 'deposited' | 'fulfilled' | 'expired'; ``` * `ListIntentsParams` (optional): Filters for the intent query. * `page` (number, optional): The page number for pagination. Page size is fixed at 20 records — paginate by incrementing `page`. * `status` (`IntentStatus`, optional): Restrict results to a single lifecycle status — `'created'`, `'deposited'`, `'fulfilled'`, or `'expired'`. > **Note** > > `IntentStatus` is exported both as a value map (`IntentStatus.Fulfilled`) and as a type. Use the value map to avoid hardcoding the string literals. ## Example ```typescript title="TypeScript" import { IntentStatus, type ListIntentsResult } from '@avail-project/nexus-core'; // First page of all intents const { intents, total }: ListIntentsResult = await client.listIntents(); console.log(`Showing ${intents.length} of ${total} intents`); // Second page, filtered to fulfilled intents const fulfilled = await client.listIntents({ page: 2, status: IntentStatus.Fulfilled, }); for (const intent of fulfilled.intents) { console.log(intent.requestHash, intent.status, intent.explorerUrl); } ``` ## Return Value The return value is a `ListIntentsResult` object. ```typescript title="TypeScript" export type ListIntentsResult = { intents: IntentRecord[]; total: number; }; export type IntentRecord = { requestHash: Hex; explorerUrl: string; status: IntentStatus; solver: Hex | null; createdAt?: number; updatedAt?: number; expiry: number; recipientAddress: Hex; destinationChain: { id: number; name: string; logo: string; universe: Universe; }; destinations: Array<{ token: { contractAddress: Hex; symbol: string; name: string; logo: string; decimals: number; }; amount: string; amountRaw: bigint; }>; sources: Array<{ chain: { id: number; name: string; logo: string; universe: Universe; }; amountRaw: bigint; amount: string; feeRaw: bigint; fee: string; token: { contractAddress: Hex; symbol: string; name: string; logo: string; decimals: number; }; }>; }; ``` > **Note** > > See the [full type definitions](https://github.com/availproject/nexus-sdk/blob/main/src/domain/types/index.ts) on GitHub. ## Utility Reference The Nexus SDK is created with `createNexusClient` and exposes a headless client plus a set of stateless helpers. This page covers client configuration, lifecycle, chain/token lookups, and the utility exports. ## Creating a client ```typescript title="TypeScript" import { createNexusClient } from '@avail-project/nexus-core'; const client = createNexusClient({ network: 'mainnet', }); await client.initialize(); ``` ### Configuration ```typescript title="TypeScript" createNexusClient(config?: { network?: 'mainnet' | 'testnet' | NetworkConfig; debug?: boolean; analytics?: AnalyticsConfig; devTiming?: DevTimingConfig; domain?: string; forceMayan?: boolean; }): NexusClient ``` | Option | Type | Description | | ------------ | ----------------------------------------- | ----------------------------------------------------------------------------------------------------- | | `network` | `'mainnet' \| 'testnet' \| NetworkConfig` | Target network. Defaults to `'mainnet'`. Pass a `NetworkConfig` object for a fully custom deployment. | | `debug` | `boolean` | Enable verbose debug logging. | | `analytics` | `AnalyticsConfig` | Analytics configuration (see [Analytics](#analytics)). | | `devTiming` | `DevTimingConfig` | Developer timing instrumentation for spans. | | `domain` | `string` | Override the auto-detected domain used in the ephemeral-key sign message. | | `forceMayan` | `boolean` | Force routing through Mayan where available. | ### Custom network config ```typescript title="TypeScript" type NetworkConfig = { MIDDLEWARE_HTTP_URL: string; MIDDLEWARE_WS_URL: string; INTENT_EXPLORER_URL: string; NETWORK_HINT: 'mainnet' | 'testnet'; }; const client = createNexusClient({ network: { MIDDLEWARE_HTTP_URL: 'https://your-deployment.example.com/middleware/', MIDDLEWARE_WS_URL: 'wss://your-deployment.example.com/middleware/', INTENT_EXPLORER_URL: 'https://your-deployment.example.com/', NETWORK_HINT: 'mainnet', }, }); ``` ## Lifecycle #### `initialize()` Initialize the client. Call once before invoking any operation; it can run before or after `setEVMProvider()`. ```typescript title="TypeScript" await client.initialize(); ``` #### `setEVMProvider(provider)` Attach or replace the EIP-1193 EVM provider used to sign and send transactions. ```typescript title="TypeScript" await client.setEVMProvider(window.ethereum); ``` | Parameter | Type | Description | | ---------- | ------------------ | ------------------------------------------------------------- | | `provider` | `EthereumProvider` | EIP-1193 compatible provider (MetaMask, WalletConnect, etc.). | #### `hasEvmProvider` Boolean getter — `true` once an EVM provider has been attached. ```typescript title="TypeScript" if (client.hasEvmProvider) { // Safe to run operations that need a wallet } ``` #### `destroy()` Tear down the client and release resources (analytics, sockets). Call when unmounting your app. ```typescript title="TypeScript" client.destroy(); ``` ## Chain and token helpers #### `getSupportedChains()` Return the chains and tokens supported by the connected deployment. ```typescript title="TypeScript" const supported = client.getSupportedChains(); // SupportedChainsAndTokensResult: Array<{ id, name, logo, swapSupported, tokens }> ``` #### `isSupportedChain(chainId)` ```typescript title="TypeScript" const ok = client.isSupportedChain(137); // boolean ``` #### `convertTokenReadableAmountToBigInt(amount, tokenSymbol, chainId)` Convert a human-readable amount into raw integer units using the token's on-chain decimals. ```typescript title="TypeScript" const amountRaw = client.convertTokenReadableAmountToBigInt('83.5', 'USDC', 137); // 83_500_000n ``` ### `client.chainList` `client.chainList` exposes synchronous lookups over the resolved deployment. ```typescript title="TypeScript" client.chainList.getChainByID(chainID: number): Chain; client.chainList.getTokenInfoBySymbol(chainID: number, symbol: string): TokenInfo; client.chainList.getNativeToken(chainID: number): TokenInfo; client.chainList.getVaultContractAddress(chainID: number): `0x${string}`; client.chainList.getTokenByAddress(chainID: number, address: `0x${string}`): TokenInfo; client.chainList.getTokenByCurrencyId(chainID: number, currencyId: number): TokenInfo; client.chainList.getChainAndTokenFromSymbol( chainID: number, tokenSymbol: string, ): { chain: Chain; token: TokenInfo; isNativeToken: boolean }; client.chainList.getChainAndTokenByAddress( chainID: number, address: `0x${string}`, ): { chain: Chain; token: TokenInfo; isNativeToken: boolean }; ``` ```typescript title="TypeScript" const polygon = client.chainList.getChainByID(137); const usdc = client.chainList.getTokenInfoBySymbol(137, 'USDC'); const vault = client.chainList.getVaultContractAddress(137); ``` ## Max-amount helpers #### `calculateMaxForBridge(params)` Compute the maximum amount that can be bridged for a token to a destination chain. `maxAmountRaw` can be passed straight into `bridge()` as `toAmountRaw`. ```typescript title="TypeScript" const max = await client.calculateMaxForBridge({ toChainId: 137, toTokenSymbol: 'USDC', }); type BridgeMaxResult = { toChainId: number; toTokenSymbol: string; provider: BridgeProvider; maxAmount: string; // human decimal string maxAmountRaw: bigint; // raw integer units symbol: string; decimals: number; sources: Array<{ chainId: number; tokenAddress: Hex; symbol: string; decimals: number; amount: string; // human decimal drawn from this source }>; }; ``` #### `calculateMaxForSwap(params)` Compute the maximum input available for a swap into a destination token. See the [Swap Methods](/docs/nexus/nexus-sdk/reference/swap-methods/fetch-swap-balances) pages for the swap surface. ```typescript title="TypeScript" const max = await client.calculateMaxForSwap({ toChainId: 8453, toTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', }); type SwapMaxResult = { toChainId: number; toTokenAddress: Hex; maxAmount: string; // human decimal string maxAmountRaw: bigint; // raw integer units symbol: string; decimals: number; sources: Array<{ chainId: number; tokenAddress: Hex; symbol: string; decimals: number; amount: string; }>; }; ``` ## Stateless utilities Formatting and address helpers are importable directly from the `/utils` subpath, and are also available on `client.utils`. ```typescript title="TypeScript" import { formatTokenBalance, formatTokenBalanceParts, formatUnits, parseUnits, truncateAddress, isValidAddress, getCoinbaseRates, getSupportedChains, } from '@avail-project/nexus-core/utils'; ``` ### Formatting ```typescript title="TypeScript" // Format a raw balance for display const formatted = formatTokenBalance(1234567890n, { decimals: 6, symbol: 'USDC' }); // Get the parts separately const parts = formatTokenBalanceParts(1234567890n, { decimals: 6 }); // Convert between raw and human units const raw = parseUnits('1.5', 18); // 1500000000000000000n const human = formatUnits(raw, 18); // "1.5" ``` ### Addresses ```typescript title="TypeScript" const valid = isValidAddress('0x742d35Cc6634C0532925a3b8D4C9db96c4b4Db45'); // boolean const short = truncateAddress('0x742d35Cc6634C0532925a3b8D4C9db96c4b4Db45'); // "0x742d...Db45" ``` ### Token logo fallback `getFallbackTokenLogoDataUri` generates a deterministic gradient SVG logo as a data URI — useful as an `onError` fallback for token images. Note it is exported from the main entry point, not `/utils`: ```typescript title="TypeScript" import { getFallbackTokenLogoDataUri } from '@avail-project/nexus-core'; const logo = getFallbackTokenLogoDataUri('USDC'); // "data:image/svg+xml;charset=utf-8,..." const large = getFallbackTokenLogoDataUri('USDC', 256); // optional size, defaults to 128 ``` ### Prices and supported chains ```typescript title="TypeScript" // Current token prices from Coinbase const rates = await getCoinbaseRates(); // Supported chains and tokens for a network (async, standalone helper) const supported = await getSupportedChains('mainnet'); ``` ### Via `client.utils` The same helpers are attached to the client as `NexusUtils`: ```typescript title="TypeScript" const rates = await client.utils.getCoinbaseRates(); const supported = await client.utils.getSupportedChains('mainnet'); const formatted = client.utils.formatTokenBalance(1234567890n, { decimals: 6, symbol: 'USDC' }); ``` > **Note** > > `client.getSupportedChains()` (the client method) is synchronous and returns the currently connected deployment's chains. The standalone `getSupportedChains(network)` helper is asynchronous and fetches the deployment for the given network hint. ## Analytics The Nexus SDK includes built-in analytics powered by PostHog. Analytics are **enabled by default** and send anonymous telemetry to Avail's PostHog instance; they can be customized or disabled through the `analytics` config. ### Disabling analytics ```typescript title="TypeScript" const client = createNexusClient({ network: 'mainnet', analytics: { enabled: false }, }); ``` ### Privacy controls ```typescript title="TypeScript" const client = createNexusClient({ network: 'mainnet', analytics: { enabled: true, privacy: { anonymizeWallets: true, // Hash wallet addresses anonymizeAmounts: true, // Exclude transaction amounts }, }, }); ``` ### Custom analytics (BYO PostHog) ```typescript title="TypeScript" const client = createNexusClient({ network: 'mainnet', analytics: { enabled: true, posthogApiKey: 'your-posthog-key', posthogApiHost: 'https://your-posthog-instance.com', appMetadata: { appName: 'My DApp', appVersion: '1.0.0', appUrl: 'https://mydapp.com', }, }, }); ``` ### Accessing analytics programmatically ```typescript title="TypeScript" if (client.analytics.isEnabled()) { client.analytics.track('custom_event', { foo: 'bar' }); } ``` ## Common pitfalls * Operation amounts are raw integer units (`bigint`), not human-readable decimal strings. Use `convertTokenReadableAmountToBigInt()` or `parseUnits()` to convert. * Bridge methods (`bridge`, `bridgeAndTransfer`, `bridgeAndExecute`) identify tokens by **symbol**; swap methods identify tokens by **contract address**. * Call `initialize()` once before running operations. It fetches deployment data and does not require a provider, so it can run before or after `setEVMProvider()`. * Call `destroy()` on teardown to flush analytics and close sockets. ## Skills Integration [skills.sh](https://skills.sh) skills give AI coding agents (Codex, Claude Code, Cursor, etc.) deep context about the Nexus SDK. ```bash # Install the SDK skill npx skills add availproject/nexus-sdk ``` * `nexus-core` — Full v2 integration guide: client lifecycle, operations, hooks, events, error handling, utils, and v1 → v2 migration Browse it at [skills.sh/availproject/nexus-sdk](https://skills.sh/availproject/nexus-sdk). For Widgets skills, see [MCP & Agent Skills](/docs/nexus/widgets/mcp-and-agent-skills). ## Bridge Tokens In this guide you will add a bridge function to your Nexus SDK project that moves tokens from any supported source chain to a destination chain. By the end you will have a working bridge button that transfers USDC to Arbitrum Sepolia with real-time step tracking. > **Prerequisite** > > This guide builds on the [Initializing Nexus with RainbowKit](/docs/nexus/nexus-sdk/get-started/initializing-nexus-with-rainbowkit) tutorial. Complete that first. ## What you will build A bridge button that: * Calls `client.bridge()` to move USDC from any chain to Arbitrum Sepolia * Uses the typed `onEvent` callback for step-by-step progress updates * Displays the Nexus explorer URL on completion ## Step 1 — Add the bridge helper function Add a `bridge()` function to your existing SDK helper file. This example bridges 0.0001 USDC to Arbitrum Sepolia (chain ID `421614`). ```tsx title="src/lib/nexus.ts" import type { BridgeResult } from "@avail-project/nexus-core"; // ... existing code (client instance, initialize, destroy, getBalancesForBridge) ... export async function bridge() { const bridgeResult: BridgeResult = await client.bridge( { toTokenSymbol: "USDC", toAmountRaw: BigInt(100), toChainId: 421614, // Arbitrum Sepolia }, { onEvent: (event) => { switch (event.type) { case "status": // Lifecycle phase: intent_building → intent_ready → ... → completed console.log("Status:", event.status); break; case "plan_preview": // Full list of steps that will execute console.log("Expected steps:", event.plan.steps); break; case "plan_progress": // Fires as each step transitions state console.log(`Step ${event.step.type}: ${event.state}`); if ( (event.state === "submitted" || event.state === "confirmed") && "txHash" in event ) { console.log("View tx:", event.explorerUrl); } break; } }, } ); return bridgeResult; } ``` ### How `client.bridge()` works | Param | Type | Required | Description | | --------------- | ---------- | -------- | -------------------------------------------------------------------------------------- | | `toTokenSymbol` | `string` | Yes | Token symbol to bridge (e.g. "USDC", "ETH"). | | `toAmountRaw` | `bigint` | Yes | Amount in the token's smallest unit (e.g. BigInt(100) = 0.0001 USDC with 6 decimals). | | `toChainId` | `number` | Yes | Destination chain ID (e.g. 421614 for Arbitrum Sepolia). | | `sources` | `number[]` | No | Restrict the source chains funds may be pulled from. Defaults to all supported chains. | The second argument is an options object with an `onEvent` callback. Progress arrives as a typed discriminated union on `event.type`: * **`status`** — lifecycle phase updates on `event.status` (e.g. `intent_building` → `completed`) * **`plan_preview`** — fired once with the full list of steps on `event.plan.steps` * **`plan_progress`** — fired as each step advances, carrying `event.stepType`, `event.state`, and `event.step`. On-chain steps expose `txHash` / `explorerUrl` in the `submitted` and `confirmed` states ## Step 2 — Create the bridge button component Create a button component that calls the bridge function and displays the result. ```tsx title="src/components/bridge-button.tsx" "use client"; import { bridge, client } from "../lib/nexus"; export default function BridgeButton({ className, onResult, }: { className?: string; onResult?: (r: any) => void; }) { const onClick = async () => { if (!client.hasEvmProvider) return alert("Initialize first"); const res = await bridge(); onResult?.(res); console.log(res); }; return ( ); } ``` ## Step 3 — Add the button to the page Import `BridgeButton` into your main page alongside the existing components: ```tsx title="src/app/page.tsx" "use client"; import { useState } from "react"; import { useAccount } from "wagmi"; import ConnectWalletButton from "@/components/connect-button"; import InitButton from "@/components/init-button"; import FetchBalancesButton from "@/components/fetch-balances-button"; import DeinitButton from "@/components/de-init-button"; import BridgeButton from "@/components/bridge-button"; import { client } from "@/lib/nexus"; export default function Page() { const { isConnected } = useAccount(); const [initialized, setInitialized] = useState(client.hasEvmProvider); const [balances, setBalances] = useState(null); const [bridgeResult, setBridgeResult] = useState(null); const btn = "px-4 py-2 rounded-md bg-blue-600 text-white hover:bg-blue-700 " + "disabled:opacity-50 disabled:cursor-not-allowed"; return (
setInitialized(true)} /> setBalances(r)} /> setBridgeResult(r)} /> { setInitialized(false); setBalances(null); }} />
Wallet Status:{" "} {isConnected ? "Connected" : "Not connected"}
SDK Status:{" "} {initialized ? "Initialized" : "Not initialized"}
{balances && (
            {JSON.stringify(balances, null, 2)}
          
)} {bridgeResult && (
            {JSON.stringify(bridgeResult, null, 2)}
          
)}
); } ``` ## Step 4 — Run and test ```bash pnpm dev ``` 1. Connect your wallet and initialize Nexus 2. Click **Bridge 0.0001 USDC to Arbitrum Sepolia** 3. Approve the transaction in your wallet 4. Watch the console for step progress events 5. On success, you will see a `BridgeResult` with the intent explorer URL, the source transactions, and the resolved intent: ```json { "intentExplorerUrl": "https://nexus-v2.testnet.avail.so/intent/0x37b1...84fe", "sourceTxs": [ { "txHash": "0x…", "txExplorerUrl": "https://…", "chain": { "id": 84532, "name": "Base Sepolia", "logo": "…" } } ], "intent": { "…": "…" } } ``` > **Customize the bridge** > > Replace the hardcoded values in `bridge()` with user inputs to build a dynamic bridge UI. You can change the `toTokenSymbol`, `toAmountRaw`, and `toChainId` parameters to bridge any supported token to any supported chain. ## Next step Take bridging further by combining it with an on-chain execution — bridge tokens and immediately deposit them into a DeFi protocol in a single transaction. - [**Deposit into Aave**](/docs/nexus/nexus-sdk/get-started/deposit-into-aave) ## Deposit into Aave In this guide you will implement a **Bridge & Execute** flow that bridges USDC from any chain and immediately supplies it into Aave V3 on Arbitrum Sepolia in a single user-signed intent. You will also build a step-by-step progress UI that tracks each phase of the transaction. > **Prerequisite** > > This guide builds on the [Bridge Tokens](/docs/nexus/nexus-sdk/get-started/bridge-tokens) tutorial. Complete that first. ## What you will build A deposit button that: 1. Bridges USDC from one or more source chains to Arbitrum Sepolia 2. Automatically calls Aave V3's `supply()` function on the destination chain 3. Displays a real-time progress checklist with explorer links ## How `bridgeAndExecute` works The `client.bridgeAndExecute()` method extends regular bridging by accepting an `execute` object that defines a contract call to run after the bridge completes. The user signs a single intent that covers both operations. | Param | Type | Required | Description | | ----------------------- | ------------------------------------------------------------ | ------------- | ---------------------------------------------------------------------------------------------------------------------------- | | `toTokenSymbol` | `string` | Yes | Token to bridge (e.g. "USDC"). | | `toAmountRaw` | `bigint` | Yes | Amount in smallest unit (e.g. BigInt(1\_000\_000) = 1 USDC). | | `toChainId` | `number` | Yes | Destination chain ID. The `execute` object inherits it, so it omits `toChainId`. | | `execute.to` | `string` | Yes | Contract address to call on the destination chain. | | `execute.data` | `string` | Yes | ABI-encoded function call data. | | `execute.tokenApproval` | `{ toTokenSymbol: string, amount: bigint, spender: string }` | For this flow | Optional in general; required whenever the target contract pulls ERC-20 funds via `transferFrom`, as Aave's `supply()` does. | ## Step 1 - Add the bridgeAndExecute helper Update your SDK helper file with a `bridgeAndExecute` function. This encodes an Aave V3 `supply()` call and passes it to the SDK. ```tsx title="src/lib/nexus.ts" import type { BridgeAndExecuteEvent } from "@avail-project/nexus-core"; import { encodeFunctionData } from "viem"; // ... existing code (client instance, initialize, destroy, getBalancesForBridge, bridge) ... const AAVE_V3_POOL = "0xBfC91D59fdAA134A4ED45f7B584cAf96D7792Eff"; // Aave V3 Pool on Arbitrum Sepolia export async function bridgeAndExecute( userAddress: `0x${string}`, onEvent: (event: BridgeAndExecuteEvent) => void ) { if (!client.hasEvmProvider) { throw new Error("Connect a wallet and initialize the client first."); } if (!userAddress) { throw new Error("User address is required"); } // Look up the USDC contract address on Arbitrum Sepolia from the client's chain list. const usdc = client.chainList.getTokenInfoBySymbol(421614, "USDC"); // Encode the Aave V3 supply() function call const data = encodeFunctionData({ abi: [ { inputs: [ { internalType: "address", name: "asset", type: "address" }, { internalType: "uint256", name: "amount", type: "uint256" }, { internalType: "address", name: "onBehalfOf", type: "address", }, { internalType: "uint16", name: "referralCode", type: "uint16", }, ], name: "supply", outputs: [], stateMutability: "nonpayable", type: "function", }, ], functionName: "supply", args: [ usdc.contractAddress, // asset: USDC token address BigInt(1_000_000), // amount: 1 USDC (6 decimals) userAddress, // onBehalfOf: user's address 0, // referralCode: 0 ], }); const result = await client.bridgeAndExecute( { toTokenSymbol: "USDC", toAmountRaw: BigInt(1_000_000), // 1 USDC (6 decimals) toChainId: 421614, // Arbitrum Sepolia execute: { // toChainId is inherited from the top-level toChainId to: AAVE_V3_POOL, data: data, tokenApproval: { toTokenSymbol: "USDC", amount: BigInt(1_000_000), spender: AAVE_V3_POOL, }, }, }, { onEvent } ); return result; } ``` ### Breaking down the `execute` object ```ts execute: { // toChainId is inherited from the top-level toChainId to: AAVE_V3_POOL, // Aave V3 Pool contract data: data, // Encoded supply() call tokenApproval: { toTokenSymbol: "USDC", amount: BigInt(1_000_000), spender: AAVE_V3_POOL, }, } ``` * **`to`** - the contract address to call after bridging completes * **`data`** - ABI-encoded function call, built with viem's `encodeFunctionData` * **`tokenApproval`** - Aave needs approval to pull USDC from the user, so the SDK handles the approval step automatically ## Step 2 - Create the deposit button component This component triggers the transaction and renders a real-time progress checklist. ```tsx title="src/components/deposit-button.tsx" "use client"; import { useState } from "react"; import { useAccount } from "wagmi"; import type { BridgeAndExecutePlanStep, } from "@avail-project/nexus-core"; import { bridgeAndExecute, client } from "../lib/nexus"; type CompletedStep = { state: string; explorerUrl?: string }; export default function DepositButton({ className, onResult, }: { className?: string; onResult?: (r: any) => void; }) { const { address } = useAccount(); const [steps, setSteps] = useState([]); const [completed, setCompleted] = useState>({}); const [error, setError] = useState(""); const onClick = async () => { if (!client.hasEvmProvider) return alert("Initialize first"); if (!address) return alert("Please connect your wallet first"); setSteps([]); setCompleted({}); setError(""); try { const res = await bridgeAndExecute(address, (event) => { // plan_preview / plan_confirmed carry the full list of steps if (event.type === "plan_preview" || event.type === "plan_confirmed") { setSteps(event.plan.steps); } // plan_progress reports per-step state transitions if (event.type === "plan_progress") { // Both 'confirmed' and 'completed' are terminal success states if (event.state === "confirmed" || event.state === "completed") { setCompleted((prev) => ({ ...prev, [event.step.id]: { state: event.state, explorerUrl: "explorerUrl" in event ? event.explorerUrl : undefined, }, })); } } }); onResult?.(res); } catch (e: any) { setError(e.message || "Transaction failed"); } }; return (
{steps.length > 0 && (

Transaction Progress:

{steps.map((step) => { const done = completed[step.id]; const isDone = !!done; return (
{isDone ? "✅" : "○"} ({step.type}) {done?.explorerUrl && ( View on Explorer )}
); })}
)} {error && (
Error: {error}
)}
); } ``` ### How the progress UI works The component derives the checklist from the typed `onEvent` stream: * **`steps`** - populated from `event.plan.steps` when a `plan_preview` (or `plan_confirmed`) event fires, listing every step the transaction will go through (allowance approval, request signing, vault deposit, bridge fill, execute approval, execute transaction, and so on) * **`completed`** - a map keyed by `step.id`, filled in from `plan_progress` events. On-chain steps settle on `confirmed` and off-chain steps on `completed`, so the UI treats both as terminal-success states The UI renders the expected steps as a checklist and marks each one done when its `step.id` appears in `completed`. If the progress event carried an `explorerUrl`, a "View on Explorer" link is shown. ## Step 3 - Add the deposit button to the page Import `DepositButton` into your main page: ```tsx title="src/app/page.tsx" "use client"; import { useState } from "react"; import { useAccount } from "wagmi"; import ConnectWalletButton from "@/components/connect-button"; import InitButton from "@/components/init-button"; import FetchBalancesButton from "@/components/fetch-balances-button"; import DeinitButton from "@/components/de-init-button"; import BridgeButton from "@/components/bridge-button"; import DepositButton from "@/components/deposit-button"; import { client } from "@/lib/nexus"; export default function Page() { const { isConnected } = useAccount(); const [initialized, setInitialized] = useState(client.hasEvmProvider); const [balances, setBalances] = useState(null); const [bridgeResult, setBridgeResult] = useState(null); const [depositResult, setDepositResult] = useState(null); const btn = "px-4 py-2 rounded-md bg-blue-600 text-white hover:bg-blue-700 " + "disabled:opacity-50 disabled:cursor-not-allowed"; return (
setInitialized(true)} /> setBalances(r)} /> setBridgeResult(r)} /> setDepositResult(r)} /> { setInitialized(false); setBalances(null); }} />
Wallet Status:{" "} {isConnected ? "Connected" : "Not connected"}
SDK Status:{" "} {initialized ? "Initialized" : "Not initialized"}
{balances && (
            {JSON.stringify(balances, null, 2)}
          
)} {bridgeResult && (
            {JSON.stringify(bridgeResult, null, 2)}
          
)} {depositResult && (
            {JSON.stringify(depositResult, null, 2)}
          
)}
); } ``` ## Step 4 - Run and test ```bash pnpm dev ``` 1. Connect your wallet and initialize Nexus 2. Click **Bridge USDC & Deposit into Aave** 3. Approve the transaction in your wallet 4. Watch the progress checklist update in real-time as each step completes 5. Click the explorer links to verify on-chain > **What you just built** > > With a single SDK call you bridged tokens across chains **and** executed a DeFi deposit on the destination chain. The `bridgeAndExecute` pattern works with any contract call. You could swap on a DEX, stake in a protocol, mint an NFT, anything you can encode as calldata. ## What's next You have completed the full Nexus SDK "Get Started" series. From here you can explore: - [**SDK Reference**](/docs/nexus/nexus-sdk/reference) — Full API docs for bridge, swap, and balance methods. - [**Bridge Methods**](/docs/nexus/nexus-sdk/reference/bridge-methods/fetch-bridge-balances) — Deep dive into bridge(), bridgeAndTransfer(), and bridgeAndExecute(). - [**Swap Methods**](/docs/nexus/nexus-sdk/reference/swap-methods/fetch-swap-balances) — Cross-chain swaps with swapWithExactIn() and swapWithExactOut(). - [**Cookbook Recipes**](/docs/nexus/nexus-sdk/cookbook-recipes) — Copy-paste patterns for common developer workflows. ## Initializing Nexus with RainbowKit In this guide you will add RainbowKit to an existing Nexus SDK project and retrieve the EIP-1193 provider from wagmi's connector instead of `window.ethereum`. By the end you will have a full wallet connection modal, multi-wallet support, and all existing SDK functionality intact. > **Prerequisite** > > This guide builds on the [Nexus SDK Setup](/docs/nexus/nexus-sdk/get-started/nexus-sdk-setup). Complete that first. ## What changes | Before (basic setup) | After (RainbowKit) | | --------------------------------------- | ------------------------------------------ | | `window.ethereum` for wallet connection | RainbowKit modal with multi-wallet support | | Manual `eth_requestAccounts` call | wagmi handles connection state | | Single injected provider only | WalletConnect, Coinbase, and more | Everything else — the SDK instance, helper functions, balance fetching, and de-initialization — stays **exactly the same**. ## Step 1 — Install RainbowKit dependencies Add the required packages to your project: #### npm ```bash npm install @rainbow-me/rainbowkit wagmi viem @tanstack/react-query ``` #### pnpm ```bash pnpm add @rainbow-me/rainbowkit wagmi viem @tanstack/react-query ``` #### yarn ```bash yarn add @rainbow-me/rainbowkit wagmi viem @tanstack/react-query ``` ## Step 2 — Create the wagmi configuration Create a config file that defines the supported chains and your WalletConnect project ID. ```tsx title="src/lib/wagmi.ts" import { getDefaultConfig } from "@rainbow-me/rainbowkit"; import { mainnet, arbitrum, polygon, optimism, base, avalanche, } from "wagmi/chains"; export const config = getDefaultConfig({ appName: "Nexus SDK with RainbowKit", projectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID!, chains: [mainnet, arbitrum, polygon, optimism, base, avalanche], ssr: true, }); ``` > **WalletConnect Project ID required** > > Get a free Project ID at [cloud.walletconnect.com](https://cloud.walletconnect.com). Add it to your `.env.local` as `NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID`. ## Step 3 — Create the providers component Wrap your app with wagmi, React Query, and RainbowKit providers. ```tsx title="src/components/providers.tsx" "use client"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { WagmiProvider } from "wagmi"; import { RainbowKitProvider } from "@rainbow-me/rainbowkit"; import { config } from "@/lib/wagmi"; import "@rainbow-me/rainbowkit/styles.css"; const queryClient = new QueryClient(); export function Providers({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` ## Step 4 — Update the layout Wrap your root layout with the new providers component: ```tsx title="src/app/layout.tsx" import { Providers } from "@/components/providers"; import "./globals.css"; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( {children}
); } ``` ## Step 5 — Update the connect button Replace the manual `window.ethereum` approach with RainbowKit's `ConnectButton`: ```tsx title="src/components/connect-button.tsx" "use client"; import { ConnectButton } from "@rainbow-me/rainbowkit"; export default function ConnectWalletButton({ className, }: { className?: string; }) { return ( {({ account, chain, openAccountModal, openChainModal, openConnectModal, authenticationStatus, mounted, }) => { const ready = mounted && authenticationStatus !== "loading"; const connected = ready && account && chain && (!authenticationStatus || authenticationStatus === "authenticated"); return (
{(() => { if (!connected) { return ( ); } if (chain.unsupported) { return ( ); } return (
); })()}
); }}
); } ``` ## Step 6 — Update the init button The key change: instead of reading `window.ethereum`, use wagmi's `useAccount` hook to get the provider from the connected wallet's connector. ```tsx title="src/components/init-button.tsx" "use client"; import { useAccount } from "wagmi"; import { initializeWithProvider, client } from "../lib/nexus"; export default function InitButton({ className, onReady, }: { className?: string; onReady?: () => void; }) { const { connector } = useAccount(); const onClick = async () => { try { const provider = await connector?.getProvider(); if (!provider) throw new Error("No provider found"); await initializeWithProvider(provider); onReady?.(); alert("Nexus initialized"); } catch (e: any) { alert(e?.message ?? "Init failed"); } }; return ( ); } ``` ### How the provider retrieval works ```ts const { connector } = useAccount(); const provider = await connector?.getProvider(); await initializeWithProvider(provider); ``` * **`useAccount()`** — wagmi hook that returns the current connection state and active connector * **`connector.getProvider()`** — returns the EIP-1193 provider for whichever wallet the user connected (MetaMask, WalletConnect, Coinbase, etc.) * **`initializeWithProvider(provider)`** — your existing helper function runs `client.initialize()` and then passes the provider to `client.setEVMProvider()` This means **any wallet RainbowKit supports** will work with the Nexus SDK — no code changes needed per wallet. ## Step 7 — Update the main page Add wallet status display using wagmi's `useAccount` hook: ```tsx title="src/app/page.tsx" "use client"; import { useState } from "react"; import { useAccount } from "wagmi"; import ConnectWalletButton from "@/components/connect-button"; import InitButton from "@/components/init-button"; import FetchBalancesButton from "@/components/fetch-balances-button"; import DeinitButton from "@/components/de-init-button"; import { client } from "@/lib/nexus"; export default function Page() { const { isConnected } = useAccount(); const [initialized, setInitialized] = useState(client.hasEvmProvider); const [balances, setBalances] = useState(null); const btn = "px-4 py-2 rounded-md bg-blue-600 text-white hover:bg-blue-700 " + "disabled:opacity-50 disabled:cursor-not-allowed"; return (
setInitialized(true)} /> setBalances(r)} /> { setInitialized(false); setBalances(null); }} />
Wallet Status:{" "} {isConnected ? "Connected" : "Not connected"}
SDK Status:{" "} {initialized ? "Initialized" : "Not initialized"}
{balances && (
            {JSON.stringify(balances, null, 2)}
          
)}
); } ``` ## Summary of changes | File | What changed | | ----------------------------------- | -------------------------------------------------------------------- | | `src/lib/wagmi.ts` | **New** — wagmi + RainbowKit config | | `src/components/providers.tsx` | **New** — provider wrapper component | | `src/app/layout.tsx` | Wrapped with `Providers` | | `src/components/connect-button.tsx` | Replaced `window.ethereum` with RainbowKit `ConnectButton.Custom` | | `src/components/init-button.tsx` | Replaced `window.ethereum` with `connector.getProvider()` from wagmi | | `src/app/page.tsx` | Added `useAccount` for wallet status display | | `src/lib/nexus.ts` | **No changes** — SDK setup is the same | ## Next step Your project now has a production-quality wallet connection experience. Next, add cross-chain bridging functionality. - [**Bridge Tokens**](/docs/nexus/nexus-sdk/get-started/bridge-tokens) ## Installation In this guide you will install `@avail-project/nexus-core` and its peer dependencies into an existing project. By the end, your project will be ready for SDK initialization in the next step. ## Prerequisites Before you begin, make sure you have: * **A JavaScript or TypeScript project** — the SDK works with any framework or runtime (React, Node.js, vanilla JS, etc.) * **Node.js 18+** or **Bun** with a package manager (npm 9+, pnpm, yarn, or bun) * **An [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compatible wallet provider** to sign transactions (e.g. MetaMask via `window.ethereum`, or a library like [wagmi](https://wagmi.sh)) > **Wallet provider** > > The SDK needs an EIP-1193 provider to send transactions. In a browser you can use `window.ethereum` directly. Wallet libraries like wagmi or RainbowKit are optional — they just make it easier to manage wallet connections in React apps. ## Step 1 — Install the core package Run one of the following commands to add the Nexus SDK to your project: #### npm ```bash npm install @avail-project/nexus-core ``` #### pnpm ```bash pnpm add @avail-project/nexus-core ``` #### yarn ```bash yarn add @avail-project/nexus-core ``` #### bun ```bash bun add @avail-project/nexus-core ``` ### What gets installed The `@avail-project/nexus-core` package provides: * **`createNexusClient`** — the factory function for creating a Nexus client and performing cross-chain operations * **Bridge methods** — `bridge()`, `bridgeAndTransfer()`, `bridgeAndExecute()` for moving tokens across chains * **Swap methods** — `swapWithExactIn()`, `swapWithExactOut()` for cross-chain token swaps * **Balance methods** — `getBalancesForBridge()` and `getBalancesForSwap()` for multi-chain token balances * **Typed progress events** — an `onEvent` callback with a discriminated union (`status`, `plan_preview`, `plan_confirmed`, `plan_progress`) for tracking transaction progress * **Type exports** — `NexusClient`, `BridgeResult`, `EthereumProvider`, and more ## Step 2 — Verify the installation Confirm the package was installed by importing and creating a client: ```ts import { createNexusClient } from "@avail-project/nexus-core"; const client = createNexusClient(); console.log("Nexus SDK ready"); ``` If this compiles without errors, the installation is complete. ## Next step The package is installed. Next, you will create an SDK instance, connect it to a wallet, and fetch your first token balances. - [**Nexus SDK Setup**](/docs/nexus/nexus-sdk/get-started/nexus-sdk-setup) ## Nexus SDK Setup In this guide you will create a Nexus SDK instance, connect it to a wallet provider, and fetch unified balances across all supported chains. By the end you will have a minimal Next.js app with four buttons that demonstrate the full SDK lifecycle. ## Prerequisites * [Node.js](https://nodejs.org) version 18 or higher, with npm 9 or higher * A package manager — this guide uses `pnpm`, but `npm` and `yarn` work too * An [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compatible wallet (e.g. MetaMask) installed in your browser * `@avail-project/nexus-core` installed in your project (see [Installation](/docs/nexus/nexus-sdk/get-started/installation)) ## What you will build A minimal Next.js page with four buttons: 1. **Connect Wallet** — connects an EIP-1193 wallet 2. **Initialize Nexus** — passes the wallet provider to the SDK 3. **Fetch Balances** — retrieves token balances across all supported chains 4. **De-initialize** — tears down the SDK instance > **Balance methods** > > The SDK provides two balance methods: `getBalancesForBridge()` returns tokens available for bridging, while `getBalancesForSwap()` returns tokens available for cross-chain swaps. Learn more in the [Bridge vs Swap concept page](/docs/nexus/concepts/bridge-v-swap). ## Step 1 — Create a Next.js project If you don't have an existing project, scaffold one: ```bash pnpm create next-app@latest my-nexus-app --ts --eslint --app --src-dir cd my-nexus-app ``` Install the Nexus SDK: ```bash pnpm add @avail-project/nexus-core ``` ## Step 2 — Create the SDK instance Create a single shared SDK instance that the rest of the app will import. This file lives outside of React so it has no component lifecycle concerns. ```tsx title="src/lib/nexus.ts" import { createNexusClient, type NexusClient } from "@avail-project/nexus-core"; // Create a single client instance. // Pass { network: 'testnet' } to use testnet chains. // Defaults to 'mainnet' if omitted. export const client: NexusClient = createNexusClient({ network: "testnet" }); export async function initializeWithProvider(provider: any) { if (!provider) throw new Error("No EIP-1193 provider (e.g. MetaMask) found"); if (client.hasEvmProvider) return; await client.initialize(); // loads deployment info from the middleware await client.setEVMProvider(provider); // attaches the connected wallet } export function destroy() { client.destroy(); } export async function getBalancesForBridge() { return await client.getBalancesForBridge(); } export async function getBalancesForSwap() { return await client.getBalancesForSwap(); } ``` ### What each part does | Export | Type | Description | | ---------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------- | | `client` | `NexusClient` | The shared Nexus client, created once with `createNexusClient()`. | | `client.hasEvmProvider` | `boolean` | Getter that returns `true` once `setEVMProvider()` has attached a wallet. | | `initializeWithProvider(provider)` | `(provider: any) => Promise` | Runs `client.initialize()` then `client.setEVMProvider(provider)`. Must be called after a wallet is connected. | | `destroy()` | `() => void` | Tears down the client (synchronous). | | `getBalancesForBridge()` | `() => Promise` | Returns bridgeable token balances across all supported chains. | | `getBalancesForSwap()` | `() => Promise` | Returns swappable token balances across all supported chains. | > **Two-step initialization** > > v2 splits startup into two calls: `client.initialize()` loads deployment data from the middleware, and `client.setEVMProvider(provider)` attaches the connected wallet. You can call them in either order, but wallet-dependent methods only work once `setEVMProvider()` has resolved. ## Step 3 — Create the UI components Create four button components in the `src/components` directory. ### connect-button.tsx Connects an EIP-1193 wallet using the browser's injected provider (`window.ethereum`). ```tsx title="src/components/connect-button.tsx" "use client"; export default function ConnectButton({ className, }: { className?: string; }) { const onClick = async () => { const eth = (window as any)?.ethereum; if (!eth) return alert("Install an EIP-1193 wallet (e.g. MetaMask)"); await eth.request?.({ method: "eth_requestAccounts" }); alert("Wallet connected"); }; return ( ); } ``` ### init-button.tsx Initializes the Nexus SDK with the connected wallet's provider. ```tsx title="src/components/init-button.tsx" "use client"; import { initializeWithProvider, client } from "../lib/nexus"; export default function InitButton({ className, onReady, }: { className?: string; onReady?: () => void; }) { const onClick = async () => { const eth = (window as any)?.ethereum; try { await initializeWithProvider(eth); onReady?.(); alert("Nexus initialized"); } catch (e: any) { alert(e?.message ?? "Init failed"); } }; return ( ); } ``` > **Using a wallet library?** > > This tutorial uses `window.ethereum` directly for simplicity. If you use wagmi, RainbowKit, or another wallet library, see [Initializing Nexus with RainbowKit](/docs/nexus/nexus-sdk/get-started/initializing-nexus-with-rainbowkit) for how to retrieve the provider from a connector. ### fetch-balances-button.tsx Fetches the user's balances across all supported chains. ```tsx title="src/components/fetch-balances-button.tsx" "use client"; import { getBalancesForBridge, getBalancesForSwap, client } from "../lib/nexus"; export default function FetchBalancesButton({ className, onResult, }: { className?: string; onResult?: (r: any) => void; }) { const onClick = async () => { if (!client.hasEvmProvider) return alert("Initialize first"); // Use getBalancesForBridge() for bridgeable tokens // Use getBalancesForSwap() for swappable tokens const bridgeBalances = await getBalancesForBridge(); const swapBalances = await getBalancesForSwap(); const res = { bridgeBalances, swapBalances }; onResult?.(res); console.log(res); }; return ( ); } ``` ### de-init-button.tsx Destroys the client and resets state. ```tsx title="src/components/de-init-button.tsx" "use client"; import { destroy, client } from "../lib/nexus"; export default function DeinitButton({ className, onDone, }: { className?: string; onDone?: () => void; }) { const onClick = async () => { destroy(); onDone?.(); alert("Nexus de-initialized"); }; return ( ); } ``` ## Step 4 — Assemble the page Wire all four components together in a single page: ```tsx title="src/app/page.tsx" "use client"; import { useState } from "react"; import ConnectButton from "@/components/connect-button"; import InitButton from "@/components/init-button"; import FetchBalancesButton from "@/components/fetch-balances-button"; import DeinitButton from "@/components/de-init-button"; import { client } from "@/lib/nexus"; export default function Page() { const [initialized, setInitialized] = useState(client.hasEvmProvider); const [balances, setBalances] = useState(null); const btn = "px-4 py-2 rounded-md bg-blue-600 text-white hover:bg-blue-700 " + "disabled:opacity-50 disabled:cursor-not-allowed"; return (
setInitialized(true)} /> setBalances(r)} /> { setInitialized(false); setBalances(null); }} />
SDK Status:{" "} {initialized ? "Initialized" : "Not initialized"}
{balances && (
            {JSON.stringify(balances, null, 2)}
          
)}
); } ``` ## Step 5 — Run and test ```bash pnpm dev ``` Open [http://localhost:3000](http://localhost:3000) and click through the buttons in order: 1. **Connect Wallet** — approve the wallet prompt in MetaMask 2. **Initialize Nexus** — the SDK connects to the wallet provider 3. **Fetch Balances** — a JSON object appears showing your tokens across chains 4. **De-initialize** — the SDK resets > **What you just built** > > In a few lines of code you connected to a wallet, initialized the Nexus SDK, and fetched a cross-chain view of every token the user holds. From here you can bridge tokens, execute swaps, or build any custom UI on top of the headless SDK. ## Next step Upgrade your wallet connection experience by replacing `window.ethereum` with RainbowKit. - [**Initializing Nexus with RainbowKit**](/docs/nexus/nexus-sdk/get-started/initializing-nexus-with-rainbowkit) ## Installation In this guide, you will install **Nexus Widget** — the single unified component for all swap, send, and deposit flows — into an existing React project. By the end, your project will be ready for the provider setup in the next step. ## Prerequisites Before you begin, make sure your project meets the following requirements: * **React 18+** with a framework like [Next.js](https://nextjs.org), [Vite](https://vitejs.dev), or similar * **Tailwind CSS v3 or v4** configured in your project * **A wallet connection library** such as [wagmi](https://wagmi.sh) + [RainbowKit](https://rainbowkit.com) or [ConnectKit](https://docs.family.co/connectkit) * **Node.js 24+** and a package manager (npm, pnpm, or yarn) > **Why wagmi?** > > The widget needs access to the user's connected wallet provider to sign transactions. It is designed to work with wagmi's hooks, but any EIP-1193 compatible provider will work. ## Interactive Configurator Before installing, you can configure your widget live and copy the exact basic config for your desired widget using the Widget Configurator . ## Installation Methods Choose your preferred installation method below. **NPM** is the recommended method for fast integration without adding extra source files to your repository. **shadcn/ui** is available for power users who require full source code control and customization. #### NPM (Recommended) ### Install the NPM Package Install the compiled @avail-project/widgets package directly from npm: ```bash npm install @avail-project/widgets ``` #### pnpm ```bash pnpm add @avail-project/widgets ``` #### yarn ```bash yarn add @avail-project/widgets ``` #### bun ```bash bun add @avail-project/widgets ``` > **Peer dependencies:** `viem` and `wagmi` are required peer dependencies. Ensure they are installed in your project: > > ```bash > npm install @avail-project/widgets viem wagmi @tanstack/react-query > ``` The npm package ships pre-compiled styles and components — no manual component file copying required. ### Legacy standalone components Earlier standalone elements (`SwapWidget`, `FastBridge`, `FastTransfer`, `NexusDeposit`, `UnifiedBalance`, `ViewHistory`) have been removed in favor of Nexus Widget. If you are upgrading from them, follow the [Widgets migration guide](/docs/nexus/migration/widgets-legacy-to-nexus-widget). ## Next step Your dependencies are installed. Next, you will wire up the `NexusProvider` so the SDK can connect to the user's wallet. - [**NexusProvider Setup**](/docs/nexus/widgets/get-started/nexus-provider-setup) — Go to next step. Setting up NexusProvider. ## NexusProvider Setup Nexus Widget relies on the **NexusProvider** — a React context provider that initializes the Nexus SDK and shares wallet state across your component tree. Without it, the widget cannot sign transactions or read on-chain data. In this step you will: 1. Wrap your application layout with the provider 2. Create a small helper component that initializes Nexus when a wallet connects #### NPM (Recommended) ## Step 1 — Wrap your app with NexusProvider Open your root layout file (or the highest-level client boundary in your app) and wrap the children with `NexusProvider` imported from `@avail-project/widgets`. ```tsx title="app/providers.tsx" showLineNumbers "use client"; import { NexusProvider } from "@avail-project/widgets"; export default function Providers({ children, }: { children: React.ReactNode; }) { return {children}; } ``` Then use this component in your root layout: ```tsx title="app/layout.tsx" showLineNumbers import Providers from "./providers"; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( {children}
); } ``` ## Step 2 — Initialize Nexus on wallet connect Create an `InitNexusOnConnect` component that listens for wallet connection events and hands the provider instance to the SDK via `useNexus()` from `@avail-project/widgets`. ```tsx title="components/InitNexusOnConnect.tsx" showLineNumbers "use client"; import { useEffect } from "react"; import { useAccount, useConnectorClient } from "wagmi"; import type { EthereumProvider } from "@avail-project/nexus-core"; import { useNexus } from "@avail-project/widgets"; export function InitNexusOnConnect() { const { status, connector } = useAccount(); const { data: walletClient } = useConnectorClient(); const { handleInit } = useNexus(); useEffect(() => { if (status !== "connected") return; void (async () => { const mobileProvider = walletClient ? ({ request: (args: unknown) => walletClient.request(args as never) } as EthereumProvider) : undefined; const desktopProvider = await connector?.getProvider(); const provider = mobileProvider ?? (desktopProvider as EthereumProvider | undefined); if (!provider || typeof provider.request !== "function") return; await handleInit(provider); })(); }, [status, connector, walletClient, handleInit]); return null; } ``` #### shadcn/ui (Power Users) ## Step 1 — Wrap your app with NexusProvider Open your root layout file and wrap the children with `NexusProvider` installed into your local `components/nexus/` folder. ```tsx title="app/providers.tsx" showLineNumbers "use client"; import NexusProvider from "@/components/nexus/NexusProvider"; export default function Providers({ children, }: { children: React.ReactNode; }) { return {children}; } ``` Then use this component in your root layout: ```tsx title="app/layout.tsx" showLineNumbers import Providers from "./providers"; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( {children}
); } ``` ## Step 2 — Initialize Nexus on wallet connect Create an `InitNexusOnConnect` component that listens for wallet connection events and passes the provider to `handleInit`: ```tsx title="components/InitNexusOnConnect.tsx" showLineNumbers "use client"; import { useEffect } from "react"; import { useAccount, useConnectorClient } from "wagmi"; import type { EthereumProvider } from "@avail-project/nexus-core"; import { useNexus } from "@/components/nexus/NexusProvider"; export function InitNexusOnConnect() { const { status, connector } = useAccount(); const { data: walletClient } = useConnectorClient(); const { handleInit } = useNexus(); useEffect(() => { if (status !== "connected") return; void (async () => { const mobileProvider = walletClient ? ({ request: (args: unknown) => walletClient.request(args as never) } as EthereumProvider) : undefined; const desktopProvider = await connector?.getProvider(); const provider = mobileProvider ?? (desktopProvider as EthereumProvider | undefined); if (!provider || typeof provider.request !== "function") return; await handleInit(provider); })(); }, [status, connector, walletClient, handleInit]); return null; } ``` > **Where to place the provider** > > `NexusProvider` must be rendered **inside** your wallet provider (e.g. wagmi's `WagmiProvider`) because it needs access to the connected wallet. A typical nesting order is: > > `WagmiProvider` → `QueryClientProvider` → `ConnectKitProvider` → **`NexusProvider`** → `{children}` ### Render `InitNexusOnConnect` alongside the provider Add `InitNexusOnConnect` inside your providers component so it runs automatically when the wallet connects: ```tsx title="app/providers.tsx" {3,12} "use client"; import { NexusProvider } from "@avail-project/widgets"; import { InitNexusOnConnect } from "@/components/InitNexusOnConnect"; export default function Providers({ children, }: { children: React.ReactNode; }) { return ( {children} ); } ``` > **Client components only** > > Both `NexusProvider` and `InitNexusOnConnect` must be client components (marked with `"use client"`) because they use React hooks and browser APIs. ## Next step The provider is configured and the SDK will initialize as soon as the user connects a wallet. You are now ready to render your first component. - [**Your First Widget**](/docs/nexus/widgets/get-started/your-first-widget) — Go to next step. Rendering the Nexus Widget. ## Your First Widget You have installed the component and configured the `NexusProvider`. Now it is time to render a working widget. In this guide you will add **Nexus Widget** to a page, understand each prop it accepts, and handle its lifecycle callbacks. ## What you will build By the end of this page you will have a fully functional swap-and-bridge widget embedded in your app. The user will be able to select a source and receive asset across chains and execute the intent — all with built-in progress UI, fee breakdowns, and error handling. Switching to send or deposit is a one-line `config.mode` change. ## Interactive Configurator You can generate the exact basic config for your desired widget mode live using the Widget Configurator . ## Step 1 — Install the package If you have not already installed the package during the installation step, install it now: #### NPM (Recommended) ```bash npm install @avail-project/widgets ``` #### shadcn/ui (Power Users) ```bash npx shadcn@latest add availproject/widgets/nexus ``` ## Step 2 — Create a page Create a new page (or add to an existing one) that renders `NexusWidget`. Here is a complete example: #### NPM Import (Recommended) ```tsx title="app/swap/page.tsx" showLineNumbers "use client"; import { useAccount } from "wagmi"; import { NexusWidget } from "@avail-project/widgets"; const USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; export default function SwapPage() { const { address } = useAccount(); if (!address) { return

Connect your wallet to get started.

; } return ( console.log("Intent started")} onComplete={(explorerUrl) => console.log("Complete!", explorerUrl)} onError={(message) => console.error("Error:", message)} /> ); } ``` #### shadcn Import (Power Users) ```tsx title="app/swap/page.tsx" showLineNumbers "use client"; import { useAccount } from "wagmi"; import { NexusWidget } from "@/components/nexus/nexus"; const USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; export default function SwapPage() { const { address } = useAccount(); if (!address) { return

Connect your wallet to get started.

; } return ( console.log("Intent started")} onComplete={(explorerUrl) => console.log("Complete!", explorerUrl)} onError={(message) => console.error("Error:", message)} /> ); } ``` ## Step 3 — Understand the props Here is a breakdown of the props `NexusWidget` accepts and what they control: | Prop | Type | Required | Description | | -------------------- | -------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------ | | `config` | `NexusWidgetConfig` | Yes | Selects the workflow with `config.mode` (`"swap"`, `"send"`, or `"deposit"`) and any mode-specific behavior. | | `config.mode` | `"swap" \| "send" \| "deposit"` | Yes | The flow to render. | | `config.destination` | `{ chain?, tokens? }` | Conditional | Required for deposit. Optional for send/swap; when supplied, it restricts the destination chain/token selection. | | `config.prefill` | `object` | No | Pre-populate the form (amount for deposit/send, initial token for send/swap) so the user acts with fewer clicks. | | `config.appearance` | `object` | No | Branding overrides: `heading`, `appName`, `appLogoURL`, `primaryColor`, and `mode` (`"system"`, `"light"`, `"dark"`). | | `connectedAddress` | `` `0x${string}` `` | No | Wallet address to use. If omitted, the connected wagmi account is used. | | `embed` | `boolean` | No | Defaults to `true`. Set `false` to render as a modal surface, controlled with `open`, `onOpenChange`, and `defaultOpen`. | | `onComplete` | `(explorerUrl?: string) => void` | No | Called after a successful swap, send, or deposit. Use it to refresh balances or navigate. | | `onStart` | `() => void` | No | Called when the user starts execution. Useful for analytics. | | `onError` | `(message: string) => void` | No | Called if execution fails, with a human-readable message. | | `onClose` | `() => void` | No | Used by modal mode. The close button is hidden in embedded mode. | | `onConnectClick` | `() => void \| Promise` | No | Called when the widget's internal Connect Wallet CTA is clicked. Use it to open your app's wallet modal. | > **config.mode is the only always-required field** > > Beyond `config.mode`, every field is optional unless the selected mode makes it conditional (deposit requires `destination`, `depositAddress`, and `executeDeposit`). See the [Swap and Bridge](/docs/nexus/widgets/components/swaps), [Send](/docs/nexus/widgets/components/transfer), and [Deposit](/docs/nexus/widgets/components/deposit) pages for each mode's configuration reference. ## Step 4 — Run and test Start your development server and navigate to the page: ```bash npm run dev ``` 1. Open your app in the browser 2. Connect a wallet using your wallet connection UI 3. The widget should appear with USDC on Base pre-selected as the receive asset 4. Try selecting a different token or chain to verify the form is interactive > **Mainnet only** > > Nexus Widget currently supports mainnet only. Testnet is not supported at the moment. ## Switching modes The same component powers send and deposit — just change `config.mode`. To render a send flow instead: ```tsx console.log("Send complete")} /> ``` ## What's next You now have a working Nexus Widget integration. From here you can explore each mode: - [**Swap and Bridge**](/docs/nexus/widgets/components/swaps) — Swap and bridge assets with the unified Nexus intent flow. - [**Send**](/docs/nexus/widgets/components/transfer) — Route funds to a recipient with Nexus intents. - [**Deposit**](/docs/nexus/widgets/components/deposit) — Route funds into a configured protocol or app action with Nexus intents. ## Calculate Max for Swap > **Note** > > **SET UP THE SDK BEFORE YOU START:** > > Install and initialize the client first — see [Installation](/docs/nexus/nexus-sdk/get-started/installation) and [SDK Setup](/docs/nexus/nexus-sdk/get-started/nexus-sdk-setup). Use the `calculateMaxForSwap()` function to compute the maximum amount that can be swapped to a destination token across all available sources. This is useful for populating a "Max" button before calling `swapWithExactIn()` or `swapWithExactOut()`. > **Note** > > **Note:** Check out the [API reference](/docs/nexus/supported-chains-and-tokens) for a full list of supported tokens and chains. ## Method signature ```typescript title="TypeScript" calculateMaxForSwap( input: SwapMaxParams, ): Promise ``` ## Parameters ```typescript title="TypeScript" export type SwapMaxParams = { toChainId: number; toTokenAddress: Hex; sources?: Source[]; }; export type Source = { tokenAddress: Hex; chainId: number; }; ``` * `SwapMaxParams`: Parameters for the `calculateMaxForSwap()` function. * `toChainId` (number, required): The chain ID of the destination chain. * `toTokenAddress` (Hex, required): The contract address of the token to receive on the destination chain. * `sources` (Source\[], optional): Restrict which source chains and tokens are considered. If omitted, all available holdings are considered. * `tokenAddress` (Hex): The contract address of the source token. * `chainId` (number): The chain ID of the source chain. ## Example ```typescript title="TypeScript" import type { SwapMaxParams, SwapMaxResult } from '@avail-project/nexus-core'; const max = await client.calculateMaxForSwap({ toChainId: 8453, toTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base }); console.log(`Max swappable: ${max.maxAmount} ${max.symbol}`); console.log('Sources used:', max.sources); ``` You can also restrict which source chains and tokens are considered: ```typescript title="TypeScript" const max = await client.calculateMaxForSwap({ toChainId: 8453, toTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', sources: [ { chainId: 10, tokenAddress: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85' }, ], }); // Feed the raw amount straight into an exact-out swap: await client.swapWithExactOut({ toChainId: 8453, toTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', toAmountRaw: max.maxAmountRaw, }); ``` ## Return Value The return value is a `SwapMaxResult` object. ```typescript title="TypeScript" export type SwapMaxResult = { toChainId: number; toTokenAddress: Hex; maxAmount: string; // Human-readable decimal string maxAmountRaw: bigint; // Raw amount suitable for toAmountRaw in swapWithExactOut symbol: string; decimals: number; sources: { chainId: number; tokenAddress: Hex; symbol: string; decimals: number; amount: string; // Human-readable portion sourced from this chain/token }[]; }; ``` * `maxAmount` (string): The maximum receivable amount as a human-readable decimal string. * `maxAmountRaw` (bigint): The same maximum in raw integer units — suitable for `toAmountRaw` in `swapWithExactOut()`. * `symbol` / `decimals`: Metadata for the destination token. * `sources`: The per-source breakdown of holdings that make up the max. > **Note** > > On failure the method throws a typed [`NexusError`](/docs/nexus/nexus-sdk/reference/hooks-and-errors) subclass. See the [full type definitions](https://github.com/availproject/nexus-sdk/blob/main/src/swap/types.ts) on GitHub. ## Fetch Swap Balances > **Note** > > **SET UP THE SDK BEFORE YOU START:** > > Install and initialize the client first — see [Installation](/docs/nexus/nexus-sdk/get-started/installation) and [SDK Setup](/docs/nexus/nexus-sdk/get-started/nexus-sdk-setup). Use the `getBalancesForSwap()` function to fetch token balances that can be used in swap operations across all supported chains in one call. > **Note** > > **Note:** Check out the [API reference](/docs/nexus/supported-chains-and-tokens) for a full list of supported tokens and chains. ## Method signature ```typescript title="TypeScript" getBalancesForSwap(): Promise ``` > **Note** > > `getBalancesForSwap()` returns the same `TokenBalance[]` shape as `getBalancesForBridge()`. If you need the tokens that can be used for bridging instead, use [`getBalancesForBridge()`](/docs/nexus/nexus-sdk/reference/bridge-methods/fetch-bridge-balances). ## Parameters * None ## Example Here is a minimal example of how to use the `getBalancesForSwap()` function to fetch balances that are eligible to be used as swap inputs: ```typescript title="TypeScript" // Get all balances that are eligible for swap operations const swapBalances = await client.getBalancesForSwap(); console.log('Swap balances:', swapBalances); ``` ## Return Value `getBalancesForSwap()` returns `Promise` — an array of `TokenBalance` objects. Each `TokenBalance` aggregates a token's holdings across every chain it lives on, with the per-chain split in `chainBalances`. ```typescript title="TypeScript" export type TokenBalance = { balance: string; // Aggregate balance across all chains (human-readable) value: string; // Aggregate fiat value (string for precision) chainBalances: ChainBalance[];// Per-chain breakdown currencyId?: number; // Currency identifier (when applicable) decimals: number; logo: string; name: string; // Display label (e.g. "USDC/USDM") symbol: string; // Majority symbol by chain count }; export type ChainBalance = { balance: string; // Balance on this chain (human-readable) value: string; // Fiat value on this chain (string for precision) symbol: string; chain: { id: number; logo: string; name: string; }; contractAddress: `0x${string}`; decimals: number; universe: Universe; // VM family of the chain (e.g. 'EVM') }; ``` > **Note** > > Field renames from [v1](https://github.com/availproject/nexus-sdk/blob/main/MIGRATION.md): `balanceInFiat` → `value`, `icon` → `logo`, and `breakdown` → `chainBalances`. See the [full type definitions](https://github.com/availproject/nexus-sdk/blob/main/src/domain/types/index.ts) on GitHub. ## Swap and Execute > **Note** > > **SET UP THE SDK BEFORE YOU START:** > > Install and initialize the client first — see [Installation](/docs/nexus/nexus-sdk/get-started/installation) and [SDK Setup](/docs/nexus/nexus-sdk/get-started/nexus-sdk-setup). Use the `swapAndExecute()` function to swap tokens to a destination chain and then execute a contract call, in a single flow. If sufficient funds already exist on the destination chain, the swap is skipped and only the execution runs. Swaps are routed through multiple DEX aggregators (such as LiFi, Bebop, and 0x), quoted in parallel. Execution runs through a per-chain smart account chosen automatically — an ephemeral key delegated via EIP-7702 on 7702-enabled chains, or a deterministic Safe owned by the ephemeral key elsewhere. > **Note** > > **Note:** Check out the [API reference](/docs/nexus/supported-chains-and-tokens) for a full list of supported tokens and chains. ## Method signature ```typescript title="TypeScript" swapAndExecute( input: SwapAndExecuteParams, options?: SwapAndExecuteOptions, ): Promise ``` ## Parameters ```typescript title="TypeScript" export interface SwapAndExecuteParams { toChainId: number; toTokenAddress: Hex; toAmountRaw: bigint; sources?: Source[]; execute: SwapExecuteParams; } export interface SwapExecuteParams { to: Hex; value?: bigint; data?: Hex; gas: bigint; gasPrice?: 'low' | 'medium' | 'high'; tokenApproval?: { toTokenAddress: Hex; amount: bigint; spender: Hex }; } export type Source = { tokenAddress: Hex; chainId: number; }; ``` * `SwapAndExecuteParams`: Parameters for the `swapAndExecute()` function. * `toChainId` (number, required): The chain ID of the destination chain. * `toTokenAddress` (Hex, required): The contract address of the token needed on the destination chain. * `toAmountRaw` (bigint, required): The amount of the destination token needed, in raw integer units. * `sources` (Source\[], optional): Restrict which chains and tokens can be used as the swap source. If omitted, the SDK selects the best sources from available balances. * `execute` (`SwapExecuteParams`, required): The contract call to run on the destination chain. * `to` (Hex, required): The contract address to call. * `value` (bigint, optional): Native token value to send, in raw integer units (wei). * `data` (Hex, optional): The encoded function call data. * `gas` (bigint, required): The gas limit. Chain-specific adjustments and buffering are applied by the SDK. * `gasPrice` (`'low' | 'medium' | 'high'`, optional): Gas price strategy. * `tokenApproval` (optional): A token approval to submit before the execution call. * `toTokenAddress` (Hex): The token to approve. * `amount` (bigint): The approval amount, in raw integer units. * `spender` (Hex): The spender address. * `options`: `SwapAndExecuteOptions` (optional): Callbacks to track and gate the operation. ```typescript title="TypeScript" export type SwapAndExecuteOptions = { onEvent?: (event: SwapAndExecuteEvent) => void; onIntent?: (data: SwapAndExecuteOnIntentHookData) => void; slippageTolerance?: number; }; ``` * `onEvent` (optional): Receives status, plan preview/confirmed, and per-step progress updates. `swapAndExecute()` emits a superset of the [Swap Events](/docs/nexus/nexus-sdk/reference/swap-methods/swap-events) — the swap events plus a `preparing` status and the execute approval/transaction progress events. * `onIntent` (optional, top-level): Called with the resolved intent before execution. The intent carries `swapRequired`. Call `allow()` to proceed, `deny()` to cancel, or `refresh(sources?)` to re-quote. * `slippageTolerance` (number, optional): Slippage override as a fraction (default `0.005`, i.e. 0.5%). ## Example ```typescript title="TypeScript" import type { SwapAndExecuteParams, SwapAndExecuteOptions, SwapAndExecuteResult, } from '@avail-project/nexus-core'; const result = await client.swapAndExecute( { toChainId: 42161, toTokenAddress: '0xaf88d065e77c8cc2239327c5edb3a432268e5831', toAmountRaw: 100_000_000n, execute: { to: '0x3333333333333333333333333333333333333333', data: '0xdeadbeef', gas: 100_000n, value: 0n, tokenApproval: { toTokenAddress: '0xaf88d065e77c8cc2239327c5edb3a432268e5831', amount: 100_000_000n, spender: '0x3333333333333333333333333333333333333333', }, }, }, { onEvent: (event) => { if (event.type === 'status') { // preparing | route_building | route_ready | awaiting_approval | approved | executing | completed console.log('Status:', event.status); } }, onIntent: ({ allow, deny, refresh, intent }) => { console.log('Swap required:', intent.swapRequired); allow(); }, }, ); console.log('Swap skipped:', result.swapSkipped); console.log('Swap result:', result.swapResult); console.log('Execute tx hash:', result.execute.txHash); ``` ## Return Value The return value is a `SwapAndExecuteResult` object. It always carries the `execute` transaction result (and an optional `approval`), plus a discriminated `swapSkipped` flag that tells you whether a swap ran. ```typescript title="TypeScript" export type SwapAndExecuteResult = { approval?: TxResult; execute: TxResult; } & ( | { swapSkipped: false; swapResult: SwapResult } | { swapSkipped: true; swapResult?: undefined } ); export type TxResult = { txHash: Hex; txExplorerUrl: string; receipt?: TransactionReceipt; }; ``` * `approval` (`TxResult`, optional): The approval transaction, when one was required. * `execute` (`TxResult`): The contract execution transaction on the destination chain. * `swapSkipped` (boolean): `true` when destination funds were already sufficient and no swap ran. * `swapResult` (`SwapResult`): Present only when `swapSkipped` is `false`. See [Swap Exact In](/docs/nexus/nexus-sdk/reference/swap-methods/swap-exact-in#return-value) for the `SwapResult` shape. > **Note** > > On failure the method throws a typed [`NexusError`](/docs/nexus/nexus-sdk/reference/hooks-and-errors) subclass rather than returning an error result. See the [full type definitions](https://github.com/availproject/nexus-sdk/blob/main/src/swap/types.ts) on GitHub. ## Swap Events Both `swapWithExactIn()` and `swapWithExactOut()` accept an `onEvent` callback (via `SwapOperationOptions`) that receives a typed `SwapEvent`. Every event carries a `type` discriminant, so you can narrow on `event.type` and then, for progress events, on `event.stepType` and `event.state`. ## Event union ```typescript title="TypeScript" export type SwapEvent = | SwapStatusEvent // type: 'status' | SwapPlanPreviewEvent // type: 'plan_preview' | SwapPlanConfirmedEvent // type: 'plan_confirmed' | SwapPlanProgressEvent; // type: 'plan_progress' ``` ### Status events Emitted as the operation moves through its lifecycle. ```typescript title="TypeScript" export type SwapStatus = | 'route_building' | 'route_ready' | 'awaiting_approval' | 'approved' | 'executing' | 'completed'; export type SwapStatusEvent = { type: 'status'; status: SwapStatus; }; ``` ### Plan preview & confirmed events `plan_preview` is emitted once the route is planned; `plan_confirmed` is emitted after the intent is approved. Both carry the same `SwapPlan`. ```typescript title="TypeScript" export type SwapPlanPreviewEvent = { type: 'plan_preview'; plan: SwapPlan; }; export type SwapPlanConfirmedEvent = { type: 'plan_confirmed'; plan: SwapPlan; }; export type SwapPlan = { hasBridge: boolean; hasDestinationSwap: boolean; steps: SwapPlanStep[]; }; export type SwapPlanStep = | SwapSourceSwapStep // type: 'source_swap' | SwapEoaToEphemeralTransferStep // type: 'eoa_to_ephemeral_transfer' | SwapBridgeDepositStep // type: 'bridge_deposit' | SwapBridgeIntentSubmissionStep // type: 'bridge_intent_submission' | BridgeFillStep // type: 'bridge_fill' | SwapDestinationSwapStep; // type: 'destination_swap' ``` ## Progress events `plan_progress` events are emitted as each step advances. Narrow first on `stepType`, then on `state`. Every progress event carries its `step` object. ```typescript title="TypeScript" export type SwapPlanProgressEvent = | SwapSourceSwapProgressEvent | SwapEoaToEphemeralTransferProgressEvent | SwapBridgeDepositProgressEvent | SwapBridgeIntentSubmissionProgressEvent | SwapBridgeFillProgressEvent | SwapDestinationSwapProgressEvent; ``` The table below lists the `state` values each `stepType` can emit: | `stepType` | States | | --------------------------- | -------------------------------------------------------------------- | | `source_swap` | `wallet_prompted` · `started` · `submitted` · `confirmed` · `failed` | | `eoa_to_ephemeral_transfer` | `wallet_prompted` · `submitted` · `confirmed` · `failed` | | `bridge_deposit` | `started` · `submitted` · `confirmed` · `failed` | | `bridge_intent_submission` | `started` · `completed` · `failed` | | `bridge_fill` | `waiting` · `completed` · `failed` | | `destination_swap` | `wallet_prompted` · `started` · `submitted` · `confirmed` · `failed` | ### Field shapes `submitted` and `confirmed` states carry `txHash` and `explorerUrl`. `failed` states carry an `error` string (and, for tx-based steps, optional `txHash`/`explorerUrl`). The bridge steps carry an `intentRequestHash` instead. ```typescript title="TypeScript" export type SwapSourceSwapProgressEvent = | { type: 'plan_progress'; stepType: 'source_swap'; state: 'wallet_prompted'; step: SwapSourceSwapStep } | { type: 'plan_progress'; stepType: 'source_swap'; state: 'started'; step: SwapSourceSwapStep } | { type: 'plan_progress'; stepType: 'source_swap'; state: 'submitted'; step: SwapSourceSwapStep; txHash: Hex; explorerUrl: string } | { type: 'plan_progress'; stepType: 'source_swap'; state: 'confirmed'; step: SwapSourceSwapStep; txHash: Hex; explorerUrl: string } | { type: 'plan_progress'; stepType: 'source_swap'; state: 'failed'; step: SwapSourceSwapStep; txHash?: Hex; explorerUrl?: string; error: string }; export type SwapEoaToEphemeralTransferProgressEvent = | { type: 'plan_progress'; stepType: 'eoa_to_ephemeral_transfer'; state: 'wallet_prompted'; step: SwapEoaToEphemeralTransferStep } | { type: 'plan_progress'; stepType: 'eoa_to_ephemeral_transfer'; state: 'submitted'; step: SwapEoaToEphemeralTransferStep; txHash: Hex; explorerUrl: string } | { type: 'plan_progress'; stepType: 'eoa_to_ephemeral_transfer'; state: 'confirmed'; step: SwapEoaToEphemeralTransferStep; txHash: Hex; explorerUrl: string } | { type: 'plan_progress'; stepType: 'eoa_to_ephemeral_transfer'; state: 'failed'; step: SwapEoaToEphemeralTransferStep; txHash?: Hex; explorerUrl?: string; error: string }; export type SwapBridgeDepositProgressEvent = | { type: 'plan_progress'; stepType: 'bridge_deposit'; state: 'started'; step: SwapBridgeDepositStep } | { type: 'plan_progress'; stepType: 'bridge_deposit'; state: 'submitted'; step: SwapBridgeDepositStep; txHash: Hex; explorerUrl: string } | { type: 'plan_progress'; stepType: 'bridge_deposit'; state: 'confirmed'; step: SwapBridgeDepositStep; txHash: Hex; explorerUrl: string } | { type: 'plan_progress'; stepType: 'bridge_deposit'; state: 'failed'; step: SwapBridgeDepositStep; txHash?: Hex; explorerUrl?: string; error: string }; export type SwapBridgeIntentSubmissionProgressEvent = | { type: 'plan_progress'; stepType: 'bridge_intent_submission'; state: 'started'; step: SwapBridgeIntentSubmissionStep } | { type: 'plan_progress'; stepType: 'bridge_intent_submission'; state: 'completed'; step: SwapBridgeIntentSubmissionStep; intentRequestHash: Hex } | { type: 'plan_progress'; stepType: 'bridge_intent_submission'; state: 'failed'; step: SwapBridgeIntentSubmissionStep; intentRequestHash?: Hex; error: string }; export type SwapBridgeFillProgressEvent = | { type: 'plan_progress'; stepType: 'bridge_fill'; state: 'waiting'; step: BridgeFillStep; intentRequestHash: Hex } | { type: 'plan_progress'; stepType: 'bridge_fill'; state: 'completed'; step: BridgeFillStep; intentRequestHash: Hex } | { type: 'plan_progress'; stepType: 'bridge_fill'; state: 'failed'; step: BridgeFillStep; intentRequestHash: Hex; error: string }; export type SwapDestinationSwapProgressEvent = | { type: 'plan_progress'; stepType: 'destination_swap'; state: 'wallet_prompted'; step: SwapDestinationSwapStep } | { type: 'plan_progress'; stepType: 'destination_swap'; state: 'started'; step: SwapDestinationSwapStep } | { type: 'plan_progress'; stepType: 'destination_swap'; state: 'submitted'; step: SwapDestinationSwapStep; txHash: Hex; explorerUrl: string } | { type: 'plan_progress'; stepType: 'destination_swap'; state: 'confirmed'; step: SwapDestinationSwapStep; txHash: Hex; explorerUrl: string } | { type: 'plan_progress'; stepType: 'destination_swap'; state: 'failed'; step: SwapDestinationSwapStep; txHash?: Hex; explorerUrl?: string; error: string }; ``` ### Step shapes ```typescript title="TypeScript" export type PlanTokenAmount = { symbol: string; contractAddress: Hex; decimals: number; logo?: string; amount: string; // human-readable amountRaw: bigint; // raw integer units }; export type SwapSourceSwapStep = { type: 'source_swap'; id: string; chain: { id: number; name: string; logo: string }; walletPath: 'ephemeral' | 'safe'; swaps: { input: PlanTokenAmount; output: PlanTokenAmount }[]; }; export type SwapEoaToEphemeralTransferStep = { type: 'eoa_to_ephemeral_transfer'; id: string; chain: { id: number; name: string; logo: string }; asset: PlanTokenAmount; }; export type SwapBridgeDepositStep = { type: 'bridge_deposit'; id: string; chain: { id: number; name: string; logo: string }; asset: PlanTokenAmount; }; export type SwapBridgeIntentSubmissionStep = { type: 'bridge_intent_submission'; id: string; }; export type BridgeFillStep = { type: 'bridge_fill'; id: string; chain: { id: number; name: string; logo: string }; asset: PlanTokenAmount; }; export type SwapDestinationSwapStep = { type: 'destination_swap'; id: string; chain: { id: number; name: string; logo: string }; walletPath: 'ephemeral' | 'safe'; swaps: { input: PlanTokenAmount; output: PlanTokenAmount }[]; }; ``` ## Usage example ```typescript title="TypeScript" const result = await client.swapWithExactIn(swapInput, { onEvent: (event) => { switch (event.type) { case 'status': console.log('Status:', event.status); break; case 'plan_preview': console.log('Planned steps:', event.plan.steps); console.log('Has bridge:', event.plan.hasBridge); break; case 'plan_progress': if (event.stepType === 'source_swap' && event.state === 'confirmed') { console.log('Source swap confirmed:', event.txHash, event.explorerUrl); } if (event.stepType === 'destination_swap' && event.state === 'confirmed') { console.log('Destination swap confirmed:', event.txHash, event.explorerUrl); } break; } }, }); ``` } title="Swap event types" href="https://github.com/availproject/nexus-sdk/blob/main/src/domain/types/swap-events.ts" target="_blank" rel="noopener noreferrer" arrow /> > **Note** > > `swapAndExecute()` emits a superset of these events (`SwapAndExecuteEvent`), adding a `preparing` status plus the execute approval and transaction progress events. See the [Swap and Execute](/docs/nexus/nexus-sdk/reference/swap-methods/swap-and-execute) page. ## Swap Exact Input > **Note** > > **SET UP THE SDK BEFORE YOU START:** > > Install and initialize the client first — see [Installation](/docs/nexus/nexus-sdk/get-started/installation) and [SDK Setup](/docs/nexus/nexus-sdk/get-started/nexus-sdk-setup). Use the `swapWithExactIn()` function to swap tokens with an exactly defined input amount. For example, if you want to swap `1 USDC` into whatever amount of the destination token you can get on the destination chain. Swaps are routed through multiple DEX aggregators (such as LiFi, Bebop, and 0x), quoted in parallel. Execution runs through a per-chain smart account chosen automatically — an ephemeral key delegated via EIP-7702 on 7702-enabled chains, or a deterministic Safe owned by the ephemeral key elsewhere. The connected EOA is never used to dispatch a swap directly. ## Method signature ```typescript title="TypeScript" swapWithExactIn( input: SwapExactInParams, options?: SwapOperationOptions, ): Promise ``` ## Parameters ```typescript title="TypeScript" export interface SwapExactInParams { sources?: (Source & { amountRaw?: bigint })[]; toChainId: number; toTokenAddress: Hex; } export type Source = { tokenAddress: Hex; chainId: number; }; ``` * `SwapExactInParams`: Parameters for using the `swapWithExactIn()` function. * `sources` (`Array`, optional): The source tokens and amounts to swap from. Omit to let the SDK use all available holdings. * `chainId` (number, required): The chain ID of the source chain. * `tokenAddress` (Hex, required): The contract address of the source token. * `amountRaw` (bigint, optional): The amount of the source token to swap, in raw integer units (e.g. `1_000_000n` for 1 USDC). * `toChainId` (number, required): The chain ID of the destination chain. * `toTokenAddress` (Hex, required): The contract address of the token to receive on the destination chain. * `options`: `SwapOperationOptions` (optional): Callbacks to track and gate the swap operation. ```typescript title="TypeScript" export type SwapOperationOptions = { onEvent?: (event: SwapEvent) => void; hooks?: { onIntent?: (data: OnIntentHookData) => void; }; slippageTolerance?: number; }; ``` * `onEvent` (optional): Receives `status`, `plan_preview`, `plan_confirmed`, and `plan_progress` updates as the swap progresses. See the [Swap Events](/docs/nexus/nexus-sdk/reference/swap-methods/swap-events) page for the full typed union. * `hooks.onIntent` (optional): Called with the resolved swap intent before execution. Call `allow()` to proceed, `deny()` to cancel, or `refresh(sources?)` to re-quote. * `slippageTolerance` (number, optional): Slippage override as a fraction (default `0.005`, i.e. 0.5%). ## Example ```typescript title="TypeScript" import type { SwapExactInParams, SwapOperationOptions, SwapResult, } from '@avail-project/nexus-core'; const result = await client.swapWithExactIn( { sources: [ { chainId: 10, amountRaw: 1_000_000n, tokenAddress: '0x...' }, { chainId: 42161, amountRaw: 500_000n, tokenAddress: '0x...' }, ], toChainId: 8453, toTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', }, { onEvent: (event) => { if (event.type === 'plan_progress') { console.log('Swap progress:', event.step.type, event.state); } }, hooks: { onIntent: ({ intent, allow }) => { console.log('Swap intent:', intent); allow(); }, }, slippageTolerance: 0.005, }, ); console.log('Swap with exact in result:', result); ``` ## Return Value The return value is a `SwapResult` object. ```typescript title="TypeScript" export type SwapResult = { sourceSwaps: ChainSwap[]; intentExplorerUrl: string; destinationSwap: ChainSwap | null; intent: SwapIntent; }; export type ChainSwap = { chainId: number; swaps: Swap[]; txHash: Hex; }; export type Swap = { inputAmount: bigint; inputContract: Hex; inputDecimals: number; outputAmount: bigint; outputContract: Hex; outputDecimals: number; }; ``` > **Note** > > On failure the method throws a typed [`NexusError`](/docs/nexus/nexus-sdk/reference/hooks-and-errors) subclass rather than returning an error result. See the [full type definitions](https://github.com/availproject/nexus-sdk/blob/main/src/swap/types.ts) on GitHub. ## Swap Exact Out > **Note** > > **SET UP THE SDK BEFORE YOU START:** > > Install and initialize the client first — see [Installation](/docs/nexus/nexus-sdk/get-started/installation) and [SDK Setup](/docs/nexus/nexus-sdk/get-started/nexus-sdk-setup). Use the `swapWithExactOut()` function to swap tokens with an exactly defined output amount. For example, if you want to get exactly `100 USDC` on the destination but don't care which amount of source funds are used to source the swap. You can however limit your source chains and tokens to a subset of the total supported holdings. Swaps are routed through multiple DEX aggregators (such as LiFi, Bebop, and 0x), quoted in parallel. Execution runs through a per-chain smart account chosen automatically — an ephemeral key delegated via EIP-7702 on 7702-enabled chains, or a deterministic Safe owned by the ephemeral key elsewhere. The connected EOA is never used to dispatch a swap directly. ## Method signature ```typescript title="TypeScript" swapWithExactOut( input: SwapExactOutParams, options?: SwapOperationOptions, ): Promise ``` ## Parameters ```typescript title="TypeScript" export interface SwapExactOutParams { sources?: Source[]; toChainId: number; toTokenAddress: Hex; toAmountRaw: bigint; toNativeAmountRaw?: bigint; } export type Source = { tokenAddress: Hex; chainId: number; }; ``` * `SwapExactOutParams`: Parameters for the `swapWithExactOut()` function. * `sources` (Source\[], optional): Restrict which chains and tokens can be used as the swap source. If omitted, the SDK automatically selects the best sources from available balances. * `tokenAddress` (Hex): The contract address of the source token. * `chainId` (number): The chain ID of the source chain. * `toChainId` (number, required): The chain ID of the destination chain where you want to receive tokens. * `toTokenAddress` (Hex, required): The contract address of the token you want to receive. * `toAmountRaw` (bigint, required): The exact amount of tokens you want to receive on the destination chain, in raw integer units (e.g. `1_000_000n` for 1 USDC). * `toNativeAmountRaw` (bigint, optional): Amount of native gas tokens to receive on the destination chain alongside the swap, in raw integer units (wei). Useful for ensuring the user has gas for subsequent transactions. * `options`: `SwapOperationOptions` (optional): Callbacks to track and gate the swap operation. ```typescript title="TypeScript" export type SwapOperationOptions = { onEvent?: (event: SwapEvent) => void; hooks?: { onIntent?: (data: OnIntentHookData) => void; }; slippageTolerance?: number; }; ``` * `onEvent` (optional): Receives `status`, `plan_preview`, `plan_confirmed`, and `plan_progress` updates as the swap progresses. See the [Swap Events](/docs/nexus/nexus-sdk/reference/swap-methods/swap-events) page for the full typed union. * `hooks.onIntent` (optional): Called with the resolved swap intent before execution. Call `allow()` to proceed, `deny()` to cancel, or `refresh(sources?)` to re-quote. * `slippageTolerance` (number, optional): Slippage override as a fraction (default `0.005`, i.e. 0.5%). ## Example ```typescript title="TypeScript" import type { SwapExactOutParams, SwapOperationOptions, SwapResult, } from '@avail-project/nexus-core'; const result = await client.swapWithExactOut( { toChainId: 42161, toTokenAddress: '0xaf88d065e77c8cc2239327c5edb3a432268e5831', // USDC on Arbitrum toAmountRaw: 100_000_000n, // 100 USDC (6 decimals) // Optional: also fund destination native gas toNativeAmountRaw: 100_000_000_000_000n, // Optional: restrict route planning to specific source tokens/chains sources: [{ chainId: 8453, tokenAddress: '0x...' }], }, { onEvent: (event) => { if (event.type === 'status') { console.log('Swap status:', event.status); } if (event.type === 'plan_preview') { console.log('Swap plan:', event.plan.steps); console.log('Has bridge:', event.plan.hasBridge); console.log('Has destination swap:', event.plan.hasDestinationSwap); } }, hooks: { onIntent: ({ allow, deny, refresh, intent }) => { console.log('Swap intent:', intent); allow(); }, }, }, ); console.log('Swap with exact out result:', result); ``` ## Return Value The return value is a `SwapResult` object. ```typescript title="TypeScript" export type SwapResult = { sourceSwaps: ChainSwap[]; intentExplorerUrl: string; destinationSwap: ChainSwap | null; intent: SwapIntent; }; export type ChainSwap = { chainId: number; swaps: Swap[]; txHash: Hex; }; export type Swap = { inputAmount: bigint; inputContract: Hex; inputDecimals: number; outputAmount: bigint; outputContract: Hex; outputDecimals: number; }; ``` > **Note** > > On failure the method throws a typed [`NexusError`](/docs/nexus/nexus-sdk/reference/hooks-and-errors) subclass rather than returning an error result. See the [full type definitions](https://github.com/availproject/nexus-sdk/blob/main/src/swap/types.ts) on GitHub. ## Bridge Tokens > **Note** > > **SET UP THE SDK BEFORE YOU START:** > > Install and initialize the client first — see [Installation](/docs/nexus/nexus-sdk/get-started/installation) and [SDK Setup](/docs/nexus/nexus-sdk/get-started/nexus-sdk-setup). Use the `bridge()` function to bridge a specific token from one (*or many*) chains to a single chain. Use the `simulateBridge()` function to simulate the bridge transaction to preview the costs and fees, before actually executing the transaction. > **Note** > > **Note:** Check out the [API reference](/docs/nexus/supported-chains-and-tokens) for a full list of supported tokens and chains. ## Method signature ```typescript title="TypeScript" bridge( params: BridgeParams, options?: BridgeOperationOptions, ): Promise simulateBridge( params: BridgeParams, ): Promise ``` ## Parameters ```typescript title="TypeScript" /** * Parameters for bridging tokens. */ export interface BridgeParams { recipient?: Hex; toTokenSymbol: string; toAmountRaw: bigint; toChainId: number; toNativeAmountRaw?: bigint; sources?: number[]; } ``` * `BridgeParams`: Parameters for bridging tokens. * `recipient` (Hex, optional): The recipient address. Defaults to the address of the connected user. * `toTokenSymbol` (string, required): The symbol of the token to be bridged (e.g. `'USDC'`). * `toAmountRaw` (bigint, required): The amount of tokens to receive on the destination chain, in raw integer units. * `toChainId` (number, required): The chain ID of the destination chain. * `toNativeAmountRaw` (bigint, optional): Amount of destination-chain native gas to also deliver to the recipient, in raw integer units. * `sources` (number\[], optional): The chain IDs of the source chains to be used for the bridge. Useful if you want to maintain your holdings on some chains. * `options`: `BridgeOperationOptions` (optional): Per-operation hooks, event listener, and fill timeout. ```typescript title="TypeScript" export type BridgeOperationOptions = { hooks?: { onIntent?: (data: OnIntentHookData) => void; onAllowance?: (data: OnAllowanceHookData) => void; }; onEvent?: (event: BridgeEvent) => void; fillTimeoutMinutes?: number; }; ``` * `hooks.onIntent` (optional): Called with the resolved intent before execution. Call `data.allow()` / `data.deny()`, or `data.refresh(sources?)` to re-quote. When omitted, the intent is auto-approved. * `hooks.onAllowance` (optional): Called when ERC-20 allowances are required. Call `data.allow([...])` with a strategy per source. When omitted, allowances default to `'min'`. * `onEvent` (optional): Callback that receives the typed `BridgeEvent` union as the operation progresses. Use it to render progress to the user. * `fillTimeoutMinutes` (optional): How long to wait for the bridge to be filled before timing out. Defaults to `2`. > **Note** > > The `onEvent` callback receives a typed discriminated union (`status` / `plan_preview` / `plan_confirmed` / `plan_progress`). See the [Bridge Events](/docs/nexus/nexus-sdk/reference/bridge-methods/bridge-events) page for the full event shapes. ## Example ```typescript title="TypeScript" import type { BridgeParams, BridgeResult } from '@avail-project/nexus-core'; const bridgeResult: BridgeResult = await client.bridge( { toTokenSymbol: 'USDC', toAmountRaw: 83_500_000n, // 83.5 USDC (6 decimals) toChainId: 137, // Polygon } satisfies BridgeParams, { onEvent: (event) => { switch (event.type) { case 'plan_preview': // Ordered list of steps about to run — render them if you wish console.log('Steps:', event.plan.steps); break; case 'plan_progress': // Granular per-step progress if (event.stepType === 'request_submission' && event.state === 'completed') { console.log('Explorer:', event.explorerUrl); } break; } }, }, ); console.log('Bridge result:', bridgeResult); // Simulate bridge to preview costs. // You can use the convertTokenReadableAmountToBigInt helper to build the raw amount. const toAmountRaw = client.convertTokenReadableAmountToBigInt('83.5', 'USDC', 137); const bridgeSimulation = await client.simulateBridge({ toTokenSymbol: 'USDC', toAmountRaw, toChainId: 137, }); console.log('Bridge simulation:', bridgeSimulation); ``` > **Warning** > > `bridge()` and `simulateBridge()` throw typed [`NexusError`](https://github.com/availproject/nexus-sdk/blob/main/src/domain/errors.ts) subclasses on failure (e.g. `UserActionError` on a wallet rejection, `ValidationError` on bad input). Branch on `error.category` / `error.code`. ## Return Value ### `bridge()` The return value is a `BridgeResult` object. ```typescript title="TypeScript" /** * Result structure for bridge transactions. */ export type BridgeResult = { intentExplorerUrl: string; sourceTxs: SourceTxs; // array of per-source deposit transactions intent: BridgeIntent; }; export type TxResult = { txHash: Hex; txExplorerUrl: string; receipt?: TransactionReceipt; }; export type SourceTxs = (TxResult & { chain: { id: number; name: string; logo: string }; })[]; ``` ### `simulateBridge()` The return value is a `BridgeSimulationResult` object. ```typescript title="TypeScript" export interface BridgeSimulationResult { intent: BridgeIntent; token: TokenInfo; } export type BridgeIntent = { provider: 'nexus' | 'mayan'; availableSources: { amount: string; amountRaw: bigint; chain: { id: number; name: string; logo: string }; token: { decimals: number; symbol: string; logo: string; contractAddress: Hex }; value: string; }[]; destination: { amount: string; amountRaw: bigint; chain: { id: number; name: string; logo: string }; token: { decimals: number; symbol: string; logo: string; contractAddress: Hex }; value: string; nativeAmount: string; nativeAmountRaw: bigint; nativeAmountValue: string; nativeAmountInToken: string; nativeToken: { decimals: number; symbol: string; logo: string; contractAddress: Hex }; }; fees: { caGas: string; protocol: string; solver: string; total: string; totalValue: string; }; selectedSources: { amount: string; amountRaw: bigint; chain: { id: number; name: string; logo: string }; token: { decimals: number; symbol: string; logo: string; contractAddress: Hex }; value: string; }[]; sourcesTotal: string; sourcesTotalValue: string; }; type TokenInfo = { contractAddress: `0x${string}`; decimals: number; logo: string; name: string; symbol: string; }; ``` > **Note** > > `BridgeIntent` replaces the [v1](https://github.com/availproject/nexus-sdk/blob/main/MIGRATION.md) `ReadableIntent` type: `sources` → `selectedSources`, `allSources` → `availableSources`, and chain fields are nested under `chain: { id, name, logo }`. See the [full type definitions](https://github.com/availproject/nexus-sdk/blob/main/src/domain/types/index.ts) on GitHub. ## Bridge and Execute > **Note** > > **SET UP THE SDK BEFORE YOU START:** > > Install and initialize the client first — see [Installation](/docs/nexus/nexus-sdk/get-started/installation) and [SDK Setup](/docs/nexus/nexus-sdk/get-started/nexus-sdk-setup). Use the `bridgeAndExecute()` function to bridge a token and execute a contract function on the recipient chain in a single flow. Use the `simulateBridgeAndExecute()` function to simulate `bridgeAndExecute()` before sending an actual transaction. > **Note** > > **Note:** Check out the [API reference](/docs/nexus/supported-chains-and-tokens) for a full list of supported tokens and chains. ## Method signature ```typescript title="TypeScript" bridgeAndExecute( params: BridgeAndExecuteParams, options?: BridgeAndExecuteOptions, ): Promise simulateBridgeAndExecute( params: BridgeAndExecuteParams, ): Promise ``` ## Parameters ```typescript title="TypeScript" export interface BridgeAndExecuteParams { toChainId: number; toTokenSymbol: string; toAmountRaw: bigint; sources?: number[]; execute: Omit; enableTransactionPolling?: boolean; transactionTimeout?: number; waitForReceipt?: boolean; receiptTimeout?: number; requiredConfirmations?: number; recentApprovalTxHash?: string; } export interface ExecuteParams { toChainId: number; to: Hex; value?: bigint; data?: Hex; gas?: bigint; gasPrice?: 'low' | 'medium' | 'high'; enableTransactionPolling?: boolean; transactionTimeout?: number; // Transaction receipt confirmation options waitForReceipt?: boolean; receiptTimeout?: number; requiredConfirmations?: number; tokenApproval?: { toTokenSymbol: string; amount: bigint; spender: Hex; }; } ``` * `BridgeAndExecuteParams`: Parameters for using the `bridgeAndExecute()` function. * `toChainId` (number, required): The chain ID of the destination chain. * `toTokenSymbol` (string, required): The symbol of the token to be bridged in this flow (e.g. `'USDC'`). * `toAmountRaw` (bigint, required): The amount of tokens to be used in this flow, in raw integer units. * `sources` (number\[], optional): The chain IDs of the source chains to be used for the bridge. Useful if you want to maintain your holdings on some chains. * `execute` (`Omit`, required): The contract call on the destination chain. `toChainId` is inherited from the top-level `toChainId`. * `enableTransactionPolling` (boolean, optional): Whether to enable transaction polling. Defaults to `false`. * `transactionTimeout` (number, optional) * `waitForReceipt` (boolean, optional) * `receiptTimeout` (number, optional) * `requiredConfirmations` (number, optional) * `recentApprovalTxHash` (string, optional) * `options`: `BridgeAndExecuteOptions` (optional): Top-level intent hook, a `beforeExecute` hook, event listener, and fill timeout. ```typescript title="TypeScript" export type BridgeAndExecuteOptions = { onIntent?: (data: BridgeAndExecuteOnIntentHookData) => void; beforeExecute?: () => Promise<{ value?: bigint; data?: Hex; gas?: bigint }>; onEvent?: (event: BridgeAndExecuteEvent) => void; fillTimeoutMinutes?: number; }; ``` * `onIntent` (optional): Called at the **top level** (not under `hooks`) with the composite intent before execution. Call `data.allow()` / `data.deny()`, or `data.refresh(sources?)` to re-quote. Read `data.intent.bridgeRequired` to detect whether a bridge is needed. When omitted, the intent is auto-approved. * `beforeExecute` (optional): Called just before the execute transaction is sent; return overrides for `value` / `data` / `gas` (for example, to inject freshly quoted calldata). * `onEvent` (optional): Callback that receives the typed `BridgeAndExecuteEvent` union as the operation progresses. * `fillTimeoutMinutes` (optional): How long to wait for the bridge to be filled before timing out. Defaults to `2`. > **Note** > > The `onEvent` callback receives a typed discriminated union. In addition to the bridge step types, `bridgeAndExecute` emits `execute_approval` and `execute_transaction` progress steps. See the [Bridge Events](/docs/nexus/nexus-sdk/reference/bridge-methods/bridge-events) page for the full event shapes. ## Example ```typescript title="TypeScript" import type { BridgeAndExecuteParams, BridgeAndExecuteResult, } from '@avail-project/nexus-core'; // Bridge and execute const bridgeAndExecuteResult: BridgeAndExecuteResult = await client.bridgeAndExecute( { toTokenSymbol: 'USDC', toAmountRaw: 100_000_000n, // 100 USDC (6 decimals) toChainId: 1, sources: [8453], execute: { to: '0x...', data: '0x...', tokenApproval: { toTokenSymbol: 'USDC', amount: 100_000_000n, // 100 USDC (6 decimals) spender: '0x...', }, }, }, { onIntent: (data) => data.allow(), onEvent: (event) => { if (event.type === 'plan_preview') console.log('Bridge+Execute steps:', event.plan.steps); if (event.type === 'plan_progress') console.log('Step progress:', event.stepType, event.state); }, }, ); console.log('Bridge and execute result:', bridgeAndExecuteResult); const bridgeAndExecuteSimulation = await client.simulateBridgeAndExecute({ toTokenSymbol: 'USDC', toAmountRaw: 100_000_000n, // 100 USDC (6 decimals) toChainId: 1, sources: [8453], execute: { to: '0x...', data: '0x...', // tokenApproval optional for simulation }, }); console.log('Bridge and execute simulation:', bridgeAndExecuteSimulation); ``` > **Warning** > > `bridgeAndExecute()` throws typed [`NexusError`](https://github.com/availproject/nexus-sdk/blob/main/src/domain/errors.ts) subclasses on failure. Branch on `error.category` / `error.code`. ## Return Value The return value is a `BridgeAndExecuteResult` object — a discriminated union on `bridgeSkipped`. When the destination already holds enough funds, the bridge is skipped and `bridgeResult` is absent. ```typescript title="TypeScript" export type TxResult = { txHash: Hex; txExplorerUrl: string; receipt?: TransactionReceipt; }; export type BridgeAndExecuteResult = { approval?: TxResult; // present when an ERC-20 approval was needed for execute execute: TxResult; } & ( | { bridgeSkipped: false; bridgeResult: BridgeResult } | { bridgeSkipped: true; bridgeResult?: undefined } ); export type BridgeAndExecuteSimulationResult = { bridgeSimulation: BridgeSimulationResult | null; // null if bridging is skipped executeSimulation: ExecuteSimulation; }; export type ExecuteFeeParams = | { type: 'eip1559'; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint } | { type: 'legacy'; gasPrice: bigint }; export type ExecuteSimulation = { feeParams: ExecuteFeeParams; estimatedGasUnits: bigint; // combined across approval (if any) and execute estimatedTotalCost: bigint; // combined estimated cost }; ``` > **Note** > > Detect a skipped bridge via `result.bridgeSkipped` (there is no `*_SKIPPED` event). See the [full type definitions](https://github.com/availproject/nexus-sdk/blob/main/src/domain/types/index.ts) on GitHub. ## Bridge Events This page documents the events emitted through the `onEvent` callback by the following methods: * `bridge()` * `bridgeAndTransfer()` * `bridgeAndExecute()` In v2 the flat `{ name, args }` event union is replaced by a typed discriminated union keyed on `type`. Every event is one of four kinds: | `type` | Meaning | | ---------------- | ---------------------------------------------------------------------------- | | `status` | High-level lifecycle status of the operation. | | `plan_preview` | The ordered list of steps about to run, emitted once the intent is resolved. | | `plan_confirmed` | The same plan after the user approves it. | | `plan_progress` | Granular per-step progress, keyed on `stepType` + `state`. | > **Note** > > * `bridgeAndTransfer()` and `bridgeAndExecute()` emit the bridge steps plus the steps for the destination-chain operation. > * `bridge()` emits only the bridge steps. > * When `bridgeAndTransfer()` or `bridgeAndExecute()` do not need to bridge (the destination already holds enough funds), only the destination-side steps are emitted. Detect a skipped bridge via `result.bridgeSkipped`, not via an event. ## Event union ```typescript title="TypeScript" export type BridgeEvent = | BridgeStatusEvent | BridgePlanPreviewEvent | BridgePlanConfirmedEvent | BridgePlanProgressEvent; export type OnEventParam = { onEvent?: (event: TEvent) => void; }; ``` ## Status events ```typescript title="TypeScript" export type BridgeStatus = | 'intent_building' | 'intent_ready' | 'awaiting_approval' | 'awaiting_allowance_selection' | 'approved' | 'executing' | 'completed'; export type BridgeStatusEvent = { type: 'status'; status: BridgeStatus; }; ``` > **Note** > > `bridgeAndExecute()` (and `bridgeAndTransfer()`) additionally emit a `'preparing'` status at the start — its status type is `BridgeAndExecuteStatus`, which is `BridgeStatus` plus `'preparing'`. ## Plan preview / confirmed events Both carry the `BridgePlan` — an ordered list of the steps that will run. ```typescript title="TypeScript" export type BridgePlanPreviewEvent = { type: 'plan_preview'; plan: BridgePlan }; export type BridgePlanConfirmedEvent = { type: 'plan_confirmed'; plan: BridgePlan }; export type BridgePlan = { steps: BridgePlanStep[]; }; export type BridgePlanStep = | BridgeAllowanceApprovalStep | BridgeRequestSigningStep | BridgeRequestSubmissionStep | BridgeVaultDepositStep | BridgeFillStep; ``` For `bridgeAndExecute()`, the plan is a `BridgeAndExecutePlan`, which adds a `bridgeRequired` flag and the execute step types: ```typescript title="TypeScript" export type BridgeAndExecutePlan = { bridgeRequired: boolean; steps: BridgeAndExecutePlanStep[]; // BridgePlanStep | ExecutePlanStep }; export type ExecutePlanStep = ExecuteApprovalStep | ExecuteTransactionStep; ``` ## Progress events Each `plan_progress` event carries a `stepType` discriminant and a `state`. The tables below list the states per step type, and the extra fields each progress event carries. ### Bridge step types | `stepType` | `state` values | Extra fields | | -------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | `allowance_approval` | `wallet_prompted`, `submitted`, `confirmed`, `failed` | `step`, `approvedAmount`, `approvedAmountRaw`; `txHash` + `explorerUrl` on `submitted`/`confirmed`; `error` on `failed` | | `request_signing` | `wallet_prompted`, `completed`, `failed` | `step`; `intentRequestHash` on `completed`; `error` on `failed` | | `request_submission` | `started`, `completed`, `failed` | `step`, `intentRequestHash`; `explorerUrl` on `completed`; `error` on `failed` | | `vault_deposit` | `started`, `wallet_prompted`, `submitted`, `confirmed`, `completed`, `failed` | `step`; `txHash` + `explorerUrl` on `submitted`/`confirmed` (optional on `failed`); `error` on `failed` | | `bridge_fill` | `waiting`, `completed`, `failed` | `step`, `intentRequestHash`; `error` on `failed` | ### Execute step types Emitted by `bridgeAndExecute()` (and `bridgeAndTransfer()`) for the destination-chain call. | `stepType` | `state` values | Extra fields | | --------------------- | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | `execute_approval` | `wallet_prompted`, `submitted`, `confirmed`, `failed` | `step`; `txHash` + `explorerUrl` on `submitted`/`confirmed` (optional on `failed`); `error` on `failed` | | `execute_transaction` | `wallet_prompted`, `submitted`, `confirmed`, `failed` | `step`, `value`, `hasData`; `txHash` + `explorerUrl` on `submitted`/`confirmed` (optional on `failed`); `error` on `failed` | ### Progress event shapes ```typescript title="TypeScript" export type BridgePlanProgressEvent = | BridgeAllowanceApprovalProgressEvent | BridgeRequestSigningProgressEvent | BridgeRequestSubmissionProgressEvent | BridgeVaultDepositProgressEvent | BridgeFillProgressEvent; // Example: allowance_approval progress export type BridgeAllowanceApprovalProgressEvent = | { type: 'plan_progress'; stepType: 'allowance_approval'; state: 'wallet_prompted'; step: BridgeAllowanceApprovalStep; approvedAmount: string; approvedAmountRaw: string; } | { type: 'plan_progress'; stepType: 'allowance_approval'; state: 'submitted' | 'confirmed'; step: BridgeAllowanceApprovalStep; approvedAmount: string; approvedAmountRaw: string; txHash: Hex; explorerUrl: string; } | { type: 'plan_progress'; stepType: 'allowance_approval'; state: 'failed'; step: BridgeAllowanceApprovalStep; approvedAmount: string; approvedAmountRaw: string; error: string; }; ``` > **Note** > > You can find the full type definitions for every step and progress event on GitHub: } title="Bridge event types" href="https://github.com/availproject/nexus-sdk/blob/main/src/domain/types/bridge-events.ts" target="_blank" rel="noopener noreferrer" arrow /> ## Usage Example ```typescript title="TypeScript" client.bridge(params, { onEvent: (event) => { switch (event.type) { case 'status': // event.status — 'intent_building' | 'intent_ready' | ... | 'completed' break; case 'plan_preview': // event.plan.steps — ordered list of steps about to run break; case 'plan_confirmed': // event.plan.steps — after approval break; case 'plan_progress': // event.stepType + event.state — granular progress if (event.stepType === 'bridge_fill' && event.state === 'completed') { // bridge filled on the destination chain } break; } }, }); ``` ## Calculate Max for Bridge > **Note** > > **SET UP THE SDK BEFORE YOU START:** > > Install and initialize the client first — see [Installation](/docs/nexus/nexus-sdk/get-started/installation) and [SDK Setup](/docs/nexus/nexus-sdk/get-started/nexus-sdk-setup). Use the `calculateMaxForBridge()` function to compute the maximum amount that can be bridged to a destination token across all same-currency holdings on other chains. This is useful for populating a "Max" button before calling [`bridge()`](/docs/nexus/nexus-sdk/reference/bridge-methods/bridge). The max is sized against the provider the bridge will actually use: the summed bridge amount is checked against the Mayan threshold (the same decision the real bridge makes), and the receivable max is computed for that provider. A small safety haircut is applied so the suggested amount survives fee drift before execution. The returned `provider` tells you which path was used. > **Note** > > **Note:** Check out the [API reference](/docs/nexus/supported-chains-and-tokens) for a full list of supported tokens and chains. ## Method signature ```typescript title="TypeScript" calculateMaxForBridge( input: BridgeMaxParams, ): Promise ``` ## Parameters ```typescript title="TypeScript" export type BridgeMaxParams = { toChainId: number; toTokenSymbol: string; /** Restrict which source chains are considered (chain IDs). Empty/omitted = all. */ sources?: number[]; }; ``` * `BridgeMaxParams`: Parameters for computing the max bridgeable amount. * `toChainId` (number, required): The chain ID of the destination chain. * `toTokenSymbol` (string, required): The symbol of the destination token (e.g. `'USDC'`). * `sources` (number\[], optional): Restrict which source chain IDs are considered. When omitted, all same-currency holdings are used. ## Example ```typescript title="TypeScript" import type { BridgeMaxParams, BridgeMaxResult } from '@avail-project/nexus-core'; const max: BridgeMaxResult = await client.calculateMaxForBridge({ toChainId: 8453, toTokenSymbol: 'USDC', } satisfies BridgeMaxParams); console.log(`Max bridgeable: ${max.maxAmount} ${max.symbol} via ${max.provider}`); console.log('Sources used:', max.sources); // Feed the raw amount straight into a bridge: await client.bridge({ toChainId: 8453, toTokenSymbol: 'USDC', toAmountRaw: max.maxAmountRaw, }); ``` You can also restrict which source chains are considered: ```typescript title="TypeScript" const max = await client.calculateMaxForBridge({ toChainId: 8453, toTokenSymbol: 'USDC', sources: [10, 42161], // only Optimism + Arbitrum balances }); ``` > **Warning** > > `calculateMaxForBridge()` throws typed [`NexusError`](https://github.com/availproject/nexus-sdk/blob/main/src/domain/errors.ts) subclasses on failure. Branch on `error.category` / `error.code`. ## Return Value The return value is a `BridgeMaxResult` object. ```typescript title="TypeScript" export type BridgeMaxResult = { toChainId: number; toTokenSymbol: string; /** Provider the max was sized against ('mayan' once the amount clears the threshold). */ provider: 'nexus' | 'mayan'; maxAmount: string; // Human-readable decimal string maxAmountRaw: bigint; // Raw integer units — suitable for toAmountRaw in bridge() symbol: string; decimals: number; sources: { chainId: number; tokenAddress: Hex; symbol: string; decimals: number; amount: string; // Human-readable portion drawn from this source }[]; }; ``` > **Note** > > See the [full type definitions](https://github.com/availproject/nexus-sdk/blob/main/src/bridge/types.ts) on GitHub. ## Fetch Bridge Balances > **Note** > > **SET UP THE SDK BEFORE YOU START:** > > Install and initialize the client first — see [Installation](/docs/nexus/nexus-sdk/get-started/installation) and [SDK Setup](/docs/nexus/nexus-sdk/get-started/nexus-sdk-setup). Use the `getBalancesForBridge()` function to fetch token balances that can be used in bridge operations across all supported chains in one call. > **Note** > > **Note:** Check out the [API reference](/docs/nexus/supported-chains-and-tokens) for a full list of supported tokens and chains. ## Method signature ```typescript title="TypeScript" getBalancesForBridge(): Promise ``` > **Note** > > If you need the tokens that can be used for swaps instead, use `getBalancesForSwap()`, which returns the same `TokenBalance[]` shape. ## Parameters * None ## Example Here is a minimal example of how to use the `getBalancesForBridge()` function to fetch the balances accessible by the bridge operations of a user: ```typescript title="TypeScript" // Get all balances accessible by the bridge operations of a user const bridgeBalances = await client.getBalancesForBridge(); console.log('Bridge balances:', bridgeBalances); ``` ## Return Value `getBalancesForBridge()` returns `Promise` — an array of `TokenBalance` objects. Each `TokenBalance` aggregates a token's holdings across every chain it lives on, with the per-chain split in `chainBalances`. ```typescript title="TypeScript" export type TokenBalance = { balance: string; // Aggregate balance across all chains (human-readable) value: string; // Aggregate fiat value (string for precision) chainBalances: ChainBalance[];// Per-chain breakdown currencyId?: number; // Bridge currency identifier (present on bridge balances) decimals: number; logo: string; name: string; // Display label (e.g. "USDC/USDM") symbol: string; // Majority symbol by chain count }; export type ChainBalance = { balance: string; // Balance on this chain (human-readable) value: string; // Fiat value on this chain (string for precision) symbol: string; chain: { id: number; logo: string; name: string; }; contractAddress: `0x${string}`; decimals: number; universe: Universe; // VM family as a numeric enum (0 = Ethereum/EVM) }; ``` > **Note** > > Field renames from [v1](https://github.com/availproject/nexus-sdk/blob/main/MIGRATION.md): `balanceInFiat` → `value`, `icon` → `logo`, and `breakdown` → `chainBalances`. See the [full type definitions](https://github.com/availproject/nexus-sdk/blob/main/src/domain/types/index.ts) on GitHub. ## Bridge and Transfer > **Note** > > **SET UP THE SDK BEFORE YOU START:** > > Install and initialize the client first — see [Installation](/docs/nexus/nexus-sdk/get-started/installation) and [SDK Setup](/docs/nexus/nexus-sdk/get-started/nexus-sdk-setup). Use the `bridgeAndTransfer()` function to source a token from multiple source chains to a single destination chain. Use the `simulateBridgeAndTransfer()` function to simulate the bridge and transfer transaction to preview the costs and fees, before actually executing the transaction. > **Note** > > **Note:** Check out the [API reference](/docs/nexus/supported-chains-and-tokens) for a full list of supported tokens and chains. ## Method signature ```typescript title="TypeScript" bridgeAndTransfer( params: TransferParams, options?: BridgeOperationOptions, ): Promise simulateBridgeAndTransfer( params: TransferParams, ): Promise ``` ## Parameters ```typescript title="TypeScript" /** * Parameters for bridging and transferring tokens. */ export interface TransferParams { toTokenSymbol: string; toAmountRaw: bigint; toChainId: number; recipient: `0x${string}`; sources?: number[]; } ``` * `TransferParams`: Parameters for transferring tokens. * `toTokenSymbol` (string, required): The symbol of the token to be bridged and transferred (e.g. `'USDC'`). * `toAmountRaw` (bigint, required): The amount of tokens to transfer to the recipient, in raw integer units. * `toChainId` (number, required): The chain ID of the destination chain. * `recipient` (`0x${string}`, required): The recipient address. * `sources` (number\[], optional): The chain IDs of the source chains to be used for the transfer. Useful if you want to maintain your holdings on some chains. * `options`: `BridgeOperationOptions` (optional): Per-operation hooks, event listener, and fill timeout — the same options object accepted by [`bridge()`](/docs/nexus/nexus-sdk/reference/bridge-methods/bridge). ```typescript title="TypeScript" export type BridgeOperationOptions = { hooks?: { onIntent?: (data: OnIntentHookData) => void; onAllowance?: (data: OnAllowanceHookData) => void; }; onEvent?: (event: BridgeEvent) => void; fillTimeoutMinutes?: number; }; ``` > **Note** > > The `onEvent` callback receives a typed discriminated union (`status` / `plan_preview` / `plan_confirmed` / `plan_progress`). See the [Bridge Events](/docs/nexus/nexus-sdk/reference/bridge-methods/bridge-events) page for the full event shapes. ## Example ```typescript title="TypeScript" import type { TransferParams, TransferResult } from '@avail-project/nexus-core'; // Smart transfer with automatic optimization const bridgeAndTransferResult: TransferResult = await client.bridgeAndTransfer( { toTokenSymbol: 'USDC', toAmountRaw: 1_530_000n, // 1.53 USDC (6 decimals) toChainId: 1, // Ethereum recipient: '0x742d35Cc6634C0532925a3b8D4C9db96c4b4Db45', }, { onEvent: (event) => { if (event.type === 'plan_preview') console.log('Transfer steps:', event.plan.steps); if (event.type === 'plan_progress') console.log('Step progress:', event.stepType, event.state); }, }, ); console.log('Bridge and transfer result:', bridgeAndTransferResult); const bridgeAndTransferSimulation = await client.simulateBridgeAndTransfer({ toTokenSymbol: 'USDC', toAmountRaw: 1_530_000n, // 1.53 USDC (6 decimals) toChainId: 1, recipient: '0x742d35Cc6634C0532925a3b8D4C9db96c4b4Db45', }); console.log('Bridge and transfer simulation:', bridgeAndTransferSimulation); ``` > **Warning** > > `bridgeAndTransfer()` throws typed [`NexusError`](https://github.com/availproject/nexus-sdk/blob/main/src/domain/errors.ts) subclasses on failure. Branch on `error.category` / `error.code`. ## Return Value ### `bridgeAndTransfer()` The return value is a `TransferResult` object, which is an alias for [`BridgeAndExecuteResult`](/docs/nexus/nexus-sdk/reference/bridge-methods/bridge-and-execute). ```typescript title="TypeScript" /** * Result structure for transfer transactions. */ export type TransferResult = BridgeAndExecuteResult; export type BridgeAndExecuteResult = { approval?: TxResult; execute: TxResult; } & ( | { bridgeSkipped: false; bridgeResult: BridgeResult } | { bridgeSkipped: true; bridgeResult?: undefined } ); ``` ### `simulateBridgeAndTransfer()` The return value is a `BridgeAndExecuteSimulationResult` object. ```typescript title="TypeScript" export type BridgeAndExecuteSimulationResult = { bridgeSimulation: BridgeSimulationResult | null; // null when bridging is skipped executeSimulation: ExecuteSimulation; }; ```