- Introduced `ibc_example.rs` demonstrating Inter-Blockchain Communication operations including cross-chain transfers, channel management, packet handling, and relayer operations. - Introduced `zk_example.rs` showcasing Zero-Knowledge proof operations such as circuit compilation, proof generation and verification, and on-chain verification with multiple proving systems.
407 lines
14 KiB
Rust
407 lines
14 KiB
Rust
//! Synor IBC SDK Examples for Rust
|
|
//!
|
|
//! Demonstrates Inter-Blockchain Communication operations:
|
|
//! - Cross-chain token transfers
|
|
//! - Channel management
|
|
//! - Packet handling
|
|
//! - Relayer operations
|
|
|
|
use std::env;
|
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
|
use synor_ibc::{
|
|
ChannelState, FeeEstimateRequest, IbcConfig, Network, RelayConfig, RelayerRegistration,
|
|
SynorIbc, TransferRequest,
|
|
};
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
// Initialize client
|
|
let config = IbcConfig {
|
|
api_key: env::var("SYNOR_API_KEY").unwrap_or_else(|_| "your-api-key".to_string()),
|
|
endpoint: "https://ibc.synor.io/v1".to_string(),
|
|
timeout: 30000,
|
|
retries: 3,
|
|
debug: false,
|
|
default_network: Network::Mainnet,
|
|
};
|
|
|
|
let ibc = SynorIbc::new(config)?;
|
|
|
|
// Check service health
|
|
let healthy = ibc.health_check().await?;
|
|
println!("Service healthy: {}\n", healthy);
|
|
|
|
// Run examples
|
|
chains_example(&ibc).await?;
|
|
channels_example(&ibc).await?;
|
|
transfer_example(&ibc).await?;
|
|
packet_example(&ibc).await?;
|
|
relayer_example(&ibc).await?;
|
|
connection_example(&ibc).await?;
|
|
|
|
ibc.close().await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn chains_example(ibc: &SynorIbc) -> Result<(), Box<dyn std::error::Error>> {
|
|
println!("=== Connected Chains ===");
|
|
|
|
// Get all connected chains
|
|
let chains = ibc.chains.list().await?;
|
|
println!("Connected chains: {}", chains.len());
|
|
|
|
for chain in &chains {
|
|
println!(" {}:", chain.chain_id);
|
|
println!(" Name: {}", chain.name);
|
|
println!(" Status: {}", chain.status);
|
|
println!(" Block height: {}", chain.latest_height);
|
|
println!(" Channels: {}", chain.channel_count);
|
|
}
|
|
|
|
// Get specific chain info
|
|
let cosmos = ibc.chains.get("cosmoshub-4").await?;
|
|
println!("\nCosmos Hub details:");
|
|
println!(" RPC: {}", cosmos.rpc_endpoint);
|
|
println!(" Rest: {}", cosmos.rest_endpoint);
|
|
println!(" Native denom: {}", cosmos.native_denom);
|
|
println!(" Prefix: {}", cosmos.bech32_prefix);
|
|
|
|
// Get supported assets on a chain
|
|
let assets = ibc.chains.get_assets("cosmoshub-4").await?;
|
|
println!("\nSupported assets on Cosmos Hub:");
|
|
for asset in assets.iter().take(5) {
|
|
println!(" {}: {}", asset.symbol, asset.denom);
|
|
println!(" Origin: {}", asset.origin_chain);
|
|
println!(" Decimals: {}", asset.decimals);
|
|
}
|
|
|
|
// Get chain paths (routes)
|
|
let paths = ibc.chains.get_paths("synor-1", "cosmoshub-4").await?;
|
|
println!("\nPaths from Synor to Cosmos Hub:");
|
|
for path in &paths {
|
|
println!(" {} -> {}", path.source_channel, path.dest_channel);
|
|
println!(" Hops: {}", path.hops);
|
|
println!(" Avg time: {}s", path.avg_transfer_time);
|
|
}
|
|
|
|
println!();
|
|
Ok(())
|
|
}
|
|
|
|
async fn channels_example(ibc: &SynorIbc) -> Result<(), Box<dyn std::error::Error>> {
|
|
println!("=== Channel Management ===");
|
|
|
|
// List all channels
|
|
let channels = ibc.channels.list().await?;
|
|
println!("Total channels: {}", channels.len());
|
|
|
|
// Filter by state
|
|
let open_channels: Vec<_> = channels
|
|
.iter()
|
|
.filter(|c| c.state == ChannelState::Open)
|
|
.collect();
|
|
println!("Open channels: {}", open_channels.len());
|
|
|
|
for channel in open_channels.iter().take(3) {
|
|
println!("\n Channel {}:", channel.channel_id);
|
|
println!(" Port: {}", channel.port_id);
|
|
println!(
|
|
" Counterparty: {} on {}",
|
|
channel.counterparty_channel_id, channel.counterparty_chain_id
|
|
);
|
|
println!(" Ordering: {}", channel.ordering);
|
|
println!(" Version: {}", channel.version);
|
|
println!(" State: {:?}", channel.state);
|
|
}
|
|
|
|
// Get specific channel
|
|
let channel = ibc.channels.get("channel-0").await?;
|
|
println!("\nChannel-0 details:");
|
|
println!(" Connection: {}", channel.connection_id);
|
|
println!(" Counterparty port: {}", channel.counterparty_port_id);
|
|
|
|
// Get channel statistics
|
|
let stats = ibc.channels.get_stats("channel-0").await?;
|
|
println!("\nChannel-0 statistics:");
|
|
println!(" Total packets sent: {}", stats.packets_sent);
|
|
println!(" Total packets received: {}", stats.packets_received);
|
|
println!(" Pending packets: {}", stats.pending_packets);
|
|
println!(" Success rate: {:.1}%", stats.success_rate);
|
|
println!(" Avg relay time: {}s", stats.avg_relay_time);
|
|
|
|
// Get channel capacity
|
|
let capacity = ibc.channels.get_capacity("channel-0").await?;
|
|
println!("\nChannel-0 capacity:");
|
|
println!(" Max throughput: {} packets/block", capacity.max_packets_per_block);
|
|
println!(" Current utilization: {:.1}%", capacity.utilization);
|
|
|
|
println!();
|
|
Ok(())
|
|
}
|
|
|
|
async fn transfer_example(ibc: &SynorIbc) -> Result<(), Box<dyn std::error::Error>> {
|
|
println!("=== Cross-Chain Transfers ===");
|
|
|
|
// Estimate transfer fee
|
|
let estimate = ibc
|
|
.transfers
|
|
.estimate_fee(FeeEstimateRequest {
|
|
source_chain: "synor-1".to_string(),
|
|
dest_chain: "cosmoshub-4".to_string(),
|
|
denom: "usyn".to_string(),
|
|
amount: "1000000".to_string(), // 1 SYN (6 decimals)
|
|
})
|
|
.await?;
|
|
|
|
println!("Transfer fee estimate:");
|
|
println!(" Gas: {}", estimate.gas);
|
|
println!(" Fee: {} {}", estimate.fee, estimate.fee_denom);
|
|
println!(" Timeout: {}s", estimate.timeout);
|
|
|
|
// Calculate timeout timestamp (10 minutes from now)
|
|
let timeout_timestamp = SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)?
|
|
.as_millis() as u64
|
|
+ 600000;
|
|
|
|
// Initiate a transfer
|
|
println!("\nInitiating transfer...");
|
|
let transfer = ibc
|
|
.transfers
|
|
.send(TransferRequest {
|
|
source_chain: "synor-1".to_string(),
|
|
dest_chain: "cosmoshub-4".to_string(),
|
|
channel: "channel-0".to_string(),
|
|
sender: "synor1abc...".to_string(), // Your address
|
|
receiver: "cosmos1xyz...".to_string(), // Recipient address
|
|
denom: "usyn".to_string(),
|
|
amount: "1000000".to_string(), // 1 SYN
|
|
memo: Some("Cross-chain transfer example".to_string()),
|
|
timeout_height: 0, // Use timestamp instead
|
|
timeout_timestamp,
|
|
})
|
|
.await?;
|
|
|
|
println!("Transfer initiated:");
|
|
println!(" TX Hash: {}", transfer.tx_hash);
|
|
println!(" Sequence: {}", transfer.sequence);
|
|
println!(" Status: {}", transfer.status);
|
|
|
|
// Track transfer status
|
|
println!("\nTracking transfer...");
|
|
let status = ibc.transfers.get_status(&transfer.tx_hash).await?;
|
|
println!("Current status: {}", status.state);
|
|
println!(" Source confirmed: {}", status.source_confirmed);
|
|
println!(" Relayed: {}", status.relayed);
|
|
println!(" Dest confirmed: {}", status.dest_confirmed);
|
|
|
|
// Get transfer history
|
|
let history = ibc.transfers.get_history("synor1abc...", 5).await?;
|
|
println!("\nTransfer history (last 5):");
|
|
for tx in &history {
|
|
let direction = if tx.sender == "synor1abc..." {
|
|
"OUT"
|
|
} else {
|
|
"IN"
|
|
};
|
|
println!(" {} {} {} ({})", direction, tx.amount, tx.denom, tx.status);
|
|
}
|
|
|
|
// Get pending transfers
|
|
let pending = ibc.transfers.get_pending("synor1abc...").await?;
|
|
println!("\nPending transfers: {}", pending.len());
|
|
|
|
println!();
|
|
Ok(())
|
|
}
|
|
|
|
async fn packet_example(ibc: &SynorIbc) -> Result<(), Box<dyn std::error::Error>> {
|
|
println!("=== Packet Handling ===");
|
|
|
|
// Get pending packets
|
|
let packets = ibc.packets.get_pending("channel-0").await?;
|
|
println!("Pending packets on channel-0: {}", packets.len());
|
|
|
|
for packet in packets.iter().take(3) {
|
|
println!("\n Packet {}:", packet.sequence);
|
|
println!(" Source: {}/{}", packet.source_port, packet.source_channel);
|
|
println!(" Dest: {}/{}", packet.dest_port, packet.dest_channel);
|
|
println!(" State: {}", packet.state);
|
|
println!(" Data size: {} bytes", packet.data.len());
|
|
println!(" Timeout height: {}", packet.timeout_height);
|
|
println!(" Timeout timestamp: {}", packet.timeout_timestamp);
|
|
}
|
|
|
|
// Get packet by sequence
|
|
let packet = ibc.packets.get("channel-0", 1).await?;
|
|
println!("\nPacket details:");
|
|
println!(" Commitment: {}", packet.commitment);
|
|
println!(" Receipt: {}", packet.receipt.unwrap_or_default());
|
|
println!(" Acknowledgement: {}", packet.acknowledgement.unwrap_or_default());
|
|
|
|
// Get packet receipts
|
|
let receipts = ibc.packets.get_receipts("channel-0", &[1, 2, 3]).await?;
|
|
println!("\nPacket receipts:");
|
|
for (seq, receipt) in &receipts {
|
|
let status = if *receipt { "received" } else { "not received" };
|
|
println!(" Sequence {}: {}", seq, status);
|
|
}
|
|
|
|
// Get timed out packets
|
|
let timed_out = ibc.packets.get_timed_out("channel-0").await?;
|
|
println!("\nTimed out packets: {}", timed_out.len());
|
|
for p in &timed_out {
|
|
println!(" Sequence {}: timeout at {}", p.sequence, p.timeout_timestamp);
|
|
}
|
|
|
|
// Get unreceived packets
|
|
let unreceived = ibc.packets.get_unreceived("channel-0").await?;
|
|
let unreceived_str = if unreceived.is_empty() {
|
|
"none".to_string()
|
|
} else {
|
|
format!("{:?}", unreceived)
|
|
};
|
|
println!("\nUnreceived packet sequences: {}", unreceived_str);
|
|
|
|
// Get unacknowledged packets
|
|
let unacked = ibc.packets.get_unacknowledged("channel-0").await?;
|
|
let unacked_str = if unacked.is_empty() {
|
|
"none".to_string()
|
|
} else {
|
|
format!("{:?}", unacked)
|
|
};
|
|
println!("Unacknowledged packet sequences: {}", unacked_str);
|
|
|
|
println!();
|
|
Ok(())
|
|
}
|
|
|
|
async fn relayer_example(ibc: &SynorIbc) -> Result<(), Box<dyn std::error::Error>> {
|
|
println!("=== Relayer Operations ===");
|
|
|
|
// Get active relayers
|
|
let relayers = ibc.relayers.list().await?;
|
|
println!("Active relayers: {}", relayers.len());
|
|
|
|
for relayer in relayers.iter().take(3) {
|
|
println!("\n {}:", relayer.address);
|
|
println!(" Chains: {:?}", relayer.chains);
|
|
println!(" Packets relayed: {}", relayer.packets_relayed);
|
|
println!(" Success rate: {:.1}%", relayer.success_rate);
|
|
println!(" Avg latency: {}ms", relayer.avg_latency);
|
|
println!(" Fee rate: {:.2}%", relayer.fee_rate);
|
|
}
|
|
|
|
// Get relayer statistics
|
|
let stats = ibc.relayers.get_stats().await?;
|
|
println!("\nGlobal relayer statistics:");
|
|
println!(" Total relayers: {}", stats.total_relayers);
|
|
println!(" Active relayers: {}", stats.active_relayers);
|
|
println!(" Packets relayed (24h): {}", stats.packets_relayed_24h);
|
|
println!(" Total fees earned: {}", stats.total_fees_earned);
|
|
|
|
// Register as a relayer
|
|
println!("\nRegistering as relayer...");
|
|
let registration = ibc
|
|
.relayers
|
|
.register(RelayerRegistration {
|
|
chains: vec!["synor-1".to_string(), "cosmoshub-4".to_string()],
|
|
fee_rate: 0.1, // 0.1% fee
|
|
min_packet_size: 0,
|
|
max_packet_size: 1000000,
|
|
})
|
|
.await?;
|
|
println!("Registered with address: {}", registration.relayer_address);
|
|
|
|
// Start relaying (in background)
|
|
println!("\nStarting relay service...");
|
|
let relay_session = ibc
|
|
.relayers
|
|
.start_relay(RelayConfig {
|
|
channels: vec!["channel-0".to_string()],
|
|
auto_ack: true,
|
|
batch_size: 10,
|
|
poll_interval: 5000,
|
|
})
|
|
.await?;
|
|
println!("Relay session started: {}", relay_session.session_id);
|
|
|
|
// Get relay queue
|
|
let queue = ibc.relayers.get_queue("channel-0").await?;
|
|
println!("\nRelay queue for channel-0: {} packets", queue.len());
|
|
|
|
// Manually relay a packet
|
|
if let Some(first_packet) = queue.first() {
|
|
println!("\nRelaying packet...");
|
|
let relay_result = ibc
|
|
.relayers
|
|
.relay_packet(first_packet.sequence, "channel-0")
|
|
.await?;
|
|
println!("Relay result: {}", relay_result.status);
|
|
println!(" TX Hash: {}", relay_result.tx_hash);
|
|
println!(" Fee earned: {}", relay_result.fee_earned);
|
|
}
|
|
|
|
// Stop relay session
|
|
ibc.relayers.stop_relay(&relay_session.session_id).await?;
|
|
println!("\nRelay session stopped");
|
|
|
|
println!();
|
|
Ok(())
|
|
}
|
|
|
|
async fn connection_example(ibc: &SynorIbc) -> Result<(), Box<dyn std::error::Error>> {
|
|
println!("=== Connection Information ===");
|
|
|
|
// List connections
|
|
let connections = ibc.connections.list().await?;
|
|
println!("Total connections: {}", connections.len());
|
|
|
|
for conn in connections.iter().take(3) {
|
|
println!("\n {}:", conn.connection_id);
|
|
println!(" Client: {}", conn.client_id);
|
|
println!(" Counterparty: {}", conn.counterparty_connection_id);
|
|
println!(" State: {}", conn.state);
|
|
println!(" Versions: {:?}", conn.versions);
|
|
}
|
|
|
|
// Get connection details
|
|
let connection = ibc.connections.get("connection-0").await?;
|
|
println!("\nConnection-0 details:");
|
|
println!(" Delay period: {}ns", connection.delay_period);
|
|
println!(" Counterparty client: {}", connection.counterparty_client_id);
|
|
println!(" Counterparty prefix: {}", connection.counterparty_prefix);
|
|
|
|
// Get client state
|
|
let client = ibc.clients.get(&connection.client_id).await?;
|
|
println!("\nClient state:");
|
|
println!(" Chain ID: {}", client.chain_id);
|
|
println!(" Trust level: {}", client.trust_level);
|
|
println!(" Trusting period: {}", client.trusting_period);
|
|
println!(" Unbonding period: {}", client.unbonding_period);
|
|
println!(" Latest height: {}", client.latest_height);
|
|
println!(" Frozen: {}", client.frozen);
|
|
|
|
// Get consensus state
|
|
let consensus = ibc
|
|
.clients
|
|
.get_consensus_state(&connection.client_id, client.latest_height)
|
|
.await?;
|
|
println!("\nConsensus state at height {}:", client.latest_height);
|
|
println!(" Timestamp: {}", consensus.timestamp);
|
|
let root_preview = if consensus.root.len() > 20 {
|
|
&consensus.root[..20]
|
|
} else {
|
|
&consensus.root
|
|
};
|
|
println!(" Root: {}...", root_preview);
|
|
let validators_preview = if consensus.next_validators_hash.len() > 20 {
|
|
&consensus.next_validators_hash[..20]
|
|
} else {
|
|
&consensus.next_validators_hash
|
|
};
|
|
println!(" Next validators hash: {}...", validators_preview);
|
|
|
|
println!();
|
|
Ok(())
|
|
}
|