1#![allow(clippy::expect_used)]
11#![allow(clippy::enum_variant_names)]
13
14use crate::common::{Address, Amount};
15use crate::merkle_batch_payment::PoolCommitment;
16use crate::utils::get_evm_network;
17use alloy::primitives::address;
18use alloy::transports::http::reqwest;
19use serde::{Deserialize, Serialize};
20use serde_with::{DisplayFromStr, serde_as};
21use std::str::FromStr;
22use std::sync::LazyLock;
23
24#[macro_use]
25extern crate tracing;
26
27pub mod common;
28pub mod contract;
29pub mod cryptography;
30pub mod data_payments;
31#[cfg(feature = "external-signer")]
32pub mod external_signer;
33pub mod merkle_batch_payment;
34pub mod merkle_payments;
35pub mod quoting_metrics;
36mod retry;
37pub mod testnet;
38pub mod transaction_config;
39pub mod utils;
40pub mod wallet;
41
42pub use retry::GasInfo;
44
45pub use common::Address as RewardsAddress;
47pub use data_payments::{EncodedPeerId, PaymentQuote, ProofOfPayment};
48
49const TX_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(24); static PUBLIC_ARBITRUM_ONE_HTTP_RPC_URL: LazyLock<reqwest::Url> = LazyLock::new(|| {
53 "https://arb1.arbitrum.io/rpc"
54 .parse()
55 .expect("Invalid RPC URL")
56});
57
58static PUBLIC_ARBITRUM_SEPOLIA_HTTP_RPC_URL: LazyLock<reqwest::Url> = LazyLock::new(|| {
59 "https://sepolia-rollup.arbitrum.io/rpc"
60 .parse()
61 .expect("Invalid RPC URL")
62});
63
64const ARBITRUM_ONE_PAYMENT_TOKEN_ADDRESS: Address =
65 address!("a78d8321B20c4Ef90eCd72f2588AA985A4BDb684");
66
67const ARBITRUM_SEPOLIA_TEST_PAYMENT_TOKEN_ADDRESS: Address =
68 address!("4bc1aCE0E66170375462cB4E6Af42Ad4D5EC689C");
69
70const ARBITRUM_ONE_PAYMENT_VAULT_ADDRESS: Address =
72 address!("9A3EcAc693b699Fc0B2B6A50B5549e50c2320A26");
73
74const ARBITRUM_SEPOLIA_TEST_PAYMENT_VAULT_ADDRESS: Address =
76 address!("d742E8CFEf27A9a884F3EFfA239Ee2F39c276522");
77
78#[serde_as]
79#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
80pub struct CustomNetwork {
81 #[serde_as(as = "DisplayFromStr")]
82 pub rpc_url_http: reqwest::Url,
83 pub payment_token_address: Address,
84 pub payment_vault_address: Address,
86}
87
88impl CustomNetwork {
89 pub fn new(rpc_url: &str, payment_token_addr: &str, payment_vault_addr: &str) -> Self {
90 Self {
91 rpc_url_http: reqwest::Url::parse(rpc_url).expect("Invalid RPC URL"),
92 payment_token_address: Address::from_str(payment_token_addr)
93 .expect("Invalid payment token address"),
94 payment_vault_address: Address::from_str(payment_vault_addr)
95 .expect("Invalid payment vault address"),
96 }
97 }
98}
99
100#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
101pub enum Network {
102 #[default]
103 ArbitrumOne,
104 ArbitrumSepoliaTest,
105 Custom(CustomNetwork),
106}
107
108impl std::fmt::Display for Network {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 match self {
111 Network::ArbitrumOne => write!(f, "evm-arbitrum-one"),
112 Network::ArbitrumSepoliaTest => write!(f, "evm-arbitrum-sepolia-test"),
113 Network::Custom(_) => write!(f, "evm-custom"),
114 }
115 }
116}
117
118impl std::str::FromStr for Network {
119 type Err = ();
120
121 fn from_str(s: &str) -> Result<Self, Self::Err> {
122 match s {
123 "evm-arbitrum-one" => Ok(Network::ArbitrumOne),
124 "evm-arbitrum-sepolia-test" => Ok(Network::ArbitrumSepoliaTest),
125 _ => Err(()),
126 }
127 }
128}
129
130impl Network {
131 pub fn new(local: bool) -> Result<Self, utils::Error> {
132 get_evm_network(local, None).inspect_err(|err| {
133 warn!("Failed to select EVM network from ENV: {err}");
134 })
135 }
136
137 pub fn new_custom(rpc_url: &str, payment_token_addr: &str, payment_vault_addr: &str) -> Self {
138 Self::Custom(CustomNetwork::new(
139 rpc_url,
140 payment_token_addr,
141 payment_vault_addr,
142 ))
143 }
144
145 pub fn identifier(&self) -> &str {
146 match self {
147 Network::ArbitrumOne => "arbitrum-one",
148 Network::ArbitrumSepoliaTest => "arbitrum-sepolia-test",
149 Network::Custom(_) => "custom",
150 }
151 }
152
153 pub fn rpc_url(&self) -> &reqwest::Url {
154 match self {
155 Network::ArbitrumOne => &PUBLIC_ARBITRUM_ONE_HTTP_RPC_URL,
156 Network::ArbitrumSepoliaTest => &PUBLIC_ARBITRUM_SEPOLIA_HTTP_RPC_URL,
157 Network::Custom(custom) => &custom.rpc_url_http,
158 }
159 }
160
161 pub fn payment_token_address(&self) -> &Address {
162 match self {
163 Network::ArbitrumOne => &ARBITRUM_ONE_PAYMENT_TOKEN_ADDRESS,
164 Network::ArbitrumSepoliaTest => &ARBITRUM_SEPOLIA_TEST_PAYMENT_TOKEN_ADDRESS,
165 Network::Custom(custom) => &custom.payment_token_address,
166 }
167 }
168
169 pub fn payment_vault_address(&self) -> &Address {
171 match self {
172 Network::ArbitrumOne => &ARBITRUM_ONE_PAYMENT_VAULT_ADDRESS,
173 Network::ArbitrumSepoliaTest => &ARBITRUM_SEPOLIA_TEST_PAYMENT_VAULT_ADDRESS,
174 Network::Custom(custom) => &custom.payment_vault_address,
175 }
176 }
177
178 pub fn estimate_merkle_payment_cost(
194 &self,
195 depth: u8,
196 pool_commitments: &[PoolCommitment],
197 ) -> Amount {
198 if pool_commitments.is_empty() {
199 return Amount::ZERO;
200 }
201
202 let multiplier = Amount::from(1u64 << depth);
203 pool_commitments
204 .iter()
205 .map(|pool| {
206 let mut prices: Vec<Amount> = pool.candidates.iter().map(|c| c.price).collect();
207 prices.sort_unstable(); prices[prices.len() / 2] * multiplier
210 })
211 .max()
212 .unwrap_or(Amount::ZERO)
213 }
214}