circles_pathfinder/
lib.rs

1//! # Circles Pathfinder
2//!
3//! Pathfinding and flow matrix calculation for the Circles protocol.
4//!
5//! This crate provides efficient pathfinding algorithms and flow matrix generation
6//! for the Circles universal basic income protocol, with ready-to-use types for
7//! smart contract interactions.
8//!
9//! ## Quick Start
10//!
11//! ### High-level API (Recommended)
12//!
13//! ```rust,no_run
14//! use circles_pathfinder::{FindPathParams, prepare_flow_for_contract};
15//! use alloy_primitives::{Address, aliases::U192, U256};
16//!
17//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
18//! let params = FindPathParams {
19//!     from: "0x123...".parse()?,
20//!     to: "0x456...".parse()?,
21//!     target_flow: U256::from(1_000_000_000_000_000_000u64), // 1 CRC
22//!     use_wrapped_balances: Some(true),
23//!     from_tokens: None,
24//!     to_tokens: None,
25//!     exclude_from_tokens: None,
26//!     exclude_to_tokens: None,
27//!     simulated_balances: None,
28//!     max_transfers: None,
29//! };
30//!
31//! // One function call gets contract-ready data
32//! let path_data = prepare_flow_for_contract("https://rpc.circles.com", params).await?;
33//!
34//! // Ready for smart contract calls
35//! let (vertices, edges, streams, coords) = path_data.to_contract_params();
36//! // contract.some_function(vertices, edges, streams, coords)
37//! # Ok(())
38//! # }
39//! ```
40//!
41//! ### Low-level API (Advanced)
42//!
43//! ```rust,no_run
44//! use circles_pathfinder::{find_path, create_flow_matrix};
45//! use alloy_primitives::{Address, aliases::U192};
46//!
47//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
48//! // Step 1: Find path
49//! let transfers = find_path(
50//!     "https://rpc.circles.com",
51//!     "0x123...".parse()?,
52//!     "0x456...".parse()?,
53//!     U192::from(1000u64),
54//!     true
55//! ).await?;
56//!
57//! // Step 2: Create flow matrix
58//! let matrix = create_flow_matrix(
59//!     "0x123...".parse()?,
60//!     "0x456...".parse()?,
61//!     U192::from(1000u64),
62//!     &transfers
63//! )?;
64//! # Ok(())
65//! # }
66//! ```
67//!
68//! ## Modules
69//!
70//! - `hub` - Circles Hub contract types and conversions
71//! - `rpc` - RPC communication and pathfinding
72//! - `flow` - Flow matrix calculation
73//! - `packing` - Coordinate packing utilities
74//! - `convenience` - High-level convenience functions
75//!
76//! ## Features
77//!
78//! - **Fast pathfinding** using Circles RPC endpoints
79//! - **Hub contract integration** with ready-to-use types
80//! - **Type safety** with `alloy-primitives`
81//! - **Efficient packing** for on-chain storage
82//! - **Comprehensive testing** with real-world scenarios
83
84mod convenience;
85mod flow;
86pub mod hub;
87mod packing;
88mod rpc;
89
90use alloy_primitives::{U256, aliases::U192};
91
92// Core public API - the main functions users need
93pub use flow::create_flow_matrix;
94pub mod path;
95
96// RPC functionality
97pub use circles_types::FindPathParams;
98pub use rpc::{find_path, find_path_with_params};
99
100// Hub contract integration types and functions
101use alloy_primitives::Address;
102use alloy_sol_types::sol;
103pub use hub::PathData;
104
105// High-level convenience functions
106pub use convenience::{
107    encode_redeem_flow_matrix, encode_redeem_trusted_data, get_available_flow,
108    prepare_flow_for_contract, prepare_flow_for_contract_simple,
109};
110
111pub use path::{
112    assert_no_netted_flow_mismatch, compute_netted_flow, expected_unwrapped_totals,
113    replace_wrapped_tokens, shrink_path_values, token_info_map_from_path, wrapped_totals_from_path,
114};
115
116// Utility functions for advanced users
117pub use packing::{pack_coordinates, transform_to_flow_vertices};
118
119#[derive(Clone, Debug)]
120pub struct FlowMatrix {
121    pub flow_vertices: Vec<Address>,
122    pub flow_edges: Vec<FlowEdge>,
123    pub streams: Vec<Stream>,
124    pub packed_coordinates: Vec<u8>,
125    pub source_coordinate: U256,
126}
127
128sol!(
129    /// Standard Circles Hub FlowEdge struct matching the contract ABI
130    #[derive(Debug, PartialEq)]
131    struct FlowEdge {
132        uint16 streamSinkId;
133        uint192 amount;
134    }
135
136    /// Standard Circles Hub Stream struct matching the contract ABI
137    #[derive(Debug, PartialEq)]
138    struct Stream {
139        uint16 sourceCoordinate;
140        uint16[] flowEdgeIds;
141        bytes data;
142    }
143
144    function redeem(bytes32 id, bytes calldata data) external;
145);
146
147/// Errors that can occur during pathfinding and flow matrix operations.
148#[derive(thiserror::Error, Debug)]
149pub enum PathfinderError {
150    /// Flow matrix terminal sum doesn't match expected value.
151    ///
152    /// This occurs when the sum of all terminal flow edges (edges that reach
153    /// the destination) doesn't equal the expected transfer amount. This
154    /// usually indicates an issue with the transfer path or RPC data.
155    ///
156    /// # Example
157    /// ```text
158    /// PathfinderError::Imbalanced {
159    ///     terminal_sum: 800,
160    ///     expected: 1000
161    /// }
162    /// ```
163    #[error("terminal sum {terminal_sum} != expected {expected}")]
164    Imbalanced {
165        /// Actual sum of terminal flow edges
166        terminal_sum: U192,
167        /// Expected total flow amount
168        expected: U192,
169    },
170
171    /// RPC transport/client error (HTTP/WS or deserialization).
172    #[error("rpc transport error: {0}")]
173    Transport(#[from] circles_rpc::CirclesRpcError),
174
175    /// JSON-RPC payload error returned by the server or an invalid response body.
176    #[error("rpc response error: {0}")]
177    RpcResponse(String),
178}