
Bridge
- 13 installs
- 610 repo stars
- Updated June 26, 2026
- alsk1992/cloddsbot
Bridge is a skill that transfers tokens across chains using the Wormhole and Circle CCTP protocols across seven supported chains.
About
Bridge is a skill for transferring tokens across chains using the Wormhole and Circle CCTP protocols. A developer quotes a route, executes a transfer, then redeems the tokens on the destination chain, or calls the same flow through a TypeScript API. It matters when an agent needs to move assets like USDC between EVM chains and Solana.
- Cross-chain token transfers via Wormhole and Circle CCTP
- Supports Solana, Ethereum, Polygon, Arbitrum, Optimism, Avalanche and Base
- Quote, send, redeem and status commands plus a TypeScript API
Bridge by the numbers
- 13 all-time installs (skills.sh)
- Ranked #289 of 479 Web3 & Blockchain skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
bridge capabilities & compatibility
Bridge fees range from ~$0.50 to ~$20 per route plus gas
- Capabilities
- dex · clanker
- Pricing
- Bring your own API key
What bridge says it does
Transfer tokens across chains using Wormhole and Circle CCTP protocols.
npx skills add https://github.com/alsk1992/cloddsbot --skill bridgeAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 13 |
|---|---|
| repo stars | ★ 610 |
| Last updated | June 26, 2026 |
| Repository | alsk1992/cloddsbot ↗ |
What it does
Quote, send, and redeem cross-chain token transfers over Wormhole and CCTP between Solana and EVM chains.
Who is it for?
Moving USDC and other tokens between Solana and EVM chains with quotes and redemption tracking
When should I use this skill?
You need to bridge tokens cross-chain and track the redemption
By the numbers
- 7 supported chains
- CCTP attestation usually ~15 minutes
Files
Bridge - Complete API Reference
Transfer tokens across chains using Wormhole and Circle CCTP protocols.
Supported Chains
| Chain | Wormhole | CCTP (USDC) |
|---|---|---|
| Solana | Yes | Yes |
| Ethereum | Yes | Yes |
| Polygon | Yes | Yes |
| Arbitrum | Yes | Yes |
| Optimism | Yes | Yes |
| Avalanche | Yes | Yes |
| Base | Yes | Yes |
---
Chat Commands
Quote
/bridge quote 100 USDC sol to eth # Quote 100 USDC Solana → Ethereum
/bridge quote 1000 USDC arb to base # Quote Arbitrum → Base
/bridge quote 50 USDC eth to sol # Quote Ethereum → SolanaExecute Transfer
/bridge send 100 USDC sol to eth # Send 100 USDC Solana → Ethereum
/bridge send 1000 USDC arb to base # Send Arbitrum → Base
/bridge send 50 USDC eth to sol --address <dest> # To specific addressRedeem (Claim)
/bridge redeem <tx-hash> # Claim transferred tokens
/bridge pending # List pending redemptionsStatus
/bridge status <tx-hash> # Check transfer status
/bridge history # View transfer history---
TypeScript API Reference
Wormhole Bridge
import { executeWormholeBridge, executeWormholeRedeem } from 'clodds/bridge/wormhole';
// Get quote
const quote = await getWormholeQuote({
sourceChain: 'solana',
destChain: 'ethereum',
token: 'USDC',
amount: 100,
});
console.log(`Transfer 100 USDC: Solana → Ethereum`);
console.log(`Fee: $${quote.fee}`);
console.log(`Est. time: ${quote.estimatedTime} seconds`);
// Execute transfer
const transfer = await executeWormholeBridge({
sourceChain: 'solana',
destChain: 'ethereum',
token: 'USDC',
amount: 100,
// Source wallet
sourcePrivateKey: process.env.SOLANA_PRIVATE_KEY,
// Destination address (optional, defaults to your address)
destAddress: '0x1234...',
});
console.log(`Transfer initiated: ${transfer.txHash}`);
console.log(`VAA: ${transfer.vaa}`);
console.log(`Status: ${transfer.status}`);
// Redeem on destination chain
const redeem = await executeWormholeRedeem({
destChain: 'ethereum',
vaa: transfer.vaa,
destPrivateKey: process.env.EVM_PRIVATE_KEY,
});
console.log(`Redeemed: ${redeem.txHash}`);
console.log(`Amount received: ${redeem.amount} USDC`);CCTP (Circle) Bridge
import { executeCCTPBridge, redeemCCTP } from 'clodds/bridge/cctp';
// CCTP is optimized for USDC transfers
const transfer = await executeCCTPBridge({
sourceChain: 'arbitrum',
destChain: 'base',
amount: 1000, // USDC
sourcePrivateKey: process.env.EVM_PRIVATE_KEY,
destAddress: '0x1234...',
});
console.log(`CCTP transfer: ${transfer.txHash}`);
console.log(`Message: ${transfer.messageHash}`);
// Wait for attestation (usually ~15 minutes)
await waitForAttestation(transfer.messageHash);
// Redeem
const redeem = await redeemCCTP({
destChain: 'base',
messageHash: transfer.messageHash,
destPrivateKey: process.env.EVM_PRIVATE_KEY,
});Check Status
import { getTransferStatus } from 'clodds/bridge';
const status = await getTransferStatus(txHash);
console.log(`Status: ${status.status}`);
// 'pending' | 'confirming' | 'attesting' | 'redeemable' | 'completed' | 'failed'
console.log(`Source confirmations: ${status.sourceConfirmations}`);
console.log(`VAA status: ${status.vaaStatus}`);
console.log(`Redeemed: ${status.redeemed}`);
if (status.status === 'redeemable') {
console.log(`Ready to redeem! VAA: ${status.vaa}`);
}Get Pending Redemptions
import { getPendingRedemptions } from 'clodds/bridge';
const pending = await getPendingRedemptions({
chains: ['ethereum', 'solana', 'arbitrum'],
address: myAddress,
});
for (const p of pending) {
console.log(`${p.sourceChain} → ${p.destChain}`);
console.log(` Amount: ${p.amount} ${p.token}`);
console.log(` Status: ${p.status}`);
console.log(` Age: ${p.age} minutes`);
}---
Transfer Flow
Wormhole
1. Lock tokens on source chain 2. Wait for confirmations (varies by chain) 3. Guardian attestation (VAA generation) 4. Redeem on destination chain
CCTP
1. Burn USDC on source chain 2. Wait for attestation (~15 min) 3. Mint USDC on destination chain
---
Fees & Times
| Route | Fee | Time |
|---|---|---|
| Solana → Ethereum | ~$5 | 15-20 min |
| Ethereum → Solana | ~$20 | 15-20 min |
| Arbitrum → Base (CCTP) | ~$0.50 | 15-20 min |
| Polygon → Arbitrum | ~$1 | 15-20 min |
---
Best Practices
1. Use CCTP for USDC - Faster and cheaper 2. Check gas prices - High gas can increase costs 3. Save VAA/message hash - Needed for redemption 4. Monitor pending transfers - Don't forget to redeem 5. Start with small amounts - Test before large transfers 6. Verify destination address - Double-check before sending
/**
* Bridge CLI Skill
*
* Commands:
* /bridge <amount> <token> from <chain> to <chain> - Bridge tokens
* /bridge quote <amount> <token> from <chain> to <chain> - Get quote
* /bridge status <txHash> - Check bridge status
* /bridge routes <token> - Show available routes
*/
import { formatHelp } from '../../help.js';
import { wrapSkillError } from '../../errors.js';
// Resolve destination address from env (same wallet as source)
function getDestinationAddress(): string {
// For EVM chains, use EVM_PRIVATE_KEY-derived address
const evmKey = process.env.EVM_PRIVATE_KEY;
if (evmKey) {
try {
const { Wallet } = require('ethers') as typeof import('ethers');
return new Wallet(evmKey).address;
} catch { /* fall through */ }
}
// For Solana, use SOLANA_WALLET_ADDRESS or derive from key
const solAddr = process.env.SOLANA_WALLET_ADDRESS;
if (solAddr) return solAddr;
return '';
}
// Map common token symbols to wormhole token addresses per chain
function resolveTokenForBridge(symbol: string, chain: string): string | undefined {
const tokens: Record<string, Record<string, string>> = {
USDC: {
ethereum: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
polygon: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',
base: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
arbitrum: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
optimism: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85',
},
USDT: {
ethereum: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
polygon: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',
arbitrum: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
},
WETH: {
ethereum: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
arbitrum: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
optimism: '0x4200000000000000000000000000000000000006',
base: '0x4200000000000000000000000000000000000006',
},
};
return tokens[symbol.toUpperCase()]?.[chain.toLowerCase()];
}
async function execute(args: string): Promise<string> {
const parts = args.trim().split(/\s+/);
const cmd = parts[0]?.toLowerCase() || 'help';
try {
const wormhole = await import('../../../bridge/wormhole');
switch (cmd) {
case 'quote': {
// /bridge quote 100 USDC from ethereum to base
const amount = parseFloat(parts[1] || '');
if (isNaN(amount) || parts.length < 7) {
return 'Usage: /bridge quote <amount> <token> from <chain> to <chain>';
}
const token = parts[2]?.toUpperCase();
const fromChain = parts[4];
const toChain = parts[6];
const destAddr = getDestinationAddress();
if (!destAddr) return 'No wallet configured. Set EVM_PRIVATE_KEY or SOLANA_WALLET_ADDRESS.';
const tokenAddress = resolveTokenForBridge(token, fromChain);
if (!tokenAddress) {
return `Token ${token} not found on ${fromChain}. Supported: USDC, USDT, WETH`;
}
const quote: any = await wormhole.wormholeQuote({
source_chain: fromChain,
destination_chain: toChain,
destination_address: destAddr,
token_address: tokenAddress,
amount: amount.toString(),
amount_units: 'human',
});
let output = `**Bridge Quote: ${amount} ${token}**\n\n`;
output += `From: ${fromChain}\nTo: ${toChain}\n`;
output += `Protocol: ${quote.protocol || 'Wormhole'}\n`;
if (quote.relayerFee) output += `Relayer fee: ${quote.relayerFee}\n`;
if (quote.estimatedTime) output += `Estimated time: ${quote.estimatedTime}\n`;
return output;
}
case 'usdc': {
// /bridge usdc 100 from ethereum to base
const amount = parseFloat(parts[1] || '');
if (isNaN(amount) || parts.length < 6) {
return 'Usage: /bridge usdc <amount> from <chain> to <chain>';
}
const fromChain = parts[3];
const toChain = parts[5];
const destAddr = getDestinationAddress();
if (!destAddr) return 'No wallet configured. Set EVM_PRIVATE_KEY or SOLANA_WALLET_ADDRESS.';
const quote: any = await wormhole.usdcQuoteAuto({
source_chain: fromChain,
destination_chain: toChain,
destination_address: destAddr,
amount: amount.toString(),
amount_units: 'human',
});
let output = `**USDC Bridge Quote (CCTP)**\n\n`;
output += `Amount: ${amount} USDC\n`;
output += `From: ${fromChain}\nTo: ${toChain}\n`;
output += `Protocol: CCTP (Circle)\n`;
if (quote.relayerFee) output += `Fee: ${quote.relayerFee}\n`;
return output;
}
case 'redeem': {
if (!parts[1]) return 'Usage: /bridge redeem <source-txid> --from <chain> --to <chain>';
const txid = parts[1];
const fromIdx = parts.indexOf('--from');
const toIdx = parts.indexOf('--to');
const fromChain = fromIdx >= 0 ? parts[fromIdx + 1] : '';
const toChain = toIdx >= 0 ? parts[toIdx + 1] : '';
if (!fromChain || !toChain) return 'Usage: /bridge redeem <source-txid> --from <chain> --to <chain>';
const result: any = await wormhole.wormholeRedeem({
source_chain: fromChain,
destination_chain: toChain,
source_txid: txid,
});
return `**Bridge Redeem**\n\nSource TX: \`${txid}\`\nFrom: ${fromChain}\nTo: ${toChain}\nStatus: ${result.status || 'submitted'}`;
}
case 'status': {
if (!parts[1]) return 'Usage: /bridge status <txHash>';
const txHash = parts[1];
// Query Wormholescan public API for transfer status
const resp = await fetch(`https://api.wormholescan.io/api/v1/operations?txHash=${txHash}`);
if (!resp.ok) {
return `**Bridge Status**\n\nTx: \`${txHash}\`\nCould not fetch status (HTTP ${resp.status}). Check manually: https://wormholescan.io/#/tx/${txHash}`;
}
const statusData: any = await resp.json();
const ops = statusData.operations || [];
if (ops.length === 0) {
return `**Bridge Status**\n\nTx: \`${txHash}\`\nNo transfer found yet. It may still be processing.\nTrack: https://wormholescan.io/#/tx/${txHash}`;
}
const op = ops[0];
let statusOutput = `**Bridge Status**\n\nTx: \`${txHash}\`\n`;
statusOutput += `Status: ${op.status || 'unknown'}\n`;
if (op.sourceChain) statusOutput += `Source: ${op.sourceChain.chainName || op.sourceChain.chainId || 'unknown'}\n`;
if (op.targetChain) statusOutput += `Destination: ${op.targetChain.chainName || op.targetChain.chainId || 'unknown'}\n`;
if (op.data?.tokenAmount) statusOutput += `Amount: ${op.data.tokenAmount}\n`;
if (op.data?.symbol) statusOutput += `Token: ${op.data.symbol}\n`;
if (op.vaa?.timestamp) statusOutput += `Timestamp: ${new Date(op.vaa.timestamp).toLocaleString()}\n`;
statusOutput += `\nExplorer: https://wormholescan.io/#/tx/${txHash}`;
return statusOutput;
}
case 'routes': {
const token = (parts[1] || 'USDC').toUpperCase();
// Query Wormholescan for supported chains
try {
const chainsResp = await fetch('https://api.wormholescan.io/api/v1/governor/available-notional-by-chain');
if (chainsResp.ok) {
const chainsData: any = await chainsResp.json();
const entries = chainsData.entries || [];
if (entries.length > 0) {
let routeOutput = `**Bridge Routes for ${token}**\n\n`;
routeOutput += `| Chain | Chain ID | Available Notional |\n`;
routeOutput += `|-------|----------|--------------------|\n`;
for (const entry of entries.slice(0, 20)) {
const chainName = entry.chainName || `Chain ${entry.chainId}`;
const notional = entry.remainingAvailableNotional
? `$${parseFloat(entry.remainingAvailableNotional).toLocaleString()}`
: 'N/A';
routeOutput += `| ${chainName} | ${entry.chainId} | ${notional} |\n`;
}
routeOutput += `\nProtocols: Wormhole Token Bridge, Circle CCTP (USDC)`;
return routeOutput;
}
}
} catch { /* fall through to static */ }
// Fallback static routes if API fails
return `**Bridge Routes for ${token}**\n\n` +
`| From | To | Protocol | Type |\n` +
`|------|----|----------|------|\n` +
`| Ethereum | Base | CCTP | Native USDC |\n` +
`| Ethereum | Polygon | Wormhole | Token Bridge |\n` +
`| Ethereum | Solana | Wormhole | Token Bridge |\n` +
`| Polygon | Base | CCTP | Native USDC |\n` +
`| Solana | Ethereum | Wormhole | Token Bridge |\n` +
`| Base | Ethereum | CCTP | Native USDC |`;
}
case 'help':
return helpText();
default: {
// Parse: <amount> <token> from <chain> to <chain>
const amount = parseFloat(parts[0] || '');
if (!isNaN(amount) && parts.length >= 5) {
const token = parts[1]?.toUpperCase();
const fromChain = parts[3];
const toChain = parts[5];
const destAddr = getDestinationAddress();
if (!destAddr) return 'No wallet configured. Set EVM_PRIVATE_KEY or SOLANA_WALLET_ADDRESS.';
if (token === 'USDC') {
const result: any = await wormhole.usdcBridgeAuto({
source_chain: fromChain,
destination_chain: toChain,
destination_address: destAddr,
amount: amount.toString(),
amount_units: 'human',
});
return `**USDC Bridge Initiated (CCTP)**\n\n` +
`Amount: ${amount} USDC\nFrom: ${fromChain}\nTo: ${toChain}\n` +
`Status: ${result.status || 'submitted'}\n` +
`TX: \`${result.sourceTxHash || 'pending'}\``;
}
const tokenAddress = resolveTokenForBridge(token, fromChain);
if (!tokenAddress) {
return `Token ${token} not found on ${fromChain}. Supported: USDC, USDT, WETH`;
}
const result: any = await wormhole.wormholeBridge({
source_chain: fromChain,
destination_chain: toChain,
destination_address: destAddr,
token_address: tokenAddress,
amount: amount.toString(),
amount_units: 'human',
});
return `**Bridge Initiated**\n\n` +
`Amount: ${amount} ${token}\nFrom: ${fromChain}\nTo: ${toChain}\n` +
`Protocol: Wormhole Token Bridge\n` +
`Status: ${result.status || 'submitted'}`;
}
return helpText();
}
}
} catch (error) {
return wrapSkillError('Bridge', cmd || 'command', error);
}
}
function helpText(): string {
return formatHelp({
name: 'Bridge',
emoji: '\u{1F309}',
description: 'Cross-chain token transfers using Wormhole and CCTP',
sections: [
{
title: 'Commands',
commands: [
{ cmd: '/bridge <amount> <token> from <chain> to <chain>', description: 'Bridge tokens' },
{ cmd: '/bridge quote <amount> <token> from <chain> to <chain>', description: 'Get quote without executing' },
{ cmd: '/bridge usdc <amount> from <chain> to <chain>', description: 'USDC via CCTP (Circle)' },
{ cmd: '/bridge redeem <txid> --from <chain> --to <chain>', description: 'Redeem a pending transfer' },
{ cmd: '/bridge status <txHash>', description: 'Check bridge transfer status' },
{ cmd: '/bridge routes [token]', description: 'Show available routes' },
],
},
],
examples: [
'/bridge 100 USDC from ethereum to base',
'/bridge quote 0.5 WETH from ethereum to arbitrum',
'/bridge usdc 500 from polygon to base',
'/bridge status 0xabc123...',
],
envVars: [
{ name: 'EVM_PRIVATE_KEY', description: 'EVM wallet private key for signing bridge transactions', required: true },
{ name: 'SOLANA_WALLET_ADDRESS', description: 'Solana wallet address (for Solana bridging)', required: false },
],
seeAlso: [
{ cmd: '/cake', description: 'DEX trading on PancakeSwap' },
{ cmd: '/trading-evm', description: 'EVM chain trading' },
{ cmd: '/bags', description: 'View token balances across chains' },
],
notes: [
'Supported chains: Ethereum, Polygon, Base, Solana, Arbitrum, Optimism',
'Protocols: Wormhole Token Bridge, Circle CCTP',
'Shortcut: /bridge usdc — skips token arg for USDC-specific CCTP bridging',
],
});
}
export default {
name: 'bridge',
description: 'Cross-chain token transfers using Wormhole and CCTP',
commands: ['/bridge'],
handle: execute,
};