use alloy_chains::NamedChain;
use alloy_primitives::{Address, FixedBytes, TxHash};
use alloy_provider::ProviderBuilder;
use cctp_rs::{Cctp, CctpError};
use tracing::{error, info, info_span};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
#[tokio::main]
async fn main() -> Result<(), CctpError> {
tracing_subscriber::registry()
.with(EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("cctp_rs=trace")))
.with(
tracing_subscriber::fmt::layer()
.with_target(true)
.with_level(true)
.with_thread_ids(false),
)
.init();
info!("Starting comprehensive 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 recipient: Address = "0x742d35Cc6634C0532925a3b844Bc9e7595f8fA0d"
.parse()
.unwrap();
let bridge = Cctp::builder()
.source_chain(NamedChain::Mainnet)
.destination_chain(NamedChain::Arbitrum)
.source_provider(eth_provider)
.destination_provider(arb_provider)
.recipient(recipient)
.build();
info!(
source_chain = %bridge.source_chain(),
destination_chain = %bridge.destination_chain(),
recipient = %recipient,
event = "bridge_initialized"
);
demonstrate_complete_bridge_flow(&bridge).await;
demonstrate_error_tracking(&bridge).await;
demonstrate_chain_validation_error();
info!("Comprehensive tracing example complete");
println!("\n╔══════════════════════════════════════════════════════════════╗");
println!("║ OpenTelemetry Instrumentation Demonstration Complete ║");
println!("╚══════════════════════════════════════════════════════════════╝");
println!("\n📊 Key Features Demonstrated:");
println!(" ✓ Complete span hierarchy from deposit to receive");
println!(" ✓ Error attributes following OTel semantic conventions");
println!(" ✓ Trace-level RPC and HTTP request spans");
println!(" ✓ Structured event logging with span context");
println!(" ✓ Async-safe span propagation");
println!(" ✓ Low-cardinality span names with high-cardinality attributes");
println!("\n🔧 Production Integration:");
println!(" • Export to Tempo/Jaeger via OTLP");
println!(" • Query with TraceQL in Grafana");
println!(" • Alert on error.type attributes");
println!(" • Track duration and performance metrics");
println!("\n📈 Observability Benefits:");
println!(" • Debug production issues with complete trace context");
println!(" • Track attestation polling performance");
println!(" • Monitor RPC provider reliability");
println!(" • Identify bottlenecks in bridge flow");
Ok(())
}
async fn demonstrate_complete_bridge_flow(
bridge: &Cctp<impl alloy_provider::Provider<alloy_network::Ethereum> + Clone>,
) {
println!("\n╔══════════════════════════════════════════════════════════════╗");
println!("║ Example 1: Complete Bridge Flow Instrumentation ║");
println!("╚══════════════════════════════════════════════════════════════╝");
println!("\nThis demonstrates the full span hierarchy for a bridge operation.");
println!("Watch the JSON output for nested spans with parent-child relationships.\n");
let span = info_span!(
"bridge_operation",
source_chain = %bridge.source_chain(),
destination_chain = %bridge.destination_chain(),
operation_type = "full_bridge_flow"
);
let _guard = span.enter();
info!(step = 1, event = "extracting_message_sent_event");
let example_tx_hash: TxHash =
"0x9f3ce6edbf3d1f08cfe3a20b7ce43c3d01e55fe3c2d7a9e5a2b5e5c8d6f9c2a1"
.parse()
.unwrap();
match bridge.get_message_sent_event(example_tx_hash).await {
Ok((message, hash)) => {
info!(
message_length_bytes = message.len(),
message_hash = %hash,
event = "message_sent_event_extracted_successfully"
);
}
Err(e) => {
info!(
error = %e,
expected = true,
event = "message_sent_event_not_found_expected"
);
}
}
info!(step = 2, event = "polling_for_attestation");
let message_hash: FixedBytes<32> = FixedBytes::from([0xaa; 32]);
match bridge
.get_attestation(
message_hash,
cctp_rs::PollingConfig::default()
.with_max_attempts(3)
.with_poll_interval_secs(2),
)
.await
{
Ok(attestation) => {
info!(
attestation_length_bytes = attestation.len(),
event = "attestation_retrieved_successfully"
);
}
Err(e) => {
info!(
error_type = "AttestationTimeout",
expected = true,
event = "attestation_timeout_expected"
);
let _ = e;
}
}
info!(event = "bridge_operation_complete");
}
async fn demonstrate_error_tracking(
bridge: &Cctp<impl alloy_provider::Provider<alloy_network::Ethereum> + Clone>,
) {
println!("\n╔══════════════════════════════════════════════════════════════╗");
println!("║ Example 2: Error Tracking with OTel Attributes ║");
println!("╚══════════════════════════════════════════════════════════════╝");
println!("\nThis demonstrates how errors are captured as span attributes.");
println!("Watch for error.type, error.message, and otel.status_code fields.\n");
let span = info_span!(
"error_tracking_demonstration",
operation_type = "error_scenarios"
);
let _guard = span.enter();
info!(scenario = 1, event = "testing_transaction_not_found");
let invalid_tx: TxHash = "0x0000000000000000000000000000000000000000000000000000000000000000"
.parse()
.unwrap();
match bridge.get_message_sent_event(invalid_tx).await {
Ok(_) => {
info!(event = "unexpected_success");
}
Err(e) => {
info!(error_recorded = true, event = "error_attributes_captured");
let _ = e;
}
}
info!(scenario = 2, event = "testing_attestation_not_found");
let invalid_hash: FixedBytes<32> = FixedBytes::from([0x00; 32]);
match bridge
.get_attestation(
invalid_hash,
cctp_rs::PollingConfig::default()
.with_max_attempts(2)
.with_poll_interval_secs(1),
)
.await
{
Ok(_) => {
info!(event = "unexpected_success");
}
Err(e) => {
info!(error_recorded = true, event = "error_attributes_captured");
let _ = e;
}
}
info!(event = "error_tracking_demonstration_complete");
}
fn demonstrate_chain_validation_error() {
println!("\n╔══════════════════════════════════════════════════════════════╗");
println!("║ Example 3: Chain Validation Error Tracking ║");
println!("╚══════════════════════════════════════════════════════════════╝");
println!("\nThis demonstrates error tracking for unsupported chains.");
println!("Watch for UnsupportedChain errors with context.\n");
let span = info_span!(
"chain_validation_demonstration",
operation_type = "unsupported_chain"
);
let _guard = span.enter();
use cctp_rs::CctpV1;
let unsupported_chain = NamedChain::BinanceSmartChain;
info!(
chain = %unsupported_chain,
event = "testing_unsupported_chain"
);
match unsupported_chain.cctp_domain_id() {
Ok(_) => {
error!(event = "unexpected_success_for_unsupported_chain");
}
Err(e) => {
info!(
error_recorded = true,
chain = %unsupported_chain,
event = "chain_not_supported_error_captured"
);
let _ = e;
}
}
info!(event = "chain_validation_demonstration_complete");
}