Cross-Chain Transfers
Send tokens from one blockchain to another using Veridex.
Overview
Veridex uses Wormhole for cross-chain messaging. When you transfer cross-chain:
- Hub (Base) creates authorization message
- Wormhole guardians attest the message
- Destination spoke executes the transfer
Basic Cross-Chain Transfer
import { createSDK } from '@veridex/sdk';
import { ethers } from 'ethers';
const sdk = createSDK('base', {
network: 'testnet',
relayerUrl: 'https://relayer.veridex.network',
});
const { credential } = await sdk.passkey.authenticate();
// Step 1: Estimate bridge fees
const fees = await sdk.getBridgeFees({
sourceChain: 10004, // Base Sepolia
destinationChain: 10005, // Optimism Sepolia
token: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
recipient: sdk.getVaultAddress(),
amount: 10000000n,
});
console.log('Bridge fees:', fees.formattedTotal, fees.currency);
console.log('Source gas:', fees.sourceGas);
console.log('Message fee:', fees.messageFee);
console.log('Relayer fee:', fees.relayerFee);
// Step 2: Prepare the bridge
const prepared = await sdk.prepareBridge({
sourceChain: 10004,
destinationChain: 10005,
token: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
recipient: sdk.getVaultAddress(),
amount: 10000000n,
});
// Step 3: Execute with progress tracking
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const result = await sdk.executeBridge(prepared, signer, (progress) => {
console.log(`Step ${progress.step}/${progress.totalSteps}: ${progress.message}`);
});
console.log('Bridge completed:', result.transactionHash);Chain Configuration
Get Chain Info
import { getChainConfig, getSupportedChains } from '@veridex/sdk';
// Get all supported chains
const chains = getSupportedChains('testnet');
console.log(chains);
// ['base', 'optimism', 'arbitrum', 'solana', 'aptos', 'sui', 'starknet', 'stacks']
// Get specific chain config
const optimism = getChainConfig('optimism', 'testnet');
console.log(optimism);
// {
// name: 'optimism',
// chainId: 11155420,
// wormholeChainId: 10005,
// rpcUrl: 'https://sepolia.optimism.io',
// contracts: { factory: '0x...', implementation: '0x...' }
// }Wormhole Chain IDs
| Chain | Testnet | Mainnet |
|---|---|---|
| Base | 10004 | 30 |
| Optimism | 10005 | 24 |
| Arbitrum | 10003 | 23 |
| Solana | 1 | 1 |
| Aptos | 22 | 22 |
| Sui | 21 | 21 |
| Starknet | 50001 | 50002 |
| Stacks | 50003 | 2147483648 |
EVM to EVM Transfer
// Base → Optimism
const result = await sdk.bridgeWithTracking(
{
sourceChain: 10004,
destinationChain: 10005, // Optimism Sepolia
token: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
recipient: sdk.getVaultAddress(), // Same address!
amount: 10000000n,
},
signer,
(p) => console.log(p.message)
);EVM to Solana Transfer
// Base → Solana
const result = await sdk.bridgeWithTracking(
{
sourceChain: 10004,
destinationChain: 1, // Solana
token: USDC,
recipient: 'DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy', // Solana address
amount: 50000000n, // 50 USDC
},
signer,
(p) => console.log(p.message)
);EVM to Aptos Transfer
// Base → Aptos
const result = await sdk.bridgeWithTracking(
{
sourceChain: 10004,
destinationChain: 22, // Aptos
token: USDC,
recipient: '0x1a89da9e9f8f0bc90d8d492890bd55fb261c6277d2a95dfcac70c268d0c23dcc',
amount: 25000000n, // 25 USDC
},
signer,
(p) => console.log(p.message)
);Transfer Speed
Veridex supports two verification paths:
| Path | Speed | Use Case |
|---|---|---|
| Query (CCQ) | ~5-7 seconds | Balance checks, low-value transfers |
| VAA | ~60 seconds | High-value transfers, critical actions |
The relayer automatically selects the optimal path based on transaction value.
Track Cross-Chain Status
The executeBridge and bridgeWithTracking methods provide real-time progress via callbacks:
const result = await sdk.executeBridge(prepared, signer, (progress) => {
switch (progress.step) {
case 1: console.log('Signing transaction...'); break;
case 2: console.log('Dispatching to source chain...'); break;
case 3: console.log('Waiting for confirmations...'); break;
case 4: console.log('Fetching Wormhole VAA...'); break;
case 5: console.log('Relaying to destination chain...'); break;
case 6: console.log('Bridge completed!'); break;
}
});
console.log('Source tx:', result.transactionHash);
console.log('Sequence:', result.sequence);React Example
import { useState } from 'react';
import { createSDK, getSupportedChains } from '@veridex/sdk';
import { ethers } from 'ethers';
const sdk = createSDK('base', {
network: 'testnet',
relayerUrl: 'https://relayer.veridex.network',
});
const USDC = '0x036CbD53842c5426634e7929541eC2318f3dCF7e';
export function BridgeForm() {
const [targetChain, setTargetChain] = useState('10005');
const [amount, setAmount] = useState('');
const [progress, setProgress] = useState<string | null>(null);
const [fees, setFees] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const estimateFees = async () => {
const feeEstimate = await sdk.getBridgeFees({
sourceChain: 10004,
destinationChain: parseInt(targetChain),
token: USDC,
recipient: sdk.getVaultAddress(),
amount: ethers.parseUnits(amount || '0', 6),
});
setFees(`Estimated fees: ${feeEstimate.formattedTotal} ${feeEstimate.currency}`);
};
const handleBridge = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setProgress('Preparing bridge...');
try {
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const result = await sdk.bridgeWithTracking(
{
sourceChain: 10004,
destinationChain: parseInt(targetChain),
token: USDC,
recipient: sdk.getVaultAddress(),
amount: ethers.parseUnits(amount, 6),
},
signer,
(p) => setProgress(`Step ${p.step}/${p.totalSteps}: ${p.message}`)
);
setProgress(`Bridge completed! Tx: ${result.transactionHash}`);
} catch (error) {
setProgress(`Error: ${error instanceof Error ? error.message : 'Bridge failed'}`);
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleBridge} className="space-y-4">
<div>
<label className="block text-sm font-medium">Destination Chain</label>
<select
value={targetChain}
onChange={(e) => setTargetChain(e.target.value)}
className="mt-1 block w-full rounded-md border p-2"
>
<option value="10005">Optimism Sepolia</option>
<option value="10003">Arbitrum Sepolia</option>
<option value="1">Solana</option>
<option value="22">Aptos</option>
<option value="50003">Stacks Testnet</option>
</select>
</div>
<div>
<label className="block text-sm font-medium">Amount (USDC)</label>
<input
type="number"
value={amount}
onChange={(e) => { setAmount(e.target.value); setFees(null); }}
onBlur={estimateFees}
placeholder="10.00"
step="0.01"
className="mt-1 block w-full rounded-md border p-2"
required
/>
</div>
{fees && <p className="text-sm text-gray-500">{fees}</p>}
<button
type="submit"
disabled={loading}
className="w-full bg-purple-600 text-white py-2 rounded-lg"
>
{loading ? 'Bridging...' : 'Bridge USDC'}
</button>
{progress && <p className="text-sm text-gray-600">{progress}</p>}
</form>
);
}Agent SDK: Cross-Chain Router
The Agent SDK provides a CrossChainRouter for multi-chain agent operations:
import { createAgentWallet } from '@veridex/agentic-payments';
const agent = await createAgentWallet({
masterCredential: { /* ... */ },
session: {
dailyLimitUSD: 100,
perTransactionLimitUSD: 25,
expiryHours: 24,
allowedChains: [10004, 10005, 50003], // Base, Optimism, Stacks
},
});
// Agent can pay on any allowed chain
await agent.pay({ chain: 10004, token: 'USDC', amount: '1000000', recipient: '0x...' });
await agent.pay({ chain: 10005, token: 'USDC', amount: '2000000', recipient: '0x...' });
// Multi-chain balance check
const portfolio = await agent.getMultiChainBalance();
console.log('Total USD:', portfolio.totalUsdValue);Fee Estimation
const fees = await sdk.getBridgeFees({
sourceChain: 10004,
destinationChain: 10005,
token: USDC,
recipient: sdk.getVaultAddress(),
amount: 10000000n,
});
console.log('Bridge fees:', fees.formattedTotal, fees.currency);
console.log('Source gas:', fees.sourceGas);
console.log('Message fee:', fees.messageFee);
console.log('Relayer fee:', fees.relayerFee);
console.log('Estimated time:', fees.estimatedTimeSeconds);Error Handling
try {
const result = await sdk.bridgeWithTracking(params, signer, onProgress);
} catch (error: any) {
if (error.message?.includes('insufficient')) {
console.log('Not enough tokens to bridge');
} else if (error.message?.includes('unsupported')) {
console.log('Chain not supported');
} else if (error.message?.includes('VAA')) {
console.log('Wormhole attestation failed, try again');
} else {
console.error('Bridge error:', error);
}
}