Avail Nexus SDK API Reference
The Avail Nexus SDK provides a comprehensive set of APIs for cross-chain bridging, token transfers, and balance management across multiple EVM chains.
Note:
This page will focus on the UI widgets available as part of the Avail Nexus SDK.
If you are looking for the API reference for the headless SDK, please refer to this page.
Quick Start
Wrap your app with NexusProvider
import { NexusProvider } from '@avail-project/nexus-widgets';
export default function App() {
return (
<NexusProvider
config={{
debug: false, // true to view debug logs
network: 'testnet', // "mainnet" (default) or "testnet"
}}
>
<YourApp />
</NexusProvider>
);
}
Forward the user’s wallet provider
import { useEffect } from 'react';
import { useAccount } from '@wagmi/react'; // any wallet lib works
import { useNexus } from '@avail-project/nexus-widgets';
export function WalletBridge() {
const { connector, isConnected } = useAccount();
const { setProvider } = useNexus();
useEffect(() => {
if (isConnected && connector?.getProvider) {
connector.getProvider().then(setProvider);
}
}, [isConnected, connector, setProvider]);
return null;
}
Alternative: Manual SDK Initialization
For developers who need to use SDK methods directly (like getUnifiedBalances
) before using UI components:
import { useNexus } from '@avail-project/nexus-widgets';
function MyComponent() {
const { initializeSdk, sdk, isSdkInitialized } = useNexus();
const handleInitialize = async () => {
const provider = await window.ethereum; // or get from your wallet library
await initializeSdk(provider); // Initializes both SDK and UI state
// Now you can use SDK methods directly
const balances = await sdk.getUnifiedBalances();
console.log('Balances:', balances);
// UI components will already be initialized when used
};
return (
<button onClick={handleInitialize} disabled={isSdkInitialized}>
{isSdkInitialized ? 'SDK Ready' : 'Initialize SDK'}
</button>
);
}
Benefits of manual initialization:
- Use SDK methods immediately after initialization
- No duplicate initialization when UI components are used
- Full control over initialization timing
- Access to unified balances and other SDK features before transactions
Configuration Options
Supported Chains
Mainnet
Network | Chain ID | Native Currency | Status |
---|---|---|---|
Ethereum | 1 | ETH | ✅ |
Optimism | 10 | ETH | ✅ |
Polygon | 137 | MATIC | ✅ |
Arbitrum | 42161 | ETH | ✅ |
Avalanche | 43114 | AVAX | ✅ |
Base | 8453 | ETH | ✅ |
Scroll | 534352 | ETH | ✅ |
Sophon | 50104 | SOPH | ✅ |
Kaia | 8217 | KAIA | ✅ |
BNB | 56 | BNB | ✅ |
HyperEVM | 999 | HYPE | ✅ |
Supported Tokens
Token | Name | Decimals | Networks |
---|---|---|---|
ETH | Ethereum | 18 | All EVM chains |
USDC | USD Coin | 6 | All supported |
USDT | Tether USD | 6 | All supported |
Use Nexus Widgets
import {
BridgeButton,
TransferButton,
BridgeAndExecuteButton,
SwapButton,
TOKEN_CONTRACT_ADDRESSES,
TOKEN_METADATA,
SUPPORTED_CHAINS,
DESTINATION_SWAP_TOKENS,
type SUPPORTED_TOKENS,
type SUPPORTED_CHAIN_IDS
} from '@avail-project/nexus-widgets';
import { parseUnits } from 'viem';
/* Bridge ----------------------------------------------------------- */
<BridgeButton prefill={{ chainId: 137, token: 'USDC', amount: '100' }}>
{({ onClick, isLoading }) => (
<button onClick={onClick} disabled={isLoading}>
{isLoading ? 'Bridging…' : 'Bridge 100 USDC → Polygon'}
</button>
)}
</BridgeButton>
/* Transfer --------------------------------------------------------- */
<TransferButton>
{({ onClick }) => <YourStyledBtn onClick={onClick}>Send Funds</YourStyledBtn>}
</TransferButton>
/* Bridge + Execute ------------------------------------------------- */
<BridgeAndExecuteButton
contractAddress={'0x794a61358D6845594F94dc1DB02A252b5b4814aD'}
contractAbi={
[
{
name: 'supply',
type: 'function',
stateMutability: 'nonpayable',
inputs: [
{ name: 'asset', type: 'address' },
{ name: 'amount', type: 'uint256' },
{ name: 'onBehalfOf', type: 'address' },
{ name: 'referralCode', type: 'uint16' },
],
outputs: [],
},
] as const
}
functionName="supply"
buildFunctionParams={(token, amount, _chainId, user) => {
const decimals = TOKEN_METADATA[token].decimals
const amountWei = parseUnits(amount, decimals)
const tokenAddr = TOKEN_CONTRACT_ADDRESSES[token][_chainId]
return { functionParams: [tokenAddr, amountWei, user, 0] }
}}
prefill={{
toChainId: 42161,
token: 'USDT',
}}
>
{({ onClick, isLoading }) => (
<Button
onClick={onClick}
disabled={isLoading}
className="w-full"
>
{isLoading ? 'Processing…' : 'Bridge & Stake'}
</Button>
)}
</BridgeAndExecuteButton>
/* Swap | EXACT_IN ------------------------------------------------------------- */
<SwapButton
prefill={{
fromChainID: 137,
fromTokenAddress: 'USDC',
toChainID: 1,
toTokenAddress: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
fromAmount: '100'
}}
>
{({ onClick, isLoading }) => (
<button onClick={onClick} disabled={isLoading}>
{isLoading ? 'Swapping…' : 'Swap USDC to ETH'}
</button>
)}
</SwapButton>
Component APIs
BridgeButton
Bridge tokens between chains with a customizable button interface.
interface BridgeButtonProps {
title?: string; // Will appear once intialization is completed
prefill?: Partial<BridgeParams>; // chainId, token, amount
className?: string;
children(props: { onClick(): void; isLoading: boolean }): React.ReactNode;
}
Example:
<BridgeButton prefill={{ chainId: 137, token: 'USDC', amount: '100' }}>
{({ onClick, isLoading }) => (
<button onClick={onClick} disabled={isLoading}>
{isLoading ? 'Bridging…' : 'Bridge USDC to Polygon'}
</button>
)}
</BridgeButton>
TransferButton
Send tokens to any address with automatic optimization (direct transfer when possible).
interface TransferButtonProps {
title?: string; // Will appear once intialization is completed
prefill?: Partial<TransferParams>; // chainId, token, amount, recipient
className?: string;
children(props: { onClick(): void; isLoading: boolean }): React.ReactNode;
}
Example:
<TransferButton
prefill={{
chainId: 42161,
token: 'USDC',
amount: '50',
recipient: '0x742d35Cc6634C0532925a3b8D4C9db96c4b4Db45',
}}
>
{({ onClick, isLoading }) => (
<button onClick={onClick} disabled={isLoading}>
{isLoading ? 'Sending…' : 'Send 50 USDC'}
</button>
)}
</TransferButton>
BridgeAndExecuteButton
Bridge tokens and execute a smart contract function in a single flow.
type DynamicParamBuilder = (
token: SUPPORTED_TOKENS,
amount: string,
chainId: SUPPORTED_CHAINS_IDS,
userAddress: `0x${string}`,
) => {
functionParams: readonly unknown[];
value?: string; // wei; defaults to "0"
};
interface BridgeAndExecuteButtonProps {
title?: string; // Will appear once intialization is completed
contractAddress: `0x${string}`; // REQUIRED
contractAbi: Abi; // REQUIRED
functionName: string; // REQUIRED
buildFunctionParams: DynamicParamBuilder; // REQUIRED
prefill?: { toChainId?: number; token?: SUPPORTED_TOKENS; amount?: string };
className?: string;
children(props: { onClick(): void; isLoading: boolean; disabled: boolean }): React.ReactNode;
}
Example - Aave Supply:
<BridgeAndExecuteButton
contractAddress="0x794a61358D6845594F94dc1DB02A252b5b4814aD" // Aave Pool
contractAbi={
[
{
name: 'supply',
type: 'function',
stateMutability: 'nonpayable',
inputs: [
{ name: 'asset', type: 'address' },
{ name: 'amount', type: 'uint256' },
{ name: 'onBehalfOf', type: 'address' },
{ name: 'referralCode', type: 'uint16' },
],
outputs: [],
},
] as const
}
functionName="supply"
buildFunctionParams={(token, amount, chainId, userAddress) => {
const decimals = TOKEN_METADATA[token].decimals;
const amountWei = parseUnits(amount, decimals);
const tokenAddress = TOKEN_CONTRACT_ADDRESSES[token][chainId];
return {
functionParams: [tokenAddress, amountWei, userAddress, 0],
};
}}
prefill={{ toChainId: 1, token: 'USDC' }}
>
{({ onClick, isLoading, disabled }) => (
<button onClick={onClick} disabled={isLoading || disabled}>
{isLoading ? 'Processing…' : 'Bridge & Supply to Aave'}
</button>
)}
</BridgeAndExecuteButton>
buildFunctionParams
receives the validated UX input (token, amount, destination chainId) plus the connected wallet address and must return the encoded functionParams
(and optional ETH value
) used in the destination call.
Nexus then:
- Bridges the asset to
toChainId
- Sets ERC-20 allowance if required
- Executes
contractAddress.functionName(functionParams, { value })
SwapButton
Cross-chain token swapping with support for both EXACT_IN and EXACT_OUT modes.
interface SwapButtonProps {
title?: string; // Will appear once initialization is completed
prefill?: Omit<SwapInputData, 'toAmount'>; // fromChainID, toChainID, fromTokenAddress, toTokenAddress, fromAmount
className?: string;
children(props: { onClick(): void; isLoading: boolean }): React.ReactNode;
}
interface SwapInputData {
fromChainID?: number;
toChainID?: number;
fromTokenAddress?: string;
toTokenAddress?: string;
fromAmount?: string | number;
toAmount?: string | number;
}
EXACT_IN Swap Example:
// Swap exactly 100 USDC from Polygon to LDO on Arbitrum
<SwapButton
prefill={{
fromChainID: 137, // Polygon
fromTokenAddress: 'USDC',
toChainID: 42161, // Arbitrum
toTokenAddress: 'LDO', // LDO
fromAmount: '100', // Exact input amount
}}
>
{({ onClick, isLoading }) => (
<button onClick={onClick} disabled={isLoading}>
{isLoading ? 'Swapping...' : 'Swap 100 USDC → ETH'}
</button>
)}
</SwapButton>
Swap Utilities
Discovering Available Swap Options
import { useNexus, DESTINATION_SWAP_TOKENS } from '@avail-project/nexus-widgets';
function SwapOptionsExample() {
const { sdk, isSdkInitialized } = useNexus();
const [swapOptions, setSwapOptions] = useState(null);
useEffect(() => {
if (isSdkInitialized) {
// Get supported source chains and tokens for swaps
const supportedOptions = sdk.utils.getSwapSupportedChainsAndTokens();
setSwapOptions(supportedOptions);
}
}, [sdk, isSdkInitialized]);
return (
<div>
{/* Source chains and tokens */}
{swapOptions?.map(chain => (
<div key={chain.id}>
<h3>{chain.name} (Chain ID: {chain.id})</h3>
{chain.tokens.map(token => (
<div key={token.tokenAddress}>
{token.symbol}: {token.tokenAddress}
</div>
))}
</div>
))}
{/* Popular destination options */}
<h3>Popular Destinations:</h3>
{Array.from(DESTINATION_SWAP_TOKENS.entries()).map(([chainId, tokens]) => (
<div key={chainId}>
<h4>Chain {chainId}</h4>
{tokens.map(token => (
<div key={token.tokenAddress}>
{token.symbol} - {token.name}
</div>
))}
</div>
))}
</div>
);
}
Key Points:
- Source restrictions: Source chains/tokens are limited to what
getSwapSupportedChainsAndTokens()
returns - Destination flexibility: Destination can be any supported chain and token address
- DESTINATION_SWAP_TOKENS: Provides popular destination options for UI building, but is not exhaustive
Prefill Behavior
Widget | Supported keys | Locked in UI |
---|---|---|
BridgeButton | chainId , token , amount | ✅ |
TransferButton | chainId , token , amount , recipient | ✅ |
BridgeAndExecuteButton | toChainId , token , amount | ✅ |
SwapButton | fromChainID , toChainID , fromTokenAddress , toTokenAddress , fromAmount , toAmount | ✅ |
Values passed in prefill
appear as read-only fields, enforcing your desired flow.
Widget Examples
Cross-Chain Swapping
// Multi-step cross-chain swap with destination selection
<SwapButton>
{({ onClick, isLoading }) => (
<div className="swap-card bg-gradient-to-r from-purple-500 to-blue-600 p-6 rounded-lg text-white">
<h3 className="text-xl font-bold mb-2">Cross-Chain Swap</h3>
<p className="mb-4">Swap tokens across any supported chains</p>
<button
onClick={onClick}
disabled={isLoading}
className="bg-white text-purple-600 px-6 py-2 rounded-full font-semibold hover:bg-gray-100"
>
{isLoading ? (
<div className="flex items-center">
<Spinner className="mr-2" />
Processing Swap...
</div>
) : (
'Start Swap'
)}
</button>
</div>
)}
</SwapButton>
// Fixed-route swap for arbitrage or specific pairs
<SwapButton
prefill={{
fromChainID: 42161, // Arbitrum
fromTokenAddress: 'USDC',
toChainID: 10, // Optimism
toTokenAddress: '0x4200000000000000000000000000000000000006', // WETH
fromAmount: '1000',
}}
>
{({ onClick, isLoading }) => (
<div className="arbitrage-card">
<h3>Arbitrage Opportunity</h3>
<p>Better WETH rates on Optimism</p>
<button onClick={onClick} disabled={isLoading}>
{isLoading ? 'Executing...' : 'Swap 1000 USDC → WETH'}
</button>
</div>
)}
</SwapButton>
DeFi Protocol Integration
// Compound V3 Supply Widget
<BridgeAndExecuteButton
contractAddress="0xc3d688B66703497DAA19211EEdff47f25384cdc3" // Compound V3 USDC
contractAbi={
[
{
inputs: [
{ internalType: 'address', name: 'asset', type: 'address' },
{ internalType: 'uint256', name: 'amount', type: 'uint256' },
],
name: 'supply',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
] as const
}
functionName="supply"
buildFunctionParams={(token, amount, chainId, userAddress) => {
const decimals = TOKEN_METADATA[token].decimals;
const amountWei = parseUnits(amount, decimals);
const tokenAddress = TOKEN_CONTRACT_ADDRESSES[token][chainId];
return {
functionParams: [tokenAddress, amountWei],
};
}}
prefill={{ toChainId: 1, token: 'USDC' }}
>
{({ onClick, isLoading }) => (
<div className="bg-blue-500 hover:bg-blue-600 rounded-lg p-4">
<h3 className="text-white font-bold">Earn with Compound</h3>
<button
onClick={onClick}
disabled={isLoading}
className="mt-2 bg-white text-blue-500 px-4 py-2 rounded"
>
{isLoading ? 'Processing...' : 'Supply & Earn'}
</button>
</div>
)}
</BridgeAndExecuteButton>
Simple Payment Flow
// Payment button for e-commerce
<TransferButton
prefill={{
token: 'USDC',
amount: '25.00',
recipient: merchantAddress,
chainId: 137, // Polygon for low fees
}}
>
{({ onClick, isLoading }) => (
<button className="checkout-btn" onClick={onClick} disabled={isLoading}>
{isLoading ? 'Processing Payment...' : 'Pay $25 USDC'}
</button>
)}
</TransferButton>
Multi-Chain Liquidity
// Bridge to specific chain for better rates
<BridgeButton prefill={{ chainId: 42161, token: 'ETH', amount: '0.1' }}>
{({ onClick, isLoading }) => (
<div className="liquidity-card">
<h3>Better rates on Arbitrum</h3>
<p>Save 60% on gas fees</p>
<button onClick={onClick} disabled={isLoading}>
{isLoading ? 'Bridging...' : 'Bridge to Arbitrum'}
</button>
</div>
)}
</BridgeButton>
Advanced Usage
Custom Loading States
<BridgeButton prefill={{ chainId: 137, token: 'USDC', amount: '100' }}>
{({ onClick, isLoading }) => (
<button
onClick={onClick}
disabled={isLoading}
className={`btn ${isLoading ? 'btn-loading' : 'btn-primary'}`}
>
{isLoading ? (
<div className="flex items-center">
<Spinner className="mr-2" />
Bridging USDC...
</div>
) : (
'Bridge 100 USDC → Polygon'
)}
</button>
)}
</BridgeButton>
Error Handling
function MyBridgeComponent() {
const [error, setError] = useState(null);
return (
<div>
{error && <div className="error-banner">{error}</div>}
<BridgeButton prefill={{ chainId: 137, token: 'USDC', amount: '100' }}>
{({ onClick, isLoading }) => (
<button
onClick={async () => {
try {
setError(null);
await onClick();
} catch (err) {
setError(err.message);
}
}}
disabled={isLoading}
>
{isLoading ? 'Processing...' : 'Bridge USDC'}
</button>
)}
</BridgeButton>
</div>
);
}
Access to SDK Methods
function BalanceAwareWidget() {
const { sdk, isSdkInitialized } = useNexus();
const [balances, setBalances] = useState([]);
useEffect(() => {
if (isSdkInitialized) {
sdk.getUnifiedBalances().then(setBalances);
}
}, [sdk, isSdkInitialized]);
return (
<div>
<div className="balances">
{balances.map((balance) => (
<div key={balance.symbol}>
{balance.symbol}: {balance.balance}
</div>
))}
</div>
<BridgeButton>
{({ onClick, isLoading }) => (
<button onClick={onClick} disabled={isLoading}>
Bridge Assets
</button>
)}
</BridgeButton>
</div>
);
}
Best Practices
1. Always simulate first
// Good: Let the widget handle simulation internally
<BridgeButton>
{({ onClick, isLoading }) => (
<button onClick={onClick} disabled={isLoading}>
Bridge
</button>
)}
</BridgeButton>
2. Handle loading states gracefully
// Good: Provide clear feedback
<TransferButton>
{({ onClick, isLoading }) => (
<button onClick={onClick} disabled={isLoading}>
{isLoading ? (
<div className="flex items-center">
<Spinner className="mr-2" />
Transferring...
</div>
) : (
'Send Tokens'
)}
</button>
)}
</TransferButton>
3. Use appropriate confirmation levels
// Good: For high-value transactions, the widget will automatically
// request higher confirmation levels
<BridgeAndExecuteButton prefill={{ amount: '10000' }}>
{/* Widget handles confirmation requirements */}
</BridgeAndExecuteButton>
4. Clean up resources
function MyComponent() {
const { deinitializeSdk } = useNexus();
useEffect(() => {
return () => {
deinitializeSdk();
};
}, []);
return <BridgeButton>{/* ... */}</BridgeButton>;
}