evmlib/contract/payment_vault/
handler.rs1use crate::common::{Address, Amount, Calldata, TxHash};
2use crate::contract::payment_vault::error::Error;
3use crate::contract::payment_vault::interface::IPaymentVault;
4use crate::contract::payment_vault::interface::IPaymentVault::IPaymentVaultInstance;
5use crate::merkle_batch_payment::PoolHash;
6use crate::retry::{GasInfo, TransactionError, send_transaction_with_retries};
7use crate::transaction_config::TransactionConfig;
8use alloy::network::{Network, TransactionResponse};
9use alloy::providers::Provider;
10use exponential_backoff::Backoff;
11use std::time::Duration;
12
13pub struct PaymentVaultHandler<P: Provider<N>, N: Network> {
14 pub contract: IPaymentVaultInstance<P, N>,
15}
16
17impl<P, N> PaymentVaultHandler<P, N>
18where
19 P: Provider<N>,
20 N: Network,
21{
22 pub fn new(contract_address: Address, provider: P) -> Self {
24 let contract = IPaymentVault::new(contract_address, provider);
25 Self { contract }
26 }
27
28 pub fn set_provider(&mut self, provider: P) {
30 let address = *self.contract.address();
31 self.contract = IPaymentVault::new(address, provider);
32 }
33
34 pub async fn pay_for_quotes<I: IntoIterator<Item: Into<IPaymentVault::DataPayment>>>(
39 &self,
40 data_payments: I,
41 transaction_config: &TransactionConfig,
42 ) -> Result<(TxHash, GasInfo), Error> {
43 debug!("Paying for quotes.");
44 let (calldata, to) = self.pay_for_quotes_calldata(data_payments)?;
45 send_transaction_with_retries(
46 self.contract.provider(),
47 calldata,
48 to,
49 "pay for quotes",
50 transaction_config,
51 )
52 .await
53 .map_err(Error::from)
54 }
55
56 pub fn pay_for_quotes_calldata<I: IntoIterator<Item: Into<IPaymentVault::DataPayment>>>(
58 &self,
59 data_payments: I,
60 ) -> Result<(Calldata, Address), Error> {
61 let data_payments: Vec<IPaymentVault::DataPayment> =
62 data_payments.into_iter().map(|item| item.into()).collect();
63
64 let calldata = self
65 .contract
66 .payForQuotes(data_payments)
67 .calldata()
68 .to_owned();
69
70 Ok((calldata, *self.contract.address()))
71 }
72
73 pub async fn pay_for_merkle_tree<I, T>(
82 &self,
83 depth: u8,
84 pool_commitments: I,
85 merkle_payment_timestamp: u64,
86 transaction_config: &TransactionConfig,
87 ) -> Result<(PoolHash, Amount, GasInfo), Error>
88 where
89 I: IntoIterator<Item = T>,
90 T: Into<IPaymentVault::PoolCommitment>,
91 {
92 debug!("Paying for Merkle tree: depth={depth}, timestamp={merkle_payment_timestamp}");
93
94 let (calldata, to) =
95 self.pay_for_merkle_tree_calldata(depth, pool_commitments, merkle_payment_timestamp)?;
96
97 let (tx_hash, gas_info) = self
98 .send_transaction_and_handle_errors(calldata, to, transaction_config)
99 .await?;
100
101 let event = self.get_merkle_payment_event(tx_hash).await?;
102
103 let winner_pool_hash = event.winnerPoolHash.0;
104 let total_amount = event.totalAmount;
105
106 debug!(
107 "MerklePaymentMade event: winnerPoolHash={}, depth={}, totalAmount={}, timestamp={}",
108 hex::encode(winner_pool_hash),
109 event.depth,
110 total_amount,
111 event.merklePaymentTimestamp
112 );
113
114 Ok((winner_pool_hash, total_amount, gas_info))
115 }
116
117 pub fn pay_for_merkle_tree_calldata<I, T>(
121 &self,
122 depth: u8,
123 pool_commitments: I,
124 merkle_payment_timestamp: u64,
125 ) -> Result<(Calldata, Address), Error>
126 where
127 I: IntoIterator<Item = T>,
128 T: Into<IPaymentVault::PoolCommitment>,
129 {
130 let pool_commitments: Vec<IPaymentVault::PoolCommitment> =
131 pool_commitments.into_iter().map(Into::into).collect();
132 let calldata =
133 super::encode_merkle_payment(depth, pool_commitments, merkle_payment_timestamp).into();
134
135 Ok((calldata, *self.contract.address()))
136 }
137
138 pub async fn get_completed_merkle_payment(
144 &self,
145 winner_pool_hash: PoolHash,
146 ) -> Result<IPaymentVault::CompletedMerklePayment, Error> {
147 debug!(
148 "Getting completed merkle payment for pool hash: {}",
149 hex::encode(winner_pool_hash)
150 );
151
152 let info = self
153 .contract
154 .getCompletedMerklePayment(winner_pool_hash.into())
155 .call()
156 .await
157 .map_err(Error::Contract)?;
158
159 if info.depth == 0 {
161 return Err(Error::PaymentNotFound(hex::encode(winner_pool_hash)));
162 }
163
164 debug!(
165 "getCompletedMerklePayment returned: depth={}, timestamp={}, paid_nodes={}",
166 info.depth,
167 info.merklePaymentTimestamp,
168 info.paidNodeAddresses.len()
169 );
170
171 Ok(info)
172 }
173
174 pub(crate) async fn get_merkle_payment_event(
181 &self,
182 tx_hash: TxHash,
183 ) -> Result<IPaymentVault::MerklePaymentMade, Error> {
184 const MAX_ATTEMPTS: u32 = 3;
185 const INITIAL_DELAY_MS: u64 = 500;
186 const MAX_DELAY_MS: u64 = 8000;
187
188 let backoff = Backoff::new(
189 MAX_ATTEMPTS,
190 Duration::from_millis(INITIAL_DELAY_MS),
191 Some(Duration::from_millis(MAX_DELAY_MS)),
192 );
193
194 let mut last_error = None;
195 let mut attempt = 1;
196
197 for duration_opt in backoff {
198 match self.try_get_merkle_payment_event(tx_hash).await {
199 Ok(event) => return Ok(event),
200 Err(e) => {
201 last_error = Some(e);
202
203 if let Some(duration) = duration_opt {
204 debug!(
205 "Failed to get MerklePaymentMade event (attempt {}/{}), retrying in {}ms",
206 attempt,
207 MAX_ATTEMPTS,
208 duration.as_millis()
209 );
210 crate::runtime::sleep(duration).await;
211 }
212 attempt += 1;
213 }
214 }
215 }
216
217 Err(last_error.unwrap_or_else(|| {
218 Error::Rpc("Failed to get MerklePaymentMade event after retries".to_string())
219 }))
220 }
221
222 async fn try_get_merkle_payment_event(
224 &self,
225 tx_hash: TxHash,
226 ) -> Result<IPaymentVault::MerklePaymentMade, Error> {
227 let tx = self
228 .contract
229 .provider()
230 .get_transaction_by_hash(tx_hash)
231 .await
232 .map_err(|e| Error::Rpc(format!("Failed to get transaction: {e}")))?
233 .ok_or_else(|| Error::Rpc("Transaction not found".to_string()))?;
234
235 let block_number = tx
236 .block_number()
237 .ok_or_else(|| Error::Rpc("Transaction has no block number".to_string()))?;
238
239 let events = self
240 .contract
241 .MerklePaymentMade_filter()
242 .from_block(block_number)
243 .to_block(block_number)
244 .query()
245 .await
246 .map_err(|e| Error::Rpc(format!("Failed to query MerklePaymentMade events: {e}")))?;
247
248 events
249 .into_iter()
250 .find(|(_, log)| log.transaction_hash == Some(tx_hash))
251 .map(|(event, _)| event)
252 .ok_or_else(|| {
253 Error::Rpc("MerklePaymentMade event not found in transaction".to_string())
254 })
255 }
256
257 async fn send_transaction_and_handle_errors(
259 &self,
260 calldata: Calldata,
261 to: Address,
262 transaction_config: &TransactionConfig,
263 ) -> Result<(TxHash, GasInfo), Error> {
264 let tx_result = crate::retry::send_transaction_with_retries(
265 self.contract.provider(),
266 calldata,
267 to,
268 "pay for merkle tree",
269 transaction_config,
270 )
271 .await;
272
273 match tx_result {
274 Ok((hash, gas_info)) => Ok((hash, gas_info)),
275 Err(TransactionError::TransactionReverted {
276 message,
277 revert_data,
278 nonce,
279 }) => {
280 let error = self.decode_revert_error(message, revert_data, nonce);
281 Err(error)
282 }
283 Err(other_err) => Err(Error::from(other_err)),
284 }
285 }
286
287 fn decode_revert_error(
289 &self,
290 message: String,
291 revert_data: Option<alloy::primitives::Bytes>,
292 nonce: Option<u64>,
293 ) -> Error {
294 if let Some(revert_data_bytes) = &revert_data
295 && let Some(decoded_err) = Error::try_decode_revert(revert_data_bytes)
296 {
297 return decoded_err;
298 }
299
300 Error::Transaction(TransactionError::TransactionReverted {
301 message,
302 revert_data,
303 nonce,
304 })
305 }
306}