|
7 | 7 |
|
8 | 8 | A production-ready Rust implementation of Circle's Cross-Chain Transfer Protocol (CCTP), enabling seamless USDC transfers across blockchain networks. |
9 | 9 |
|
| 10 | +> **Note:** This is version 2.0.0 of cctp-rs (the crate), which introduces a trait-based architecture for comprehensive testing. This refers to the library version, not the CCTP protocol version (which is v1). See the [Migration Guide](#migration-guide-from-version-1x-to-200) below for upgrading from earlier versions. |
| 11 | +
|
10 | 12 | ## Features |
11 | 13 |
|
| 14 | +- 🧪 **Trait-based architecture** - Full dependency injection for comprehensive testing |
12 | 15 | - 🚀 **Type-safe** contract interactions using Alloy |
13 | 16 | - 🔄 **Multi-chain support** for mainnet and testnet networks |
14 | 17 | - 📦 **Builder pattern** for intuitive API usage |
| 18 | +- 🎯 **Test fakes included** - Built-in fake implementations for testing |
15 | 19 |
|
16 | 20 | ## Supported Chains |
17 | 21 |
|
@@ -40,42 +44,45 @@ Add to your `Cargo.toml`: |
40 | 44 |
|
41 | 45 | ```toml |
42 | 46 | [dependencies] |
43 | | -cctp-rs = "0.3.0" |
| 47 | +cctp-rs = "2.0" |
44 | 48 | ``` |
45 | 49 |
|
46 | 50 | ### Basic Example |
47 | 51 |
|
48 | 52 | ```rust |
49 | 53 | use cctp_rs::{Cctp, CctpError}; |
| 54 | +use cctp_rs::providers::{AlloyProvider, IrisAttestationProvider, TokioClock}; |
50 | 55 | use alloy_chains::NamedChain; |
51 | | -use alloy_primitives::{Address, U256}; |
52 | | -use alloy_provider::{Provider, ProviderBuilder}; |
| 56 | +use alloy_provider::ProviderBuilder; |
53 | 57 |
|
54 | 58 | #[tokio::main] |
55 | 59 | async fn main() -> Result<(), CctpError> { |
56 | 60 | // Create providers for source and destination chains |
57 | 61 | let eth_provider = ProviderBuilder::new() |
58 | | - .on_http("https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY".parse()?); |
59 | | - |
| 62 | + .on_builtin("https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY").await?; |
| 63 | + |
60 | 64 | let arb_provider = ProviderBuilder::new() |
61 | | - .on_http("https://arb-mainnet.g.alchemy.com/v2/YOUR_API_KEY".parse()?); |
| 65 | + .on_builtin("https://arb-mainnet.g.alchemy.com/v2/YOUR_API_KEY").await?; |
62 | 66 |
|
63 | | - // Set up the CCTP bridge |
| 67 | + // Set up the CCTP bridge with production providers |
64 | 68 | let bridge = Cctp::builder() |
65 | 69 | .source_chain(NamedChain::Mainnet) |
66 | 70 | .destination_chain(NamedChain::Arbitrum) |
67 | | - .source_provider(eth_provider) |
68 | | - .destination_provider(arb_provider) |
| 71 | + .source_provider(AlloyProvider::new(eth_provider)) |
| 72 | + .destination_provider(AlloyProvider::new(arb_provider)) |
| 73 | + .attestation_provider(IrisAttestationProvider::production()) |
| 74 | + .clock(TokioClock::new()) |
| 75 | + .receipt_adapter(cctp_rs::EthereumReceiptAdapter) |
69 | 76 | .recipient("0xYourRecipientAddress".parse()?) |
70 | 77 | .build(); |
71 | 78 |
|
72 | 79 | // Get contract addresses |
73 | 80 | let token_messenger = bridge.token_messenger_contract()?; |
74 | 81 | let destination_domain = bridge.destination_domain_id()?; |
75 | | - |
| 82 | + |
76 | 83 | println!("Token Messenger: {}", token_messenger); |
77 | 84 | println!("Destination Domain: {}", destination_domain); |
78 | | - |
| 85 | + |
79 | 86 | Ok(()) |
80 | 87 | } |
81 | 88 | ``` |
@@ -121,11 +128,14 @@ async fn bridge_usdc(bridge: &Cctp<impl Provider>) -> Result<(), CctpError> { |
121 | 128 |
|
122 | 129 | The library is organized into several key modules: |
123 | 130 |
|
124 | | -- **`bridge`** - Core CCTP bridge implementation |
125 | | -- **`chain`** - Chain-specific configurations and support |
| 131 | +- **`bridge`** - Core CCTP bridge implementation with dependency injection |
| 132 | +- **`chain`** - Chain-specific configurations and the `CctpV1` trait |
| 133 | +- **`traits`** - Core trait abstractions (`BlockchainProvider`, `AttestationProvider`, `Clock`) |
| 134 | +- **`providers`** - Production implementations (`AlloyProvider`, `IrisAttestationProvider`, `TokioClock`) |
| 135 | +- **`receipt_adapter`** - Network-specific receipt handling (e.g., `EthereumReceiptAdapter`) |
126 | 136 | - **`attestation`** - Attestation response types from Circle's Iris API |
127 | 137 | - **`error`** - Comprehensive error types for proper error handling |
128 | | -- **`contracts`** - Type-safe bindings for TokenMessenger and MessageTransmitter |
| 138 | +- **`message_transmitter`** / **`token_messenger`** - Type-safe contract bindings |
129 | 139 |
|
130 | 140 | ## Error Handling |
131 | 141 |
|
@@ -172,18 +182,95 @@ println!("Domain ID: {}", domain_id); |
172 | 182 | println!("Token Messenger: {}", token_messenger); |
173 | 183 | ``` |
174 | 184 |
|
| 185 | +## Migration Guide: From Version 1.x to 2.0.0 |
| 186 | + |
| 187 | +Version 2.0.0 introduces breaking changes to enable comprehensive testing through dependency injection. All external I/O operations are now abstracted behind traits. |
| 188 | + |
| 189 | +### What Changed |
| 190 | + |
| 191 | +The `Cctp` struct now has 7 type parameters instead of 2, enabling you to inject custom implementations for: |
| 192 | +- Blockchain providers (RPC calls) |
| 193 | +- Attestation providers (Circle API calls) |
| 194 | +- Clock (time operations) |
| 195 | +- Receipt adapters (network-specific receipt handling) |
| 196 | + |
| 197 | +### Migration Steps |
| 198 | + |
| 199 | +**Version 1.x code:** |
| 200 | +```rust |
| 201 | +use cctp_rs::{Cctp, CctpError}; |
| 202 | +use alloy_provider::ProviderBuilder; |
| 203 | + |
| 204 | +let eth_provider = ProviderBuilder::new() |
| 205 | + .on_http("https://eth.llamarpc.com".parse()?); |
| 206 | + |
| 207 | +let arb_provider = ProviderBuilder::new() |
| 208 | + .on_http("https://arbitrum.llamarpc.com".parse()?); |
| 209 | + |
| 210 | +let bridge = Cctp::builder() |
| 211 | + .source_chain(NamedChain::Mainnet) |
| 212 | + .destination_chain(NamedChain::Arbitrum) |
| 213 | + .source_provider(eth_provider) |
| 214 | + .destination_provider(arb_provider) |
| 215 | + .recipient("0x...".parse()?) |
| 216 | + .build(); |
| 217 | +``` |
| 218 | + |
| 219 | +**Version 2.0.0 code:** |
| 220 | +```rust |
| 221 | +use cctp_rs::{Cctp, CctpError, EthereumReceiptAdapter}; |
| 222 | +use cctp_rs::providers::{AlloyProvider, IrisAttestationProvider, TokioClock}; |
| 223 | +use alloy_provider::ProviderBuilder; |
| 224 | + |
| 225 | +let eth_provider = ProviderBuilder::new() |
| 226 | + .on_builtin("https://eth.llamarpc.com").await?; |
| 227 | + |
| 228 | +let arb_provider = ProviderBuilder::new() |
| 229 | + .on_builtin("https://arbitrum.llamarpc.com").await?; |
| 230 | + |
| 231 | +let bridge = Cctp::builder() |
| 232 | + .source_chain(NamedChain::Mainnet) |
| 233 | + .destination_chain(NamedChain::Arbitrum) |
| 234 | + .source_provider(AlloyProvider::new(eth_provider)) // Wrap with AlloyProvider |
| 235 | + .destination_provider(AlloyProvider::new(arb_provider)) // Wrap with AlloyProvider |
| 236 | + .attestation_provider(IrisAttestationProvider::production()) // Add attestation provider |
| 237 | + .clock(TokioClock::new()) // Add clock |
| 238 | + .receipt_adapter(EthereumReceiptAdapter) // Add receipt adapter |
| 239 | + .recipient("0x...".parse()?) |
| 240 | + .build(); |
| 241 | +``` |
| 242 | + |
| 243 | +### Key Changes |
| 244 | + |
| 245 | +1. **Wrap RPC providers**: Use `AlloyProvider::new(provider)` to wrap Alloy providers |
| 246 | +2. **Add attestation provider**: Use `IrisAttestationProvider::production()` for mainnet or `IrisAttestationProvider::sandbox()` for testnets |
| 247 | +3. **Add clock**: Use `TokioClock::new()` for production |
| 248 | +4. **Add receipt adapter**: Use `EthereumReceiptAdapter` for Ethereum-compatible networks |
| 249 | +5. **Alloy API changes**: Use `.on_builtin()` instead of `.on_http()` (Alloy v1.0 change) |
| 250 | + |
| 251 | +### Benefits of Version 2.0.0 |
| 252 | + |
| 253 | +- **Testability**: Inject fake implementations for comprehensive testing |
| 254 | +- **Network flexibility**: Support for Optimism and other networks via custom receipt adapters |
| 255 | +- **Time control**: Test timeout behavior without waiting |
| 256 | +- **Type safety**: All external dependencies are explicit in the type signature |
| 257 | + |
| 258 | +See [`examples/test_fakes.rs`](examples/test_fakes.rs) for examples of implementing test doubles. |
| 259 | + |
175 | 260 | ## Examples |
176 | 261 |
|
177 | 262 | Check out the [`examples/`](examples/) directory for complete working examples: |
178 | 263 |
|
179 | 264 | - [`basic_bridge.rs`](examples/basic_bridge.rs) - Simple USDC bridge example |
180 | 265 | - [`attestation_monitoring.rs`](examples/attestation_monitoring.rs) - Monitor attestation status |
181 | 266 | - [`multi_chain.rs`](examples/multi_chain.rs) - Bridge across multiple chains |
| 267 | +- [`test_fakes.rs`](examples/test_fakes.rs) - Comprehensive test fake implementations |
182 | 268 |
|
183 | 269 | Run examples with: |
184 | 270 |
|
185 | 271 | ```bash |
186 | 272 | cargo run --example basic_bridge |
| 273 | +cargo run --example test_fakes |
187 | 274 | ``` |
188 | 275 |
|
189 | 276 | ## Contributing |
|
0 commit comments