55for cross-chain message verification using the Hashi protocol format.
66"""
77
8+ import asyncio
89import logging
910from typing import TYPE_CHECKING , Any
1011
2324
2425logger = logging .getLogger (__name__ )
2526
27+ FUTURE_PRICE_TIMESTAMP_ERROR_B64 = "4B8j6Q"
28+ FUTURE_PRICE_RETRY_DELAY = 6 # seconds to wait before retry
29+ FUTURE_PRICE_MAX_RETRIES = 3 # maximum retry attempts
30+
2631
2732class ProofManager :
2833 """Handles proof generation and submission for cross-chain messages."""
@@ -176,16 +181,19 @@ async def generate_proof(self, payment_event: PaymentEvent) -> list[Any]:
176181 )
177182 return proof
178183
179- async def submit_proof (self , proof : list [Any ], paymaster_address : str ) -> str :
184+ async def submit_proof (self , proof : list [Any ], paymaster_address : str ) -> str | None :
180185 """
181186 Submit proof to CrossChainPaymaster contract.
182187
188+ Includes retry logic for FuturePriceTimestamp errors, which occur when
189+ the price oracle's timestamp is slightly ahead of the block timestamp.
190+
183191 Args:
184192 proof: The generated proof array
185193 paymaster_address: Address of the CrossChainPaymaster contract
186194
187195 Returns:
188- Transaction hash of the submission
196+ Transaction hash of the submission, or None if all retries failed
189197 """
190198 logger .info (f"Submitting proof to CrossChainPaymaster at { paymaster_address } " )
191199
@@ -210,7 +218,7 @@ async def submit_proof(self, proof: list[Any], paymaster_address: str) -> str:
210218 )
211219
212220 if self .rofl_util :
213- # ROFL mode: build transaction for rofl_util
221+ # ROFL mode: build transaction for rofl_util with retry logic
214222 tx_params : TxParams = {
215223 "from" : "0x0000000000000000000000000000000000000000" , # ROFL will override
216224 "gas" : 3000000 ,
@@ -220,14 +228,41 @@ async def submit_proof(self, proof: list[Any], paymaster_address: str) -> str:
220228 tx_data = contract .functions .processPayment (
221229 receipt_proof_struct
222230 ).build_transaction (tx_params )
223- success = await self .rofl_util .submit_tx (tx_data )
224- if success :
225- logger .info ("Proof submitted successfully via ROFL" )
226- # Return a success indicator since ROFL doesn't provide tx hash
227- return "ROFL_SUBMITTED"
228- else :
229- logger .error ("Failed to submit proof via ROFL" )
230- raise Exception ("ROFL submission failed" )
231+
232+ # Retry loop for FuturePriceTimestamp errors
233+ for attempt in range (FUTURE_PRICE_MAX_RETRIES ):
234+ try :
235+ success = await self .rofl_util .submit_tx (tx_data )
236+ if success :
237+ logger .info ("Proof submitted successfully via ROFL" )
238+ return "ROFL_SUBMITTED"
239+ else :
240+ logger .error ("Failed to submit proof via ROFL (no success)" )
241+ return None
242+ except Exception as e :
243+ error_msg = str (e )
244+ if FUTURE_PRICE_TIMESTAMP_ERROR_B64 in error_msg :
245+ remaining = FUTURE_PRICE_MAX_RETRIES - attempt - 1
246+ if remaining > 0 :
247+ logger .warning (
248+ f"FuturePriceTimestamp error (oracle ahead of block), "
249+ f"retrying in { FUTURE_PRICE_RETRY_DELAY } s... "
250+ f"({ remaining } retries left)"
251+ )
252+ await asyncio .sleep (FUTURE_PRICE_RETRY_DELAY )
253+ continue
254+ else :
255+ logger .error (
256+ f"FuturePriceTimestamp error persisted after "
257+ f"{ FUTURE_PRICE_MAX_RETRIES } attempts, giving up: { error_msg } "
258+ )
259+ return None
260+ else :
261+ # Non-retryable error
262+ logger .error (f"ROFL submission failed with error: { error_msg } " )
263+ return None
264+
265+ return None
231266 else :
232267 # Local mode
233268 tx_hash = contract .functions .processPayment (receipt_proof_struct ).transact (
0 commit comments