LayerZero
The LayerZero adapter enables omnichain messaging and token transfers between Tenzro Network and other blockchains using LayerZero V2 protocol. LayerZero provides ultra-light node verification and supports dozens of chains including Ethereum, Arbitrum, Optimism, Polygon, Avalanche, BNB Chain, and more.
Overview
LayerZero is an omnichain interoperability protocol that enables cross-chain messaging and token transfers through an innovative ultra-light node design. Unlike traditional bridges that rely on wrapped tokens or federated validators, LayerZero delivers messages directly between chains using independent oracles and relayers for security.
The Tenzro Network LayerZero adapter (LayerZeroAdapter) integrates with LayerZero V2 endpoints to provide:
- Cross-chain message passing with guaranteed delivery
- Native token transfers without wrapping
- Peer management for supported chains
- Nonce tracking for message ordering
- Real JSON-RPC integration with EndpointV2 contracts
- LayerZero Scan API integration for transfer status monitoring
Architecture
The LayerZero adapter sits within the tenzro-bridge crate as one of four bridge implementations. It implements the BridgeAdapter trait, providing a unified interface for cross-chain operations.
tenzro-bridge/
├── adapters/
│ ├── layerzero.rs # LayerZero V2 adapter
│ ├── chainlink_ccip.rs # Chainlink CCIP adapter
│ ├── debridge.rs # deBridge adapter
│ └── canton.rs # Canton enterprise adapter
├── router.rs # Strategy-based routing
└── types.rs # Common bridge typesThe adapter maintains state for:
- Endpoint configuration — EndpointV2 contract address and chain ID
- Peer registry — Trusted peer addresses on remote chains
- Nonce counters — Per-destination outbound message nonces
- Transfer tracking — In-flight transfer status monitoring
Key Features
Ultra-Light Nodes
LayerZero's breakthrough innovation is the ultra-light node (ULN) architecture. Instead of running full nodes for every chain, LayerZero uses:
- Oracles — Provide block headers from the source chain
- Relayers — Submit transaction proofs to the destination chain
- Endpoint contracts — Verify proofs and deliver messages on-chain
This separation of concerns ensures that even if an oracle and relayer collude, they cannot forge invalid messages—the endpoint contract cryptographically verifies all proofs.
Message Security
All cross-chain messages on Tenzro Network use cryptographic signatures for authenticity. The TenzroMessage type includes built-in signing and verification:
use tenzro_bridge::types::{TenzroMessage, ChainType};
use tenzro_crypto::signatures::{Ed25519Signer, Signer};
// Create a message
let mut message = TenzroMessage {
source_chain: ChainType::Tenzro,
destination_chain: ChainType::Ethereum,
payload: b"Transfer 100 TNZO to Alice".to_vec(),
nonce: 42,
sender: sender_address.clone(),
signature: None,
signer_public_key: None,
};
// Sign the message with Ed25519
let signer = Ed25519Signer::new(secret_key);
message.sign(&signer)?;
// Verify on the receiving side
assert!(message.verify_signature()?.is_valid);Message signing uses tenzro-crypto primitives supporting both Ed25519 and Secp256k1 signatures. The verification process recovers the public key from the signature and validates it matches the claimed signer.
Peer Management
LayerZero requires explicit peer configuration to prevent unauthorized chains from sending messages. The adapter maintains a peer registry mapping chain IDs to trusted peer addresses:
// Add a trusted peer on Ethereum
adapter.add_peer(
ChainType::Ethereum,
"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb".to_string()
).await?;
// Check if a peer is trusted
if adapter.is_peer_trusted(&ChainType::Ethereum).await {
// Send message to Ethereum peer
}Configuration
The LayerZero adapter is configured through the LayerZeroConfig struct:
use tenzro_bridge::adapters::LayerZeroConfig;
let config = LayerZeroConfig {
endpoint_address: "0x1a44076050125825900e736c501f859c50fE728c".to_string(),
chain_id: 1337, // Tenzro chain ID
rpc_url: "https://rpc.tenzro.network".to_string(),
scan_api_url: "https://scan.layerzero.network/api".to_string(),
scan_api_key: Some("your_api_key".to_string()),
};
let adapter = LayerZeroAdapter::new(config).await?;Configuration Parameters:
endpoint_address— LayerZero EndpointV2 contract address on Tenzrochain_id— Tenzro Network chain ID (default: 1337)rpc_url— JSON-RPC endpoint for Tenzro nodescan_api_url— LayerZero Scan API endpoint for transfer trackingscan_api_key— Optional API key for LayerZero Scan
Usage Examples
Sending a Cross-Chain Message
use tenzro_bridge::adapters::LayerZeroAdapter;
use tenzro_bridge::types::{TenzroMessage, ChainType};
// Create adapter instance
let adapter = LayerZeroAdapter::new(config).await?;
// Create message to send to Ethereum
let message = TenzroMessage {
source_chain: ChainType::Tenzro,
destination_chain: ChainType::Ethereum,
payload: serde_json::to_vec(&json!({
"action": "transfer",
"recipient": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"amount": "1000000000000000000" // 1 TNZO (18 decimals)
}))?,
nonce: adapter.get_next_nonce(&ChainType::Ethereum).await?,
sender: wallet_address.clone(),
signature: None,
signer_public_key: None,
};
// Send the message
let tx_hash = adapter.send_message(message).await?;
println!("Message sent in transaction: {}", tx_hash);Bridging Tokens
use tenzro_bridge::types::{BridgeTransfer, BridgeAsset};
// Create a token transfer from Tenzro to Arbitrum
let transfer = BridgeTransfer {
id: uuid::Uuid::new_v4().to_string(),
source_chain: ChainType::Tenzro,
destination_chain: ChainType::Arbitrum,
asset: BridgeAsset::Tnzo,
amount: 5_000_000_000_000_000_000, // 5 TNZO
sender: sender_address.clone(),
recipient: recipient_address.clone(),
status: TransferStatus::Pending,
tx_hash: None,
created_at: chrono::Utc::now(),
completed_at: None,
};
// Execute the transfer
let tx_hash = adapter.bridge_tokens(transfer.clone()).await?;
// Monitor transfer status via LayerZero Scan API
let status = adapter.get_transfer_status(&transfer.id).await?;
match status {
TransferStatus::Delivered => println!("Transfer completed!"),
TransferStatus::Pending => println!("Transfer in progress..."),
TransferStatus::Failed => println!("Transfer failed"),
}Verifying Incoming Messages
// Verify a message received from another chain
let incoming_message = receive_message_from_layerzero().await?;
// Check message signature
let verification = incoming_message.verify_signature()?;
if !verification.is_valid {
return Err("Invalid message signature");
}
// Check if sender chain is a trusted peer
if !adapter.is_peer_trusted(&incoming_message.source_chain).await {
return Err("Untrusted source chain");
}
// Process the verified message
process_cross_chain_message(incoming_message).await?;Supported Chains
LayerZero V2 supports 70+ blockchains. Common chains supported by the Tenzro adapter include:
// 22 chains supported with LayerZero V2 EIDs
ChainType::Ethereum // EID 30101
ChainType::BnbChain // EID 30102
ChainType::Avalanche // EID 30106
ChainType::Polygon // EID 30109
ChainType::Arbitrum // EID 30110
ChainType::Optimism // EID 30111
ChainType::ZkSync // EID 30165
ChainType::Base // EID 30184
ChainType::Solana // EID 30168
ChainType::Sei // EID 30280
ChainType::Sonic // EID 30332
ChainType::Berachain // EID 30362
ChainType::Story // EID 30364
ChainType::Monad // EID 30390
ChainType::MegaETH // EID 30398
ChainType::Tron // EID 30420
ChainType::Canton // Canton enterprise chainsBridge Router Integration
The BridgeRouter automatically selects the best adapter for each cross-chain operation based on configurable strategies:
use tenzro_bridge::router::{BridgeRouter, RoutingStrategy};
let router = BridgeRouter::new(RoutingStrategy::Cost);
// Add LayerZero adapter
router.add_adapter("layerzero", Box::new(layerzero_adapter)).await;
// Router automatically chooses LayerZero for supported chains
let transfer = BridgeTransfer { /* ... */ };
let result = router.route_transfer(transfer).await?;Available routing strategies:
- Cost — Minimize bridge fees
- Speed — Minimize confirmation time
- Availability — Use the first available adapter
Fee Estimation
LayerZero charges fees for oracle and relayer services. Estimate fees before sending messages:
// Estimate fee for a message to Ethereum
let estimated_fee = adapter.estimate_fee(
&ChainType::Ethereum,
1024 // payload size in bytes
).await?;
println!(
"Estimated fee: {} wei (native gas on source chain)",
estimated_fee
);Implementation Note:
Fee estimation performs a real eth_call to EndpointV2.quote(MessagingParams) on the source chain, returning the actual nativeFee required. A static heuristic is used as fallback only when the RPC call fails or no RPC URL is configured.
Security Considerations
When using LayerZero bridge, keep these security principles in mind:
- Always verify peer addresses — Only add trusted peers to the registry
- Validate message signatures — Use
verify_signature()on all incoming messages - Implement replay protection — Check nonces to prevent message replay (issue #102)
- Monitor transfer status — Use LayerZero Scan API to track cross-chain transfers
- Set reasonable limits — Implement maximum transfer amounts per transaction
Error Handling
use tenzro_bridge::error::BridgeError;
match adapter.send_message(message).await {
Ok(tx_hash) => {
println!("Message sent: {}", tx_hash);
}
Err(BridgeError::UntrustedPeer(chain)) => {
eprintln!("Chain {:?} is not a trusted peer", chain);
}
Err(BridgeError::InvalidSignature) => {
eprintln!("Message signature verification failed");
}
Err(BridgeError::InsufficientFee) => {
eprintln!("Insufficient fee for cross-chain message");
}
Err(e) => {
eprintln!("Bridge error: {:?}", e);
}
}Monitoring and Observability
The LayerZero adapter integrates with LayerZero Scan API for real-time transfer monitoring:
// Track a transfer using LayerZero Scan
let transfer_id = "550e8400-e29b-41d4-a716-446655440000";
let status = adapter.get_transfer_status(&transfer_id).await?;
println!("Transfer status: {:?}", status);
println!("Source tx: {:?}", transfer.tx_hash);
// Poll for completion
loop {
let status = adapter.get_transfer_status(&transfer_id).await?;
match status {
TransferStatus::Delivered => {
println!("Transfer delivered!");
break;
}
TransferStatus::Failed => {
eprintln!("Transfer failed!");
break;
}
TransferStatus::Pending => {
tokio::time::sleep(Duration::from_secs(10)).await;
}
}
}OFT Token Transfer Format
LayerZero V2 OFT (Omnichain Fungible Token) transfers use uint64 amountSD (shared decimals) rather than uint256. The adapter converts between local token decimals and shared decimals automatically. The OFT send payload format is [bytes32 to][uint64 amountSD].
TYPE_3 Options Encoding
Cross-chain messages use TYPE_3 options encoding for gas configuration on the destination chain. The encoding format is: worker_id (uint8) + option_size (uint16) + option_type (uint8) + gas (uint128) + value (uint128). This provides fine-grained control over destination execution gas and native value delivery.
Roadmap
The LayerZero adapter is under active development. Planned improvements include:
- Nonce-based replay protection for all message types
- Automatic transfer status updates via event listeners
- Support for custom oracles and relayers
- Integration with Tenzro's ZK proof system for privacy-preserving transfers
- Testnet deployment with live Ethereum/Arbitrum integration