diff --git a/bot/src/bot.rs b/bot/src/bot.rs index bcef35b..33807a5 100644 --- a/bot/src/bot.rs +++ b/bot/src/bot.rs @@ -25,9 +25,11 @@ use starknet_id::encode; use tokio::time::{sleep, Duration as TokioDuration}; use crate::logger::Logger; +use crate::models::TxResult; use crate::models::{ AggregateResult, AggregateResults, DomainAggregateResult, MetadataDoc, Unzip5, }; +use crate::starknet_utils::check_pending_transactions; use crate::starknet_utils::create_jsonrpc_client; use crate::starknetid_utils::get_renewal_price; use crate::utils::{from_uint256, hex_to_bigdecimal, to_uint256}; @@ -383,6 +385,8 @@ pub async fn renew_domains( aggregate_results.domains.len() )); let mut nonce = account.get_nonce().await.unwrap(); + let mut tx_results = Vec::::new(); + // If we have: i32 more than 75 domains to renew we make multiple transactions to avoid hitting the 3M steps limit while !aggregate_results.domains.is_empty() && !aggregate_results.renewers.is_empty() @@ -420,6 +424,13 @@ pub async fn renew_domains( domains_to_renew.len(), nonce, )); + tx_results.push(TxResult { + tx_hash, + reverted: None, + revert_reason: None, + domains_renewed: domains_to_renew.len(), + }); + // We only inscrease nonce if no error occurred in the previous transaction nonce += FieldElement::ONE; } @@ -434,12 +445,42 @@ pub async fn renew_domains( } else { logger.severe(format!( "Error while renewing domains: {:?} for domains: {:?}", - e, domains_to_renew + e, + domains_to_renew.len() )); return Err(e); } } } + + check_pending_transactions(config, &mut tx_results).await; + + let filtered_results: Vec<&TxResult> = tx_results + .iter() + .filter(|tx| tx.reverted.is_some()) + .collect(); + + let failed_count = filtered_results + .iter() + .rev() + .take(3) + .filter(|tx| tx.reverted == Some(true)) + .count(); + + // If 3 transactions have failed, we stop the process + if failed_count == 3 { + logger.severe("The last 3 transactions have failed. Stopping process."); + logger.info(format!("Sent {:?} transactions", tx_results.len())); + filtered_results.iter().rev().take(3).for_each(|failure| { + logger.severe(format!( + "Transaction 0x{:x} with {:?} domains has failed with reason: {:?}", + failure.tx_hash, failure.domains_renewed, failure.revert_reason + )); + }); + logger.severe("Stopping process."); + break; + } + println!("Waiting for 1 minute before sending the next transaction..."); sleep(TokioDuration::from_secs(60)).await; } diff --git a/bot/src/models.rs b/bot/src/models.rs index b6c6539..ad5d3a2 100644 --- a/bot/src/models.rs +++ b/bot/src/models.rs @@ -4,7 +4,7 @@ use bigdecimal::BigDecimal; use bson::DateTime; use mongodb::Database; use serde::{Deserialize, Serialize}; -use starknet::core::types::FieldElement; +use starknet::core::types::{FieldElement, TransactionExecutionStatus}; pub struct AppState { pub db: Database, @@ -90,6 +90,14 @@ pub struct States { pub states: HashMap, } +#[derive(Debug, Clone)] +pub struct TxResult { + pub tx_hash: FieldElement, + pub reverted: Option, + pub revert_reason: Option, + pub domains_renewed: usize, +} + pub trait Unzip5 { type A; type B; diff --git a/bot/src/starknet_utils.rs b/bot/src/starknet_utils.rs index dc36d66..1bba5e3 100644 --- a/bot/src/starknet_utils.rs +++ b/bot/src/starknet_utils.rs @@ -1,7 +1,63 @@ -use crate::config::Config; -use starknet::providers::{jsonrpc::HttpTransport, JsonRpcClient}; +use crate::{config::Config, models::TxResult}; +use starknet::{ + core::types::{ + MaybePendingTransactionReceipt, PendingTransactionReceipt, TransactionExecutionStatus, + TransactionReceipt, + }, + providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider}, +}; use url::Url; pub fn create_jsonrpc_client(conf: &Config) -> JsonRpcClient { JsonRpcClient::new(HttpTransport::new(Url::parse(&conf.rpc.rpc_url).unwrap())) } + +pub async fn check_pending_transactions(conf: &Config, tx_results: &mut Vec) { + let client = create_jsonrpc_client(conf); + for tx_result in tx_results.iter_mut() { + if tx_result.reverted.is_none() { + match client.get_transaction_receipt(tx_result.tx_hash).await { + Ok(receipt) => match receipt { + MaybePendingTransactionReceipt::PendingReceipt(pending_receipt) => { + if let PendingTransactionReceipt::Invoke(invocation) = pending_receipt { + match invocation.execution_result.status() { + TransactionExecutionStatus::Succeeded => { + tx_result.reverted = Some(false); + } + TransactionExecutionStatus::Reverted => { + tx_result.reverted = Some(true); + tx_result.revert_reason = invocation + .execution_result + .revert_reason() + .map(|s| s.to_owned()); + } + } + } + } + MaybePendingTransactionReceipt::Receipt(receipt) => { + if let TransactionReceipt::Invoke(invocation) = receipt { + match invocation.execution_result.status() { + TransactionExecutionStatus::Succeeded => { + tx_result.reverted = Some(false); + } + TransactionExecutionStatus::Reverted => { + tx_result.reverted = Some(true); + tx_result.revert_reason = invocation + .execution_result + .revert_reason() + .map(|s| s.to_owned()); + } + } + } + } + }, + Err(e) => { + eprintln!( + "Error checking status for tx_hash {}: {}", + tx_result.tx_hash, e + ); + } + } + } + } +}