Skip to content

Commit e21488f

Browse files
committed
release: version 2.0.0 with comprehensive documentation and examples
1 parent 04063e5 commit e21488f

File tree

12 files changed

+223
-68
lines changed

12 files changed

+223
-68
lines changed

CHANGELOG.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,52 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.0.0] - 2025-11-03
9+
10+
### Added
11+
12+
- **Trait-based architecture** enabling comprehensive testing through dependency injection
13+
- New `traits` module with `BlockchainProvider`, `AttestationProvider`, and `Clock` traits
14+
- New `providers` module with production implementations:
15+
- `AlloyProvider` - Wraps Alloy RPC providers for blockchain operations
16+
- `IrisAttestationProvider` - Production/sandbox Circle API client
17+
- `TokioClock` - Real tokio::time-based clock implementation
18+
- New `receipt_adapter` module with `ReceiptAdapter` trait for network-specific receipt handling
19+
- `EthereumReceiptAdapter` implementation for Ethereum-compatible networks
20+
- Comprehensive example in `examples/test_fakes.rs` demonstrating fake implementations for testing
21+
- `AttestationResponse` now derives `Clone` to support test scenarios
22+
23+
### Changed
24+
25+
- **BREAKING**: `Cctp` struct now has 7 type parameters (up from 2) for full dependency injection:
26+
- `SN` - Source network type
27+
- `DN` - Destination network type
28+
- `SP` - Source blockchain provider
29+
- `DP` - Destination blockchain provider
30+
- `A` - Attestation provider
31+
- `C` - Clock implementation
32+
- `RA` - Receipt adapter
33+
- **BREAKING**: Builder API now requires explicit provider injection:
34+
- Must wrap RPC providers with `AlloyProvider::new(provider)`
35+
- Must provide `attestation_provider(IrisAttestationProvider::production())`
36+
- Must provide `clock(TokioClock::new())`
37+
- Must provide `receipt_adapter(EthereumReceiptAdapter)`
38+
- **BREAKING**: Updated to Alloy 1.0+ API conventions
39+
- All examples updated to use new trait-based API
40+
- Library documentation updated with migration guide
41+
42+
### Benefits
43+
44+
- Full testability with ability to inject fake implementations for adversarial testing
45+
- Time control in tests without actual waiting
46+
- Network flexibility through receipt adapter abstraction
47+
- Type-safe external dependencies
48+
- Maintained backward compatibility for chain configurations and contract addresses
49+
50+
### Migration
51+
52+
See the [Migration Guide](README.md#migration-guide-from-version-1x-to-200) in README.md for detailed upgrade instructions.
53+
854
## [0.4.0] - 2025-10-14
955

1056
### Changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cctp-rs"
3-
version = "0.5.1"
3+
version = "2.0.0"
44
edition = "2021"
55
authors = ["Joseph Livesey <[email protected]>"]
66
categories = ["finance", "api-bindings"]

README.md

Lines changed: 101 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@
77

88
A production-ready Rust implementation of Circle's Cross-Chain Transfer Protocol (CCTP), enabling seamless USDC transfers across blockchain networks.
99

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+
1012
## Features
1113

14+
- 🧪 **Trait-based architecture** - Full dependency injection for comprehensive testing
1215
- 🚀 **Type-safe** contract interactions using Alloy
1316
- 🔄 **Multi-chain support** for mainnet and testnet networks
1417
- 📦 **Builder pattern** for intuitive API usage
18+
- 🎯 **Test fakes included** - Built-in fake implementations for testing
1519

1620
## Supported Chains
1721

@@ -40,42 +44,45 @@ Add to your `Cargo.toml`:
4044

4145
```toml
4246
[dependencies]
43-
cctp-rs = "0.3.0"
47+
cctp-rs = "2.0"
4448
```
4549

4650
### Basic Example
4751

4852
```rust
4953
use cctp_rs::{Cctp, CctpError};
54+
use cctp_rs::providers::{AlloyProvider, IrisAttestationProvider, TokioClock};
5055
use alloy_chains::NamedChain;
51-
use alloy_primitives::{Address, U256};
52-
use alloy_provider::{Provider, ProviderBuilder};
56+
use alloy_provider::ProviderBuilder;
5357

5458
#[tokio::main]
5559
async fn main() -> Result<(), CctpError> {
5660
// Create providers for source and destination chains
5761
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+
6064
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?;
6266

63-
// Set up the CCTP bridge
67+
// Set up the CCTP bridge with production providers
6468
let bridge = Cctp::builder()
6569
.source_chain(NamedChain::Mainnet)
6670
.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)
6976
.recipient("0xYourRecipientAddress".parse()?)
7077
.build();
7178

7279
// Get contract addresses
7380
let token_messenger = bridge.token_messenger_contract()?;
7481
let destination_domain = bridge.destination_domain_id()?;
75-
82+
7683
println!("Token Messenger: {}", token_messenger);
7784
println!("Destination Domain: {}", destination_domain);
78-
85+
7986
Ok(())
8087
}
8188
```
@@ -121,11 +128,14 @@ async fn bridge_usdc(bridge: &Cctp<impl Provider>) -> Result<(), CctpError> {
121128

122129
The library is organized into several key modules:
123130

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`)
126136
- **`attestation`** - Attestation response types from Circle's Iris API
127137
- **`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
129139

130140
## Error Handling
131141

@@ -172,18 +182,95 @@ println!("Domain ID: {}", domain_id);
172182
println!("Token Messenger: {}", token_messenger);
173183
```
174184

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+
175260
## Examples
176261

177262
Check out the [`examples/`](examples/) directory for complete working examples:
178263

179264
- [`basic_bridge.rs`](examples/basic_bridge.rs) - Simple USDC bridge example
180265
- [`attestation_monitoring.rs`](examples/attestation_monitoring.rs) - Monitor attestation status
181266
- [`multi_chain.rs`](examples/multi_chain.rs) - Bridge across multiple chains
267+
- [`test_fakes.rs`](examples/test_fakes.rs) - Comprehensive test fake implementations
182268

183269
Run examples with:
184270

185271
```bash
186272
cargo run --example basic_bridge
273+
cargo run --example test_fakes
187274
```
188275

189276
## Contributing

examples/attestation_monitoring.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
//! Run with: `cargo run --example attestation_monitoring`
66
77
use alloy_chains::NamedChain;
8-
use alloy_network::Ethereum;
98
use alloy_primitives::{FixedBytes, TxHash};
10-
use alloy_provider::{Provider, ProviderBuilder};
11-
use cctp_rs::{AttestationResponse, AttestationStatus, Cctp, CctpError};
9+
use alloy_provider::ProviderBuilder;
10+
use cctp_rs::providers::{AlloyProvider, IrisAttestationProvider, TokioClock};
11+
use cctp_rs::{AttestationResponse, AttestationStatus, Cctp, CctpError, UniversalReceiptAdapter};
1212
use std::time::Duration;
1313
use tokio::time::sleep;
1414

@@ -27,8 +27,11 @@ async fn main() -> Result<(), CctpError> {
2727
let bridge = Cctp::builder()
2828
.source_chain(NamedChain::Mainnet)
2929
.destination_chain(NamedChain::Arbitrum)
30-
.source_provider(eth_provider)
31-
.destination_provider(arb_provider)
30+
.source_provider(AlloyProvider::new(eth_provider))
31+
.destination_provider(AlloyProvider::new(arb_provider))
32+
.attestation_provider(IrisAttestationProvider::production())
33+
.clock(TokioClock::new())
34+
.receipt_adapter(UniversalReceiptAdapter)
3235
.recipient(
3336
"0x742d35Cc6634C0532925a3b844Bc9e7595f8fA0d"
3437
.parse()
@@ -74,7 +77,17 @@ async fn main() -> Result<(), CctpError> {
7477
}
7578

7679
/// Simulates monitoring attestation status changes
77-
async fn simulate_attestation_monitoring(_bridge: &Cctp<impl Provider<Ethereum> + Clone>) {
80+
async fn simulate_attestation_monitoring<SN, DN, SP, DP, A, C, RA>(
81+
_bridge: &Cctp<SN, DN, SP, DP, A, C, RA>,
82+
) where
83+
SN: alloy_network::Network,
84+
DN: alloy_network::Network,
85+
SP: cctp_rs::traits::BlockchainProvider<SN>,
86+
DP: cctp_rs::traits::BlockchainProvider<DN>,
87+
A: cctp_rs::traits::AttestationProvider,
88+
C: cctp_rs::traits::Clock,
89+
RA: cctp_rs::ReceiptAdapter<SN>,
90+
{
7891
println!("\n📈 Simulating attestation status progression:");
7992

8093
let statuses = [

examples/basic_bridge.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
use alloy_chains::NamedChain;
88
use alloy_primitives::Address;
99
use alloy_provider::ProviderBuilder;
10-
use cctp_rs::{Cctp, CctpError, CctpV1};
10+
use cctp_rs::providers::{AlloyProvider, IrisAttestationProvider, TokioClock};
11+
use cctp_rs::{Cctp, CctpError, CctpV1, UniversalReceiptAdapter};
1112
use std::str::FromStr;
1213

1314
#[tokio::main]
@@ -37,8 +38,11 @@ async fn main() -> Result<(), CctpError> {
3738
let bridge = Cctp::builder()
3839
.source_chain(NamedChain::Mainnet)
3940
.destination_chain(NamedChain::Arbitrum)
40-
.source_provider(eth_provider)
41-
.destination_provider(arb_provider)
41+
.source_provider(AlloyProvider::new(eth_provider))
42+
.destination_provider(AlloyProvider::new(arb_provider))
43+
.attestation_provider(IrisAttestationProvider::production())
44+
.clock(TokioClock::new())
45+
.receipt_adapter(UniversalReceiptAdapter)
4246
.recipient(recipient)
4347
.build();
4448

0 commit comments

Comments
 (0)