Deposit (exact-input-mode)
Route funds into a configured protocol or app action with Nexus intents using exact input mode.
How exact-input mode works
Note
Looking for exact-output mode instead? See Deposit (exact-output-mode) to enter the exact amount you want to deposit.
Installation
pnpm add @avail-project/widgets viem wagmi @tanstack/react-query
Usage
NexusWidget must be rendered inside NexusProvider (mandatory — it initializes the SDK), and the provider should receive your identity.clientId. See Get Started for provider setup and Client ID for details.
In exact-input mode, set mode: "deposit" and route: "exactInput". The user selects one or more tokens to spend and specifies input amounts. The widget quotes the destination deposit amount, handles quoting and routing, and dynamically provides the execution amount to executeDeposit.
import { NexusProvider, NexusWidget } from "@avail-project/widgets";
import { encodeFunctionData } from "viem";
const APP_DEPOSIT_CONTRACT = "0x...";
const DESTINATION_TOKEN = "0x...";
const DESTINATION_CHAIN_ID = 42161;
const APP_DEPOSIT_ABI = [
{
inputs: [
{ internalType: "address", name: "token", type: "address" },
{ internalType: "uint256", name: "amount", type: "uint256" },
{ internalType: "address", name: "user", type: "address" },
],
name: "deposit",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
] as const;
const depositConfig = {
mode: "deposit",
route: "exactInput", // Enables exact input deposit mode
destination: {
chain: DESTINATION_CHAIN_ID,
tokens: [
{
address: DESTINATION_TOKEN,
symbol: "USDT",
decimals: 6,
logo: "https://example.com/usdt.svg",
},
],
},
depositAddress: APP_DEPOSIT_CONTRACT,
executeDeposit: (_symbol, tokenAddress, amount, _chainId, user) => ({
to: APP_DEPOSIT_CONTRACT,
gas: 400_000n,
data: encodeFunctionData({
abi: APP_DEPOSIT_ABI,
functionName: "deposit",
args: [tokenAddress, amount, user],
}),
tokenApproval: {
toTokenAddress: tokenAddress,
amount,
spender: APP_DEPOSIT_CONTRACT,
},
}),
validation: {
minAmount: "1",
maxAmount: "1000",
},
appearance: {
appName: "Your App",
appLogoURL: "https://example.com/logo.svg",
heading: "Deposit",
mode: "system",
primaryColor: "#0A6BEB",
},
} as const;
<NexusProvider config={{ identity: { clientId: "YOUR_NEXUS_CLIENT_ID" } }}>
<NexusWidget config={depositConfig} connectedAddress={address} />
</NexusProvider>;Configuration
swapAndExecute.
| Prop | Required | Description |
|---|---|---|
config.mode | Yes | Must be "deposit". |
config.route | Yes | Set to "exactInput" for exact input mode. |
config.destination.chain | Yes | Chain ID of the fixed destination network. Deposit destination chain cannot be changed by the user. |
config.destination.tokens | Yes | One or more accepted destination tokens. The token dropdown is limited to this list and is editable only when multiple tokens are supplied. |
config.depositAddress | Yes | Smart contract address for the deposit target. |
config.executeDeposit | Yes | Callback builder returning { to, gas, data?, value?, tokenApproval? } for the final app transaction. gas must be a positive bigint. |
config.enableOnRamp | Optional | Defaults to false. Set true to display "Deposit with Cash" alongside wallet funding. |
config.validation.minAmount | Optional | Minimum deposit amount. Must be 0 or greater. |
config.validation.maxAmount | Optional | Maximum deposit amount. Must be greater than 0. |
config.theme | Optional | "dark", "light", or "system". Defaults to light; system follows OS changes. Overrides appearance.mode. |
config.appearance | Optional | Supports heading, appName, appLogoURL, mode, and primaryColor. |
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, onConnectWallet | Optional | Host app callbacks. Use onConnectClick to open your app/header wallet modal from the element CTA. |
Dynamic executeDeposit Execution
beforeExecute):
type ExecuteDeposit = (
tokenSymbol: string,
tokenAddress: `0x${string}`,
amount: bigint,
chainId: number,
user: `0x${string}`,
) => {
to: `0x${string}`;
gas: bigint;
data?: `0x${string}`;
value?: bigint;
tokenApproval?: {
toTokenAddress: `0x${string}`;
amount: bigint;
spender: `0x${string}`;
};
};- Quoting: As the user selects funding tokens and inputs amounts to spend, Nexus generates a quote for the destination token.
- Dynamic Calldata Construction: The SDK invokes
beforeExecutewith the settled destination amount (context.executeAmountRaw), calling yourexecuteDepositcallback with the actual quotedamount. - Execution: The widget executes token approvals (if required) and calls the deposit contract with the encoded calldata.
- Gas Requirement:
executeDepositmust return a positivebigintforgas(e.g.400_000n).
Onramp & Diagnostics
- Local Currency Funding: Setting
enableOnRamp: trueallows users to fund deposits with cash or card in addition to cross-chain wallet balances. - Provider Verification: Onramp checks the active wallet provider instead of trusting a persisted address. Disconnected users see "Connect Wallet" after quotes arrive; reconnecting refreshes the quote for the verified account before Pay is enabled.
- Wallet Disconnect Recovery: If the wallet disconnects during checkout, reconnect the funded wallet from the settled purchase screen to finish the deposit.
- Modal Dismissal: Popup widgets (
embed={false}) dismiss through the header close button, not outside clicks or Escape. - Console Tracing: Filter the browser console by
[Nexus Onramp]to trace payment status, SDK gas-swap events, wallet approval, and the final deposit receipt.
Modal Usage
embed={false} to invoke Deposit from a button and show the element as a modal. The widget still needs to sit under NexusProvider.
<NexusWidget
config={depositConfig}
connectedAddress={address}
embed={false}
defaultOpen={false}
/>const [open, setOpen] = useState(false);
<NexusWidget
config={depositConfig}
connectedAddress={address}
embed={false}
open={open}
onOpenChange={setOpen}
/>;Supporting Multiple Opportunities / Selector UI
NexusWidget executes a single targeted deposit flow at a time, build any opportunity selector in your parent component and render NexusWidget with the selected config:
import { useState } from "react";
import { NexusProvider, NexusWidget } from "@avail-project/widgets";
import { encodeFunctionData } from "viem";
const OPPORTUNITIES = {
aave: {
chain: 42161,
token: {
address: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
symbol: "USDT",
decimals: 6,
},
appName: "Aave",
depositAddress: "0x794a61358D6845594F94dc1DB02A252b5b4814aD",
executeDeposit: (symbol, tokenAddress, amount, chainId, user) => ({
to: "0x794a61358D6845594F94dc1DB02A252b5b4814aD",
gas: 400_000n,
data: encodeFunctionData({
/* Aave supply arguments */
}),
tokenApproval: {
toTokenAddress: tokenAddress,
amount,
spender: "0x794a61358D6845594F94dc1DB02A252b5b4814aD",
},
}),
},
compound: {
chain: 8453,
token: {
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
symbol: "USDC",
decimals: 6,
},
appName: "Compound",
depositAddress: "0xc3d688B66703497DAA19211EEdff47f25384cdc3",
executeDeposit: (symbol, tokenAddress, amount, chainId, user) => ({
to: "0xc3d688B66703497DAA19211EEdff47f25384cdc3",
gas: 400_000n,
data: encodeFunctionData({
/* Compound supply arguments */
}),
tokenApproval: {
toTokenAddress: tokenAddress,
amount,
spender: "0xc3d688B66703497DAA19211EEdff47f25384cdc3",
},
}),
},
};
export function MultiDepositShowcase({ address }) {
const [selectedOpt, setSelectedOpt] =
useState<keyof typeof OPPORTUNITIES>("aave");
return (
<div className="space-y-4">
<div className="flex flex-col gap-1.5 max-w-xs mx-auto">
<label className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">
Select Opportunity
</label>
<select
value={selectedOpt}
onChange={(e) =>
setSelectedOpt(e.target.value as keyof typeof OPPORTUNITIES)
}
className="w-full h-10 px-3 rounded-lg border border-border bg-background text-sm focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent cursor-pointer"
>
<option value="aave">Aave (USDT on Arbitrum)</option>
<option value="compound">Compound (USDC on Base)</option>
</select>
</div>
<NexusProvider config={{ identity: { clientId: "YOUR_NEXUS_CLIENT_ID" } }}>
<NexusWidget
config={{
mode: "deposit",
route: "exactInput",
enableOnRamp: true,
destination: {
chain: OPPORTUNITIES[selectedOpt].chain,
tokens: [OPPORTUNITIES[selectedOpt].token],
},
depositAddress: OPPORTUNITIES[selectedOpt].depositAddress,
executeDeposit: OPPORTUNITIES[selectedOpt].executeDeposit,
appearance: {
appName: OPPORTUNITIES[selectedOpt].appName,
mode: "system",
},
}}
connectedAddress={address}
/>
</NexusProvider>
</div>
);
}How is this guide?