agglayer_config/
outbound.rs1use std::time::Duration;
2
3use serde::{Deserialize, Deserializer, Serialize};
4use serde_with::serde_as;
5use tracing::warn;
6
7use crate::Multiplier;
8
9#[derive(Serialize, Default, Debug, Deserialize, PartialEq, Eq)]
13#[serde(rename = "outbound", rename_all = "kebab-case")]
14pub struct OutboundConfig {
15 pub rpc: OutboundRpcConfig,
16}
17
18#[derive(Serialize, Default, Debug, PartialEq, Eq)]
21#[serde(rename = "rpc", rename_all = "kebab-case")]
22pub struct OutboundRpcConfig {
23 pub settle_tx: OutboundRpcSettleConfig,
25 pub settle_cert: OutboundRpcSettleConfig,
26}
27
28impl<'de> Deserialize<'de> for OutboundRpcConfig {
29 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
30 where
31 D: Deserializer<'de>,
32 {
33 #[derive(Deserialize)]
34 #[serde(rename = "rpc", rename_all = "kebab-case")]
35 pub struct Intermediate {
36 pub settle_tx: Option<OutboundRpcSettleConfig>,
37 pub settle_cert: Option<OutboundRpcSettleConfig>,
38 pub settle: Option<OutboundRpcSettleConfig>,
39 }
40
41 let deserialized = Intermediate::deserialize(deserializer)?;
42
43 let (settle_tx, settle_cert) = match (
44 deserialized.settle_tx,
45 deserialized.settle_cert,
46 deserialized.settle,
47 ) {
48 (Some(tx), Some(cert), _) => (tx, cert),
49 (None, None, Some(settle)) => {
50 warn!("'settle' is deprecated. Please use 'settle-tx' and 'settle-cert' instead.");
51 (settle.clone(), settle)
52 }
53 _ => {
54 return Err(serde::de::Error::custom(
55 "Either both ['settle-tx','settle-cert'] or 'settle' must be specified",
56 ));
57 }
58 };
59
60 Ok(OutboundRpcConfig {
61 settle_tx,
62 settle_cert,
63 })
64 }
65}
66
67#[serde_as]
70#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
71#[serde(rename = "settle", rename_all = "kebab-case")]
72pub struct OutboundRpcSettleConfig {
73 #[serde(default = "default_rpc_retries")]
75 pub max_retries: usize,
76
77 #[serde(default = "default_rpc_retry_interval")]
79 #[serde(with = "crate::with::HumanDuration")]
80 pub retry_interval: Duration,
81
82 #[serde(default = "default_rpc_confirmations")]
85 pub confirmations: usize,
86
87 #[serde(
91 default = "default_gas_multiplier_factor",
92 skip_serializing_if = "same_as_default_gas_multiplier_factor"
93 )]
94 pub gas_multiplier_factor: u32,
95
96 #[serde(default)]
98 pub gas_price: GasPriceConfig,
99}
100
101impl Default for OutboundRpcSettleConfig {
102 fn default() -> Self {
103 OutboundRpcSettleConfig {
104 max_retries: default_rpc_retries(),
105 retry_interval: default_rpc_retry_interval(),
106 confirmations: default_rpc_confirmations(),
107 gas_multiplier_factor: default_gas_multiplier_factor(),
108 gas_price: GasPriceConfig::default(),
109 }
110 }
111}
112
113#[serde_as]
115#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
116#[serde(rename_all = "kebab-case")]
117pub struct GasPriceConfig {
118 #[serde(default, skip_serializing_if = "crate::is_default")]
122 pub multiplier: Multiplier,
123
124 #[serde(default, skip_serializing_if = "crate::is_default")]
127 #[serde_as(as = "crate::with::EthAmount")]
128 pub floor: u128,
129
130 #[serde(default = "default_gas_price_ceiling")]
133 #[serde_as(as = "crate::with::EthAmount")]
134 pub ceiling: u128,
135}
136
137impl Default for GasPriceConfig {
138 fn default() -> Self {
139 GasPriceConfig {
140 multiplier: Multiplier::default(),
141 floor: 0,
142 ceiling: default_gas_price_ceiling(),
143 }
144 }
145}
146
147const fn default_gas_multiplier_factor() -> u32 {
149 100
150}
151
152const fn same_as_default_gas_multiplier_factor(v: &u32) -> bool {
153 *v == default_gas_multiplier_factor()
154}
155
156const fn default_rpc_retries() -> usize {
158 30
159}
160
161const fn default_rpc_retry_interval() -> Duration {
163 Duration::from_secs(10)
164}
165
166const fn default_rpc_confirmations() -> usize {
169 12
170}
171
172const fn default_gas_price_ceiling() -> u128 {
174 100_000_000_000_u128
176}
177
178#[cfg(test)]
179mod tests {
180 mod outbound {
181 use serde::Deserialize;
182
183 use crate::outbound::OutboundConfig;
184
185 #[test]
186 #[allow(deprecated)]
187 fn expected_namespace() {
188 #[derive(Debug, Deserialize)]
189 struct DummyContainer {
190 outbound: OutboundConfig,
191 }
192
193 let toml = r#"
194 [outbound.rpc.settle-tx]
195 max-retries = 10
196 [outbound.rpc.settle-cert]
197 max-retries = 11
198 [outbound.rpc.settle]
199 max-retries = 12
200 "#;
201
202 let config = toml::from_str::<DummyContainer>(toml).unwrap();
203
204 assert_eq!(config.outbound.rpc.settle_tx.max_retries, 10);
205 assert_eq!(config.outbound.rpc.settle_cert.max_retries, 11);
206 }
207
208 mod rpc {
209 mod settle {
210 use std::time::Duration;
211
212 use crate::outbound::OutboundRpcSettleConfig;
213
214 #[test]
215 fn test_default() {
216 let toml = r#"
217 "#;
218
219 let config = toml::from_str::<OutboundRpcSettleConfig>(toml).unwrap();
220
221 assert_eq!(config.max_retries, 30);
222 assert_eq!(config.retry_interval, Duration::from_secs(10));
223 assert_eq!(config.confirmations, 12);
224 }
225
226 #[test]
227 fn test_custom() {
228 let toml = r#"
229 max-retries = 10
230 retry-interval = 1
231 confirmations = 5
232 "#;
233
234 let config = toml::from_str::<OutboundRpcSettleConfig>(toml).unwrap();
235
236 assert_eq!(config.max_retries, 10);
237 assert_eq!(config.retry_interval, Duration::from_secs(1));
238 assert_eq!(config.confirmations, 5);
239 }
240 }
241 }
242 }
243}