openzeppelin_relayer/models/relayer/
response.rs

1use crate::models::NetworkType;
2use serde::{Deserialize, Serialize};
3use utoipa::ToSchema;
4
5use super::{
6    RelayerNetworkPolicy, RelayerRepoModel, RelayerSolanaSwapConfig, SolanaAllowedTokensPolicy,
7    SolanaFeePaymentStrategy,
8};
9
10#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
11pub struct DeletePendingTransactionsResponse {
12    pub queued_for_cancellation_transaction_ids: Vec<String>,
13    pub failed_to_queue_transaction_ids: Vec<String>,
14    pub total_processed: u32,
15}
16
17#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
18pub struct RelayerResponse {
19    pub id: String,
20    pub name: String,
21    pub network: String,
22    #[serde(rename = "network_type")]
23    pub network_type: NetworkType,
24    pub paused: bool,
25    pub policies: NetworkPolicyResponse,
26    pub address: String,
27    pub system_disabled: bool,
28}
29
30#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
31#[serde(tag = "network_type")]
32pub enum RelayerStatus {
33    #[serde(rename = "evm")]
34    Evm {
35        balance: String,
36        pending_transactions_count: u64,
37        last_confirmed_transaction_timestamp: Option<String>,
38        system_disabled: bool,
39        paused: bool,
40        nonce: String,
41    },
42    #[serde(rename = "stellar")]
43    Stellar {
44        balance: String,
45        pending_transactions_count: u64,
46        last_confirmed_transaction_timestamp: Option<String>,
47        system_disabled: bool,
48        paused: bool,
49        sequence_number: String,
50    },
51}
52
53#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
54#[serde(untagged)]
55pub enum NetworkPolicyResponse {
56    Evm(EvmPolicyResponse),
57    Solana(SolanaPolicyResponse),
58    Stellar(StellarPolicyResponse),
59}
60
61#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
62pub struct EvmPolicyResponse {
63    #[serde(skip_serializing_if = "Option::is_none")]
64    #[schema(nullable = false)]
65    pub gas_price_cap: Option<u128>,
66    #[serde(skip_serializing_if = "Option::is_none")]
67    #[schema(nullable = false)]
68    pub whitelist_receivers: Option<Vec<String>>,
69    #[serde(skip_serializing_if = "Option::is_none")]
70    #[schema(nullable = false)]
71    pub eip1559_pricing: Option<bool>,
72    pub private_transactions: bool,
73    pub min_balance: u128,
74}
75
76#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
77pub struct SolanaPolicyResponse {
78    fee_payment_strategy: SolanaFeePaymentStrategy,
79    #[serde(skip_serializing_if = "Option::is_none")]
80    pub fee_margin_percentage: Option<f32>,
81    #[serde(skip_serializing_if = "Option::is_none")]
82    #[schema(nullable = false)]
83    pub allowed_tokens: Option<Vec<SolanaAllowedTokensPolicy>>,
84    #[serde(skip_serializing_if = "Option::is_none")]
85    #[schema(nullable = false)]
86    pub allowed_programs: Option<Vec<String>>,
87    #[serde(skip_serializing_if = "Option::is_none")]
88    #[schema(nullable = false)]
89    pub allowed_accounts: Option<Vec<String>>,
90    #[serde(skip_serializing_if = "Option::is_none")]
91    #[schema(nullable = false)]
92    pub disallowed_accounts: Option<Vec<String>>,
93    #[serde(skip_serializing_if = "Option::is_none")]
94    #[schema(nullable = false)]
95    pub max_signatures: Option<u8>,
96    pub max_tx_data_size: u16,
97    pub min_balance: u64,
98    #[serde(skip_serializing_if = "Option::is_none")]
99    #[schema(nullable = false)]
100    pub max_allowed_fee_lamports: Option<u64>,
101    #[serde(skip_serializing_if = "Option::is_none")]
102    #[schema(nullable = false)]
103    pub swap_config: Option<RelayerSolanaSwapConfig>,
104}
105
106#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
107pub struct StellarPolicyResponse {
108    #[serde(skip_serializing_if = "Option::is_none")]
109    #[schema(nullable = false)]
110    pub max_fee: Option<u32>,
111    pub min_balance: u64,
112}
113
114impl From<RelayerRepoModel> for RelayerResponse {
115    fn from(model: RelayerRepoModel) -> Self {
116        let policies = match model.policies {
117            RelayerNetworkPolicy::Evm(evm) => NetworkPolicyResponse::Evm(EvmPolicyResponse {
118                gas_price_cap: evm.gas_price_cap,
119                whitelist_receivers: evm.whitelist_receivers,
120                eip1559_pricing: evm.eip1559_pricing,
121                min_balance: evm.min_balance,
122                private_transactions: evm.private_transactions,
123            }),
124            RelayerNetworkPolicy::Solana(solana) => {
125                NetworkPolicyResponse::Solana(SolanaPolicyResponse {
126                    fee_payment_strategy: solana.fee_payment_strategy,
127                    fee_margin_percentage: solana.fee_margin_percentage,
128                    min_balance: solana.min_balance,
129                    allowed_tokens: solana.allowed_tokens,
130                    allowed_programs: solana.allowed_programs,
131                    allowed_accounts: solana.allowed_accounts,
132                    disallowed_accounts: solana.disallowed_accounts,
133                    max_signatures: solana.max_signatures,
134                    max_tx_data_size: solana.max_tx_data_size,
135                    max_allowed_fee_lamports: solana.max_allowed_fee_lamports,
136                    swap_config: solana.swap_config,
137                })
138            }
139            RelayerNetworkPolicy::Stellar(stellar) => {
140                NetworkPolicyResponse::Stellar(StellarPolicyResponse {
141                    max_fee: stellar.max_fee,
142                    min_balance: stellar.min_balance,
143                })
144            }
145        };
146
147        Self {
148            id: model.id,
149            name: model.name,
150            network: model.network,
151            network_type: model.network_type,
152            paused: model.paused,
153            policies,
154            address: model.address,
155            system_disabled: model.system_disabled,
156        }
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use crate::models::{
163        RelayerEvmPolicy, RelayerSolanaPolicy, RelayerStellarPolicy, SolanaAllowedTokensSwapConfig,
164        SolanaFeePaymentStrategy,
165    };
166
167    use super::*;
168
169    #[test]
170    fn test_from_relayer_repo_model_evm() {
171        let model = RelayerRepoModel {
172            id: "test-id".to_string(),
173            name: "Test Relayer".to_string(),
174            network: "ethereum".to_string(),
175            network_type: NetworkType::Evm,
176            paused: false,
177            policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
178                gas_price_cap: Some(100),
179                whitelist_receivers: Some(vec!["0x123".to_string()]),
180                eip1559_pricing: Some(true),
181                min_balance: 1000,
182                private_transactions: true,
183            }),
184            address: "0xabc".to_string(),
185            system_disabled: false,
186            signer_id: "test-signer-id".to_string(),
187            notification_id: Some("test-notification-id".to_string()),
188            custom_rpc_urls: None,
189        };
190
191        let response: RelayerResponse = model.clone().into();
192
193        assert_eq!(response.id, model.id);
194        assert_eq!(response.name, model.name);
195        assert_eq!(response.network, model.network);
196        assert_eq!(response.network_type, model.network_type);
197        assert_eq!(response.paused, model.paused);
198        assert_eq!(response.address, model.address);
199        assert_eq!(response.system_disabled, model.system_disabled);
200
201        if let NetworkPolicyResponse::Evm(evm) = response.policies {
202            if let RelayerNetworkPolicy::Evm(expected) = model.policies {
203                assert_eq!(evm.gas_price_cap, expected.gas_price_cap);
204                assert_eq!(evm.whitelist_receivers, expected.whitelist_receivers);
205                assert_eq!(evm.eip1559_pricing, expected.eip1559_pricing);
206                assert_eq!(evm.min_balance, expected.min_balance);
207                assert_eq!(evm.private_transactions, expected.private_transactions);
208            } else {
209                panic!("Expected EVM policy");
210            }
211        } else {
212            panic!("Expected EVM policy response");
213        }
214    }
215
216    #[test]
217    fn test_from_relayer_repo_model_solana() {
218        let model = RelayerRepoModel {
219            id: "test-id".to_string(),
220            name: "Test Relayer".to_string(),
221            network: "solana".to_string(),
222            network_type: NetworkType::Solana,
223            paused: true,
224            policies: RelayerNetworkPolicy::Solana(RelayerSolanaPolicy {
225                fee_payment_strategy: SolanaFeePaymentStrategy::User,
226                fee_margin_percentage: Some(0.5),
227                min_balance: 5000,
228                allowed_tokens: Some(vec![SolanaAllowedTokensPolicy {
229                    mint: "mint-address".to_string(),
230                    decimals: Some(9),
231                    symbol: Some("SOL".to_string()),
232                    max_allowed_fee: Some(1000),
233                    swap_config: Some(SolanaAllowedTokensSwapConfig {
234                        slippage_percentage: Some(100.0),
235                        max_amount: None,
236                        min_amount: None,
237                        retain_min_amount: None,
238                    }),
239                }]),
240                allowed_programs: Some(vec!["program1".to_string()]),
241                allowed_accounts: Some(vec!["account1".to_string()]),
242                disallowed_accounts: Some(vec!["bad-account".to_string()]),
243                max_signatures: Some(10),
244                max_tx_data_size: 1024,
245                max_allowed_fee_lamports: Some(10000),
246                swap_config: None,
247            }),
248            address: "solana-address".to_string(),
249            system_disabled: false,
250            signer_id: "test-signer-id".to_string(),
251            notification_id: Some("test-notification-id".to_string()),
252            custom_rpc_urls: None,
253        };
254
255        let response: RelayerResponse = model.clone().into();
256
257        assert_eq!(response.id, model.id);
258        assert_eq!(response.name, model.name);
259        assert_eq!(response.network, model.network);
260        assert_eq!(response.network_type, model.network_type);
261        assert_eq!(response.paused, model.paused);
262        assert_eq!(response.address, model.address);
263        assert_eq!(response.system_disabled, model.system_disabled);
264
265        if let NetworkPolicyResponse::Solana(solana) = response.policies {
266            if let RelayerNetworkPolicy::Solana(expected) = model.policies {
267                assert_eq!(solana.min_balance, expected.min_balance);
268                assert_eq!(solana.allowed_tokens, expected.allowed_tokens);
269                assert_eq!(solana.allowed_programs, expected.allowed_programs);
270                assert_eq!(solana.allowed_accounts, expected.allowed_accounts);
271                assert_eq!(solana.disallowed_accounts, expected.disallowed_accounts);
272                assert_eq!(solana.max_signatures, expected.max_signatures);
273                assert_eq!(solana.max_tx_data_size, expected.max_tx_data_size);
274                assert_eq!(
275                    solana.max_allowed_fee_lamports,
276                    expected.max_allowed_fee_lamports
277                );
278            } else {
279                panic!("Expected Solana policy");
280            }
281        } else {
282            panic!("Expected Solana policy response");
283        }
284    }
285
286    #[test]
287    fn test_from_relayer_repo_model_stellar() {
288        let model = RelayerRepoModel {
289            id: "test-id".to_string(),
290            name: "Test Relayer".to_string(),
291            network: "stellar".to_string(),
292            network_type: NetworkType::Stellar,
293            paused: false,
294            policies: RelayerNetworkPolicy::Stellar(RelayerStellarPolicy {
295                max_fee: Some(200),
296                min_balance: 2000,
297                timeout_seconds: Some(100),
298            }),
299            address: "stellar-address".to_string(),
300            system_disabled: true,
301            signer_id: "test-signer-id".to_string(),
302            notification_id: Some("test-notification-id".to_string()),
303            custom_rpc_urls: None,
304        };
305
306        let response: RelayerResponse = model.clone().into();
307
308        assert_eq!(response.id, model.id);
309        assert_eq!(response.name, model.name);
310        assert_eq!(response.network, model.network);
311        assert_eq!(response.network_type, model.network_type);
312        assert_eq!(response.paused, model.paused);
313        assert_eq!(response.address, model.address);
314        assert_eq!(response.system_disabled, model.system_disabled);
315
316        if let NetworkPolicyResponse::Stellar(stellar) = response.policies {
317            if let RelayerNetworkPolicy::Stellar(expected) = model.policies {
318                assert_eq!(stellar.max_fee, expected.max_fee);
319                assert_eq!(stellar.min_balance, expected.min_balance);
320            } else {
321                panic!("Expected Stellar policy");
322            }
323        } else {
324            panic!("Expected Stellar policy response");
325        }
326    }
327}