Build a Cross-Chain DeFi Application
Build a cross-chain DeFi application that bridges tokens across Ethereum, Arbitrum, Base, and Solana using four bridge adapters — LayerZero V2, Chainlink CCIP, deBridge DLN, and LI.FI. Compare routes and fees in real time, execute optimal cross-chain swaps, track bridge status, and aggregate yield opportunities across chains. Includes both Rust and TypeScript SDK examples.
What You'll Build
- Multi-adapter bridge route comparison (LayerZero, CCIP, deBridge, LI.FI)
- Optimal cross-chain token transfers with fee minimization
- Real-time bridge status tracking with polling
- Cross-chain DEX swap execution (bridge + swap in one flow)
- Yield aggregation across chains with automatic allocation
- Settlement of profits on the Tenzro Ledger
Bridge Adapters Used
- LayerZero V2 — EndpointV2 omnichain messaging, OFT transfers, 130+ chains
- Chainlink CCIP — Router.ccipSend() cross-chain messaging with token transfers
- deBridge DLN — Intent-based cross-chain swaps, order creation API
- LI.FI — Bridge aggregator covering 30+ bridges and 66+ chains
Prerequisites
[dependencies]
tenzro-sdk = "0.1"
tokio = { version = "1", features = ["full"] }
tracing-subscriber = "0.3"
uuid = { version = "1", features = ["v4"] }Or for TypeScript:
npm install @tenzro/sdkStep 1: Connect and Create Identity
use tenzro_sdk::{TenzroClient, SettlementRequest, config::SdkConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let config = SdkConfig::testnet();
let client = TenzroClient::connect(config).await?;
// Register identity and create wallet
let identity = client.identity().register_human("Cross-Chain DeFi Builder").await?;
let wallet = client.wallet().create_wallet().await?;
println!("DID: {} Wallet: {}", identity.did, wallet.address);TypeScript version
import { TenzroClient } from "@tenzro/sdk";
const client = new TenzroClient({ network: "testnet" });
// Register identity and create wallet
const identity = await client.identity.registerHuman("Cross-Chain DeFi Builder");
const wallet = await client.wallet.createWallet();
console.log(`DID: ${identity.did} Wallet: ${wallet.address}`);Step 2: Compare Bridge Routes and Fees
Before bridging, query all available adapters for routes and fees. The get_routes method returns results sorted by fee ascending, so the first entry is always the cheapest option.
// Compare bridge routes from Ethereum to Arbitrum
let routes = client.bridge().get_routes(
"ethereum", // source chain
"arbitrum", // destination chain
"USDC", // token
"500000000", // 500 USDC (6 decimals)
).await?;
for route in &routes {
println!(
" {} -- fee: {} USD, time: {}s",
route.adapter, route.fee_usd_estimate, route.estimated_time_seconds,
);
} layerzero -- fee: 0.42 USD, time: 120s
chainlink_ccip -- fee: 0.68 USD, time: 180s
debridge -- fee: 0.35 USD, time: 90sTypeScript version
// Compare bridge routes (TypeScript SDK)
const routes = await client.bridge.getRoutes({
sourceChain: "ethereum",
destinationChain: "arbitrum",
token: "USDC",
amount: "500000000",
});
for (const route of routes) {
console.log(
` ${route.adapter} -- fee: ${route.feeUsdEstimate} USD, time: ${route.estimatedTimeSeconds}s`
);
}Fee sources. LayerZero fees come from a real EndpointV2.quote() eth_call. CCIP fees come from Router.getFee(). deBridge fees come from the DLN order-creation API. LI.FI fees come from the /v1/quote REST endpoint. All fall back to static estimates if the RPC call fails.
Step 3: Execute the Bridge Transfer
Select the cheapest adapter and execute the transfer. The SDK handles calldata encoding, fee payment, and transaction submission.
// Execute the cheapest bridge transfer
let bridge_result = client.bridge().bridge_tokens(
"ethereum", // source
"arbitrum", // destination
"USDC", // token
"500000000", // amount
&wallet.address, // recipient
Some("debridge"), // adapter (cheapest from comparison)
).await?;
println!("Bridge tx: {}", bridge_result.transaction_hash);
println!("Order ID: {}", bridge_result.order_id);Bridge tx: 0x8a3f...c4d1
Order ID: ord-a1b2c3d4-e5f6-7890Step 4: Track Bridge Status
Bridge transfers are not instant. Poll the status endpoint until the transfer is fulfilled on the destination chain. Each adapter uses its own tracking mechanism: LayerZero uses the Scan API, CCIP uses OffRamp.getExecutionState(), and deBridge uses the stats API.
// Track bridge transfer status
loop {
let status = client.bridge().get_status(&bridge_result.order_id).await?;
println!("Status: {:?}", status.state);
match status.state.as_str() {
"Fulfilled" | "Completed" => {
println!("Bridge complete! Dest tx: {}", status.destination_tx.unwrap_or_default());
break;
}
"Failed" | "Cancelled" => {
eprintln!("Bridge failed: {}", status.error.unwrap_or_default());
break;
}
_ => {
tokio::time::sleep(std::time::Duration::from_secs(15)).await;
}
}
}Step 5: Cross-Chain Swap (Bridge + DEX)
A cross-chain swap combines a bridge transfer with a DEX trade on the destination chain. Bridge ETH from Arbitrum to Base, then swap into USDC on a Base DEX:
// Execute a cross-chain swap: sell ETH on Arbitrum, receive USDC on Base
// Step 1: Get the best route across all adapters
let swap_routes = client.bridge().get_routes("arbitrum", "base", "ETH", "100000000000000000").await?;
let best = &swap_routes[0]; // sorted by fee ascending
println!("Best route: {} (fee: {} USD)", best.adapter, best.fee_usd_estimate);
// Step 2: Execute the bridge
let swap_result = client.bridge().bridge_tokens(
"arbitrum", "base", "ETH", "100000000000000000",
&wallet.address, Some(&best.adapter),
).await?;
// Step 3: Once bridged, swap ETH -> USDC on Base DEX
let agent = client.agent().register(
"defi-swap-agent", "Cross-Chain Swap Agent", &["defi", "swap"],
).await?;
let dex_swap = client.agent().send_message(
&agent.agent_id,
&format!(
"Use eth_encode_function tool: \
function_signature=swapExactETHForTokens(uint256,address[],address,uint256), \
args=[\"0\", \"[\\\"0xWETH\\\", \\\"0xUSDC\\\"]\", \"{}\", \"999999999\"]",
wallet.address,
),
).await?;
println!("DEX swap calldata: {}", dex_swap.payload);Step 6: Yield Aggregation Across Chains
Scan yield opportunities across multiple chains, rank by APY, and automatically bridge capital to the best opportunity. The agent handles route selection and bridge execution.
// Yield aggregation: compare APY across chains and allocate
struct YieldOpportunity {
chain: String,
protocol: String,
apy: f64,
tvl_usd: f64,
}
let opportunities = vec![
YieldOpportunity { chain: "ethereum".into(), protocol: "Aave V3".into(), apy: 3.2, tvl_usd: 5_200_000_000.0 },
YieldOpportunity { chain: "arbitrum".into(), protocol: "GMX".into(), apy: 8.7, tvl_usd: 420_000_000.0 },
YieldOpportunity { chain: "base".into(), protocol: "Aerodrome".into(), apy: 12.4, tvl_usd: 180_000_000.0 },
];
// Sort by APY descending, filter by minimum TVL
let mut sorted: Vec<_> = opportunities.iter()
.filter(|o| o.tvl_usd > 100_000_000.0)
.collect();
sorted.sort_by(|a, b| b.apy.partial_cmp(&a.apy).unwrap());
for opp in &sorted {
println!("{} on {} -- APY: {:.1}%, TVL: {:.0}M",
opp.protocol, opp.chain, opp.apy, opp.tvl_usd / 1_000_000.0);
}
// Bridge to the best chain and deposit
let best_opp = &sorted[0];
println!("\nAllocating to {} on {}", best_opp.protocol, best_opp.chain);
// Check if we need to bridge
if best_opp.chain != "ethereum" {
let bridge = client.bridge().bridge_tokens(
"ethereum", &best_opp.chain, "USDC", "1000000000",
&wallet.address, None, // auto-select cheapest adapter
).await?;
println!("Bridging to {}: tx {}", best_opp.chain, bridge.transaction_hash);
}Aerodrome on base -- APY: 12.4%, TVL: $180M
GMX on arbitrum -- APY: 8.7%, TVL: $420M
Aave V3 on ethereum -- APY: 3.2%, TVL: $5200M
Allocating to Aerodrome on base
Bridging to base: tx 0xf4a9...2b7cStep 7: Settle on Tenzro Ledger
All profits settle on the Tenzro Ledger with the 0.5% network fee automatically deducted:
// Settle profits back to Tenzro Ledger
let settlement = client.settlement().settle(SettlementRequest {
request_id: format!("xchain-defi-{}", uuid::Uuid::new_v4()),
provider: wallet.address.clone(),
customer: "0x0000000000000000000000000000000000000000".to_string(),
amount: 2500,
asset: "TNZO".to_string(),
}).await?;
println!("Settlement: {} (status: {})", settlement.receipt_id, settlement.status);Full Example
Run with:
cargo run --example cross_chain_defiView full source
use tenzro_sdk::{TenzroClient, SettlementRequest, config::SdkConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let config = SdkConfig::testnet();
let client = TenzroClient::connect(config).await?;
// 1. Identity & Wallet
let identity = client.identity().register_human("Cross-Chain DeFi Builder").await?;
let wallet = client.wallet().create_wallet().await?;
// 2. Compare bridge routes
let routes = client.bridge().get_routes("ethereum", "arbitrum", "USDC", "500000000").await?;
let cheapest = &routes[0];
println!("Cheapest: {} ({} USD)", cheapest.adapter, cheapest.fee_usd_estimate);
// 3. Bridge tokens
let bridge = client.bridge().bridge_tokens(
"ethereum", "arbitrum", "USDC", "500000000",
&wallet.address, Some(&cheapest.adapter),
).await?;
// 4. Track until complete
loop {
let status = client.bridge().get_status(&bridge.order_id).await?;
if matches!(status.state.as_str(), "Fulfilled" | "Completed") { break; }
if matches!(status.state.as_str(), "Failed" | "Cancelled") {
return Err(format!("Bridge failed: {:?}", status.error).into());
}
tokio::time::sleep(std::time::Duration::from_secs(15)).await;
}
// 5. Settle on Tenzro Ledger
let settlement = client.settlement().settle(SettlementRequest {
request_id: format!("xchain-defi-{}", uuid::Uuid::new_v4()),
provider: wallet.address.clone(),
customer: "0x0000000000000000000000000000000000000000".to_string(),
amount: 2500,
asset: "TNZO".to_string(),
}).await?;
println!("Done. Settlement: {}", settlement.receipt_id);
Ok(())
}What You Learned
- Multi-adapter route comparison — querying LayerZero, CCIP, deBridge, and LI.FI for fees and timing
- Bridge execution — selecting the cheapest adapter and executing token transfers
- Status tracking — polling adapter-specific APIs until fulfillment
- Cross-chain swaps — combining bridge + DEX trades in a single flow
- Yield aggregation — ranking opportunities across chains and auto-allocating capital
- Settlement — finalizing profits on the Tenzro Ledger
Next Steps
- See the deBridge Cross-Chain Swap tutorial for a deep dive into deBridge DLN
- See the LI.FI Bridge Aggregation tutorial for the full LI.FI integration
- Read the Cross-Chain Arbitrage tutorial for an automated arbitrage agent