circles-types 0.3.1

Core type definitions for the Circles protocol
Documentation
use alloy_primitives::{aliases::U192, Address, Bytes, U256};
use serde::{Deserialize, Serialize};

/// Simulated balance for path finding.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SimulatedBalance {
    pub holder: Address,
    pub token: Address,
    pub amount: U256,
    pub is_wrapped: bool,
    pub is_static: bool,
}

/// Simulated trust connection for path finding.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SimulatedTrust {
    pub truster: Address,
    pub trustee: Address,
}

/// Path finding parameters for `circlesV2_findPath`.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FindPathParams {
    #[serde(rename = "Source")]
    pub from: Address,
    #[serde(rename = "Sink")]
    pub to: Address,
    #[serde(rename = "TargetFlow")]
    pub target_flow: U256,
    #[serde(rename = "UseWrappedBalances")]
    pub use_wrapped_balances: Option<bool>,
    #[serde(rename = "FromTokens")]
    pub from_tokens: Option<Vec<Address>>,
    #[serde(rename = "ToTokens")]
    pub to_tokens: Option<Vec<Address>>,
    #[serde(rename = "ExcludeFromTokens")]
    pub exclude_from_tokens: Option<Vec<Address>>,
    #[serde(rename = "ExcludeToTokens")]
    pub exclude_to_tokens: Option<Vec<Address>>,
    #[serde(rename = "SimulatedBalances")]
    pub simulated_balances: Option<Vec<SimulatedBalance>>,
    #[serde(rename = "SimulatedTrusts")]
    pub simulated_trusts: Option<Vec<SimulatedTrust>>,
    #[serde(rename = "MaxTransfers")]
    pub max_transfers: Option<u32>,
}

/// A single transfer step in a pathfinding result.
/// This is the pathfinding version; different from the contract-facing `TransferStep`.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PathfindingTransferStep {
    pub from: Address,
    pub to: Address,
    pub token_owner: String, // TypeScript uses string, keeping it for API compatibility
    pub value: U256,
}

/// Result of pathfinding computation.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PathfindingResult {
    pub max_flow: U256,
    pub transfers: Vec<PathfindingTransferStep>,
}

/// Flow edge structure for `operateFlowMatrix`.
/// Corresponds to `TypeDefinitions.FlowEdge` in Hub V2.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlowEdgeStruct {
    #[serde(rename = "streamSinkId")]
    pub stream_sink_id: u16,
    pub amount: U192, // uint192 in Solidity
}

/// Stream structure for `operateFlowMatrix`.
/// Corresponds to `TypeDefinitions.Stream` in Hub V2.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StreamStruct {
    #[serde(rename = "sourceCoordinate")]
    pub source_coordinate: u16,
    #[serde(rename = "flowEdgeIds")]
    pub flow_edge_ids: Vec<u16>,
    pub data: Bytes, // Handles both Uint8Array and Hex from TypeScript
}

/// Flow matrix for ABI encoding, used with Hub V2 `operateFlowMatrix`.
/// This is the pathfinding version; different from the contract-facing `FlowMatrix`.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PathfindingFlowMatrix {
    #[serde(rename = "flowVertices")]
    pub flow_vertices: Vec<String>, // Keep as strings for API compatibility
    #[serde(rename = "flowEdges")]
    pub flow_edges: Vec<FlowEdgeStruct>,
    pub streams: Vec<StreamStruct>,
    #[serde(rename = "packedCoordinates")]
    pub packed_coordinates: String, // Hex string
    #[serde(rename = "sourceCoordinate")]
    pub source_coordinate: u16, // Convenience field, not part of ABI
}

/// Advanced transfer options.
/// Extends `FindPathParams` to add transfer-specific options.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdvancedTransferOptions {
    // All fields from FindPathParams except from, to, targetFlow
    pub use_wrapped_balances: Option<bool>,
    pub from_tokens: Option<Vec<Address>>,
    pub to_tokens: Option<Vec<Address>>,
    pub exclude_from_tokens: Option<Vec<Address>>,
    pub exclude_to_tokens: Option<Vec<Address>>,
    pub simulated_balances: Option<Vec<SimulatedBalance>>,
    pub simulated_trusts: Option<Vec<SimulatedTrust>>,
    pub max_transfers: Option<u32>,

    /// Custom data to attach to the transfer (optional)
    pub tx_data: Option<Bytes>,
}

impl AdvancedTransferOptions {
    /// Convert to `FindPathParams` with the required from/to/targetFlow fields.
    pub fn to_find_path_params(
        self,
        from: Address,
        to: Address,
        target_flow: U256,
    ) -> FindPathParams {
        FindPathParams {
            from,
            to,
            target_flow,
            use_wrapped_balances: self.use_wrapped_balances,
            from_tokens: self.from_tokens,
            to_tokens: self.to_tokens,
            exclude_from_tokens: self.exclude_from_tokens,
            exclude_to_tokens: self.exclude_to_tokens,
            simulated_balances: self.simulated_balances,
            simulated_trusts: self.simulated_trusts,
            max_transfers: self.max_transfers,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{FindPathParams, SimulatedTrust};
    use alloy_primitives::{Address, U256};
    use serde_json::json;

    #[test]
    fn serializes_simulated_trusts_with_rpc_field_name() {
        let params = FindPathParams {
            from: Address::repeat_byte(0x11),
            to: Address::repeat_byte(0x22),
            target_flow: U256::from(42u64),
            use_wrapped_balances: Some(true),
            from_tokens: None,
            to_tokens: None,
            exclude_from_tokens: None,
            exclude_to_tokens: None,
            simulated_balances: None,
            simulated_trusts: Some(vec![SimulatedTrust {
                truster: Address::repeat_byte(0x33),
                trustee: Address::repeat_byte(0x44),
            }]),
            max_transfers: Some(7),
        };

        let serialized = serde_json::to_value(params).expect("serialize path params");

        assert_eq!(
            serialized["SimulatedTrusts"],
            json!([{
                "truster": format!("{:#x}", Address::repeat_byte(0x33)),
                "trustee": format!("{:#x}", Address::repeat_byte(0x44)),
            }])
        );
    }
}

// ============================================================================
// Original Flow Types (moved from lib.rs)
// ============================================================================

/// Edge in the flow graph (sinkId 1 == final hop).
/// This is the original version from lib.rs.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FlowEdge {
    pub stream_sink_id: u16,
    pub amount: U192,
}

/// Stream with byte data.
/// This is the original version from lib.rs.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Stream {
    pub source_coordinate: u16,
    pub flow_edge_ids: Vec<u16>,
    pub data: Vec<u8>,
}

/// Transfer step for internal flow calculations.
/// This is the original version from lib.rs—different from `PathfindingTransferStep`.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TransferStep {
    pub from_address: Address,
    pub to_address: Address,
    pub token_owner: Address,
    pub value: U192,
}

/// ABI-ready matrix returned by `create_flow_matrix`.
/// This is the original version from lib.rs—different from `PathfindingFlowMatrix`.
#[derive(Clone, Debug)]
pub struct FlowMatrix {
    pub flow_vertices: Vec<Address>,
    pub flow_edges: Vec<FlowEdge>,
    pub streams: Vec<Stream>,
    pub packed_coordinates: Vec<u8>,
    pub source_coordinate: u16,
}