use alloy_chains::NamedChain;
use alloy_primitives::{FixedBytes, TxHash};
use alloy_provider::ProviderBuilder;
use cctp_rs::{Cctp, CctpError};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
#[tokio::main]
async fn main() -> Result<(), CctpError> {
tracing_subscriber::registry()
.with(EnvFilter::from_default_env())
.with(
tracing_subscriber::fmt::layer()
.with_target(true)
.with_level(true)
.with_thread_ids(false)
.with_thread_names(false),
)
.init();
tracing::info!("Starting CCTP bridge tracing example");
let eth_provider = ProviderBuilder::new()
.connect_http("https://eth-mainnet.g.alchemy.com/v2/demo".parse().unwrap());
let arb_provider = ProviderBuilder::new()
.connect_http("https://arb-mainnet.g.alchemy.com/v2/demo".parse().unwrap());
let bridge = Cctp::builder()
.source_chain(NamedChain::Mainnet)
.destination_chain(NamedChain::Arbitrum)
.source_provider(eth_provider)
.destination_provider(arb_provider)
.recipient(
"0x742d35Cc6634C0532925a3b844Bc9e7595f8fA0d"
.parse()
.unwrap(),
)
.build();
tracing::info!(
source_chain = %bridge.source_chain(),
destination_chain = %bridge.destination_chain(),
"Bridge initialized"
);
println!("\n=== Example 1: Getting MessageSent event ===");
println!("This operation creates a span: cctp_rs.get_message_sent_event");
println!("With attributes: tx_hash, source_chain, destination_chain\n");
let example_tx_hash: TxHash =
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
.parse()
.unwrap();
match bridge.get_message_sent_event(example_tx_hash).await {
Ok((message, hash)) => {
tracing::info!(
message_length = message.len(),
message_hash = %hash,
"Successfully extracted MessageSent event"
);
}
Err(e) => {
tracing::warn!(error = %e, "Example transaction not found (expected)");
}
}
println!("\n=== Example 2: Attestation polling ===");
println!("This operation creates multiple nested spans:");
println!(" - cctp_rs.get_attestation_with_retry (parent)");
println!(" ├─ cctp_rs.get_attestation (for each attempt)");
println!(" └─ cctp_rs.process_attestation_response\n");
let message_hash: FixedBytes<32> = FixedBytes::from([42u8; 32]);
match bridge
.get_attestation(
message_hash,
cctp_rs::PollingConfig::default()
.with_max_attempts(2)
.with_poll_interval_secs(5),
)
.await
{
Ok(attestation) => {
tracing::info!(
attestation_length = attestation.len(),
"Successfully retrieved attestation"
);
}
Err(e) => {
tracing::warn!(error = %e, "Attestation not found (expected for example data)");
}
}
println!("\n=== Span Hierarchy Demonstration Complete ===");
println!("\nKey Observability Features Demonstrated:");
println!("✓ Static span names: cctp_rs.operation_name");
println!("✓ Structured attributes: All dynamic data in fields");
println!("✓ Async-safe: Spans propagate across .await boundaries");
println!("✓ Parent-child relationships: Nested spans for complex operations");
println!("✓ Low cardinality: Span names are static, data in attributes");
println!("\n📊 In production, these spans would be:");
println!(" • Exported to Tempo/Jaeger via OTLP");
println!(" • Queryable via TraceQL");
println!(" • Visualized in Grafana dashboards");
println!("\n🔍 Example TraceQL queries:");
println!(" # Find all attestation polling operations");
println!(" {{ resource.service.name = \"cctp-rs\" && name = \"cctp_rs.get_attestation\" }}");
println!("\n # Find operations for specific chain pair");
println!(" {{ span.source_chain = \"Mainnet\" && span.destination_chain = \"Arbitrum\" }}");
Ok(())
}