Send Tokens
Transfer tokens from your Veridex vault to any address.
Prerequisites
- Wallet connected (see Create Wallet)
- Tokens in your vault (use testnet faucets)
- SDK initialized
Basic Transfer
Prepare and Execute
import { createSDK } from '@veridex/sdk';
import { ethers } from 'ethers';
const sdk = createSDK('base', {
network: 'testnet',
relayerUrl: 'https://relayer.veridex.network',
});
// Authenticate first (shows passkey picker)
const { credential } = await sdk.passkey.authenticate();
// Step 1: Prepare the transfer (shows gas cost before signing)
const prepared = await sdk.prepareTransfer({
targetChain: 10004, // Base Sepolia Wormhole chain ID
token: '0x036CbD53842c5426634e7929541eC2318f3dCF7e', // USDC
recipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f5A234',
amount: ethers.parseUnits('10', 6), // 10 USDC
});
console.log('Gas cost:', prepared.formattedCost);
console.log('Expires:', new Date(prepared.expiresAt));
// Step 2: Get human-readable summary
const summary = await sdk.getTransactionSummary(prepared);
console.log(summary.title); // "Transfer"
console.log(summary.description); // "Send 10.0 USDC to 0x742d...5A234"
// Step 3: Execute with a signer (signs with passkey, then dispatches)
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const result = await sdk.executeTransfer(prepared, signer);
console.log('Transaction hash:', result.transactionHash);
console.log('Sequence:', result.sequence);Native Token Transfer
const prepared = await sdk.prepareTransfer({
targetChain: 10004,
token: 'native',
recipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f5A234',
amount: ethers.parseEther('0.01'), // 0.01 ETH
});
const result = await sdk.executeTransfer(prepared, signer);
console.log('ETH transfer:', result.transactionHash);Agent SDK Payments
For AI agents, the Agent SDK provides a simpler payment interface:
import { createAgentWallet } from '@veridex/agentic-payments';
const agent = await createAgentWallet({
masterCredential: { /* passkey credential */ },
session: {
dailyLimitUSD: 50,
perTransactionLimitUSD: 10,
expiryHours: 24,
allowedChains: [10004], // Base Sepolia
},
});
// Direct payment — session key handles signing
const receipt = await agent.pay({
chain: 10004, // Base Sepolia
token: 'USDC',
amount: '10000000', // 10 USDC (6 decimals)
recipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f5A234',
});
console.log('Tx:', receipt.txHash);
console.log('Status:', receipt.status);Check Spending Limits Before Transfer
import { ethers } from 'ethers';
// Check if a transfer amount is within limits
const check = await sdk.checkSpendingLimit(ethers.parseUnits('100', 6));
if (check.allowed) {
console.log('Transfer allowed!');
} else {
console.log('Blocked:', check.message);
console.log('Suggestions:', check.suggestions);
}
// Get formatted limits for UI
const formatted = await sdk.getFormattedSpendingLimits();
console.log(`${formatted.dailyUsedPercentage}% of daily limit used`);Check Balance Before Transfer
// Get vault balances on the current chain
const portfolio = await sdk.getVaultBalances();
for (const entry of portfolio.tokens) {
console.log(`${entry.token.symbol}: ${entry.formatted}`);
}
console.log('Total USD:', portfolio.totalUsdValue);Transaction Tracking
Track transaction status after execution:
const result = await sdk.executeTransfer(prepared, signer);
// Wait for confirmation
const state = await sdk.waitForTransaction(result.transactionHash);
console.log('Status:', state.status);
console.log('Confirmations:', state.confirmations);
console.log('Tx Hash:', result.transactionHash);
console.log('Sequence:', result.sequence);React Example
import { useState } from 'react';
import { createSDK } from '@veridex/sdk';
import { ethers } from 'ethers';
const sdk = createSDK('base', {
network: 'testnet',
relayerUrl: 'https://relayer.veridex.network',
});
const USDC = '0x036CbD53842c5426634e7929541eC2318f3dCF7e';
export function SendForm() {
const [recipient, setRecipient] = useState('');
const [amount, setAmount] = useState('');
const [loading, setLoading] = useState(false);
const [txHash, setTxHash] = useState<string | null>(null);
const [summary, setSummary] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const handleSend = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError(null);
setTxHash(null);
try {
// Prepare transfer
const prepared = await sdk.prepareTransfer({
targetChain: 10004,
token: USDC,
recipient,
amount: ethers.parseUnits(amount, 6),
});
// Show summary
const txSummary = await sdk.getTransactionSummary(prepared);
setSummary(`${txSummary.description} (Gas: ${prepared.formattedCost})`);
// Execute
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const result = await sdk.executeTransfer(prepared, signer);
setTxHash(result.transactionHash);
} catch (err) {
setError(err instanceof Error ? err.message : 'Transfer failed');
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleSend} className="space-y-4">
<div>
<label className="block text-sm font-medium">Recipient</label>
<input
type="text"
value={recipient}
onChange={(e) => setRecipient(e.target.value)}
placeholder="0x..."
className="mt-1 block w-full rounded-md border p-2"
required
/>
</div>
<div>
<label className="block text-sm font-medium">Amount (USDC)</label>
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
placeholder="10.00"
step="0.01"
className="mt-1 block w-full rounded-md border p-2"
required
/>
</div>
<button
type="submit"
disabled={loading}
className="w-full bg-blue-600 text-white py-2 rounded-lg"
>
{loading ? 'Sending...' : 'Send USDC'}
</button>
{summary && <p className="text-gray-600 text-sm">{summary}</p>}
{txHash && (
<p className="text-green-600">
Success!{' '}
<a
href={`https://sepolia.basescan.org/tx/${txHash}`}
target="_blank"
rel="noopener noreferrer"
className="underline"
>
View on Explorer
</a>
</p>
)}
{error && <p className="text-red-600">{error}</p>}
</form>
);
}Common Token Addresses
Base Sepolia (Testnet)
| Token | Address |
|---|---|
| USDC | 0x036CbD53842c5426634e7929541eC2318f3dCF7e |
| WETH | 0x4200000000000000000000000000000000000006 |
Base Mainnet
| Token | Address |
|---|---|
| USDC | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
| WETH | 0x4200000000000000000000000000000000000006 |
Error Handling
try {
const prepared = await sdk.prepareTransfer({ /* ... */ });
const result = await sdk.executeTransfer(prepared, signer);
} catch (error: any) {
if (error.message?.includes('No credential set')) {
console.log('Please register or login first');
} else if (error.message?.includes('expired')) {
console.log('Prepared transfer expired, please prepare again');
} else {
console.error('Transfer failed:', error);
}
}
// For Agent SDK payments
import { AgentPaymentError, AgentPaymentErrorCode } from '@veridex/agentic-payments';
try {
await agent.pay({ chain: 10004, token: 'USDC', amount: '1000000', recipient: '0x...' });
} catch (error) {
if (error instanceof AgentPaymentError) {
if (error.code === AgentPaymentErrorCode.INSUFFICIENT_BALANCE) {
console.log('Not enough tokens:', error.suggestion);
} else if (error.code === AgentPaymentErrorCode.LIMIT_EXCEEDED) {
console.log('Spending limit exceeded');
}
}
}