Skip to main content

circles_transfers/
error.rs

1use alloy_primitives::Address;
2use thiserror::Error;
3
4/// Transfers package error source categories.
5#[derive(Debug, Clone, Copy)]
6pub enum TransfersErrorSource {
7    Transfers,
8    Pathfinding,
9    FlowMatrix,
10    Validation,
11}
12
13/// Transfer-specific errors.
14#[derive(Debug, Error)]
15pub enum TransferError {
16    /// Generic error with optional context.
17    #[error("{message}")]
18    Generic {
19        message: String,
20        code: Option<String>,
21        category: TransfersErrorSource,
22    },
23    /// No valid path found for the route.
24    #[error("No valid transfer path found from {from:#x} to {to:#x}. {reason}")]
25    NoPathFound {
26        from: Address,
27        to: Address,
28        reason: String,
29    },
30    /// Insufficient balance for requested transfer.
31    #[error("Insufficient balance for transfer. Requested: {requested} wei, available: {available} wei.")]
32    InsufficientBalance {
33        requested: String,
34        available: String,
35        from: Address,
36        to: Address,
37    },
38    /// Wrapped tokens required but not enabled.
39    #[error("Insufficient unwrapped token balance; wrapped tokens present but use_wrapped_balances is false.")]
40    WrappedTokensRequired,
41    /// Flow matrix contains unregistered avatars.
42    #[error("Flow matrix contains {count} unregistered avatar(s): {addresses:?}")]
43    UnregisteredAvatars {
44        addresses: Vec<Address>,
45        count: usize,
46    },
47    /// Flow matrix terminal sum mismatch.
48    #[error(
49        "Flow matrix terminal sum ({terminal_sum}) does not equal expected amount ({expected})"
50    )]
51    FlowMatrixMismatch {
52        terminal_sum: String,
53        expected: String,
54    },
55    /// Transfer path is empty.
56    #[error("Transfer path is empty for route from {from:#x} to {to:#x}")]
57    EmptyPath { from: Address, to: Address },
58}
59
60impl TransferError {
61    pub fn no_path_found(from: Address, to: Address, reason: Option<String>) -> Self {
62        TransferError::NoPathFound {
63            from,
64            to,
65            reason: reason.unwrap_or_else(|| "This could mean there's no trust connection, insufficient balance, or the tokens are not transferable.".to_string()),
66        }
67    }
68
69    pub fn insufficient_balance(
70        requested: alloy_primitives::U256,
71        available: alloy_primitives::U256,
72        from: Address,
73        to: Address,
74    ) -> Self {
75        TransferError::InsufficientBalance {
76            requested: requested.to_string(),
77            available: available.to_string(),
78            from,
79            to,
80        }
81    }
82
83    pub fn wrapped_tokens_required() -> Self {
84        TransferError::WrappedTokensRequired
85    }
86
87    pub fn unregistered_avatars(addresses: Vec<Address>) -> Self {
88        let count = addresses.len();
89        TransferError::UnregisteredAvatars { addresses, count }
90    }
91
92    pub fn flow_matrix_mismatch(
93        terminal_sum: alloy_primitives::U256,
94        expected: alloy_primitives::U256,
95    ) -> Self {
96        TransferError::FlowMatrixMismatch {
97            terminal_sum: terminal_sum.to_string(),
98            expected: expected.to_string(),
99        }
100    }
101
102    pub fn empty_path(from: Address, to: Address) -> Self {
103        TransferError::EmptyPath { from, to }
104    }
105
106    pub fn generic(
107        message: impl Into<String>,
108        code: Option<impl Into<String>>,
109        category: TransfersErrorSource,
110    ) -> Self {
111        TransferError::Generic {
112            message: message.into(),
113            code: code.map(|c| c.into()),
114            category,
115        }
116    }
117}