Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions crates/indexer/engine/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ impl<P: Provider + Send + Sync + Clone + std::fmt::Debug + 'static> Engine<P> {
*block_number,
block.timestamp,
&tx.transaction,
&tx.receipt,
cursors,
is_at_head,
)
Expand Down Expand Up @@ -391,6 +392,7 @@ impl<P: Provider + Send + Sync + Clone + std::fmt::Debug + 'static> Engine<P> {
data.block_number,
data.timestamp,
&tx.transaction,
&tx.receipt,
cursors,
is_at_head,
)
Expand All @@ -414,6 +416,7 @@ impl<P: Provider + Send + Sync + Clone + std::fmt::Debug + 'static> Engine<P> {
block_number: u64,
block_timestamp: u64,
transaction: &Option<TransactionContent>,
receipt: &Option<torii_indexer_fetcher::ReceiptData>,
cursors: &HashMap<Felt, ContractType>,
is_at_head: bool,
) -> Result<(), ProcessError> {
Expand Down Expand Up @@ -472,6 +475,11 @@ impl<P: Provider + Send + Sync + Clone + std::fmt::Debug + 'static> Engine<P> {
.await?;
}

// Process transaction receipt if available
if let Some(receipt_data) = receipt {
self.process_receipt(receipt_data).await?;
}

Ok(())
}

Expand All @@ -495,6 +503,58 @@ impl<P: Provider + Send + Sync + Clone + std::fmt::Debug + 'static> Engine<P> {
Ok(())
}

async fn process_receipt(
&mut self,
receipt: &torii_indexer_fetcher::ReceiptData,
) -> Result<(), ProcessError> {
use starknet::core::types::{TransactionExecutionStatus, TransactionFinalityStatus, PriceUnit};

// Serialize execution resources to JSON
let execution_resources_json = serde_json::to_string(&receipt.execution_resources)
.unwrap_or_else(|_| "{}".to_string());

// Convert execution status to string
let execution_status = match receipt.execution_status {
TransactionExecutionStatus::Succeeded => "SUCCEEDED",
TransactionExecutionStatus::Reverted => "REVERTED",
};

// Convert finality status to string
let finality_status = match receipt.finality_status {
TransactionFinalityStatus::AcceptedOnL2 => "ACCEPTED_ON_L2",
TransactionFinalityStatus::AcceptedOnL1 => "ACCEPTED_ON_L1",
TransactionFinalityStatus::PreConfirmed => "PRE_CONFIRMED",
};

// Convert price unit to string
let actual_fee_unit = match receipt.actual_fee_unit {
PriceUnit::Wei => "WEI",
PriceUnit::Fri => "FRI",
};

self.storage
.store_transaction_receipt(
receipt.transaction_hash,
receipt.actual_fee_amount,
actual_fee_unit,
execution_status,
finality_status,
receipt.revert_reason.as_deref(),
&execution_resources_json,
receipt.block_hash,
receipt.block_number,
)
.await?;

trace!(
target: LOG_TARGET,
transaction_hash = %format!("{:#x}", receipt.transaction_hash),
"Processed transaction receipt."
);

Ok(())
}

async fn process_transaction(
&mut self,
block_number: u64,
Expand Down
72 changes: 71 additions & 1 deletion crates/indexer/fetcher/src/json_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use tracing::{debug, error, trace, warn};
use crate::error::Error;
use crate::{
Cursors, FetchPreconfirmedBlockResult, FetchRangeBlock, FetchRangeResult, FetchResult,
FetchTransaction, FetcherConfig, FetchingFlags,
FetchTransaction, FetcherConfig, FetchingFlags, ReceiptData,
};

pub(crate) const LOG_TARGET: &str = "torii::indexer::fetcher";
Expand Down Expand Up @@ -191,6 +191,7 @@ impl<P: Provider + Send + Sync + Clone + std::fmt::Debug + 'static> Fetcher<P> {
*tx_hash,
FetchTransaction {
transaction: None,
receipt: None,
events: vec![],
},
)
Expand Down Expand Up @@ -318,6 +319,72 @@ impl<P: Provider + Send + Sync + Clone + std::fmt::Debug + 'static> Fetcher<P> {
))
}

fn extract_receipt_data(
tx_with_receipt: &starknet::core::types::TransactionWithReceipt,
block_hash: Felt,
block_number: u64,
) -> ReceiptData {
use starknet::core::types::TransactionReceipt;

let receipt = &tx_with_receipt.receipt;

// Extract common fields from the receipt enum
let (actual_fee_amount, actual_fee_unit, execution_status, finality_status, revert_reason, execution_resources) = match receipt {
TransactionReceipt::Invoke(r) => (
r.actual_fee.amount,
r.actual_fee.unit,
r.execution_result.status(),
r.finality_status,
r.execution_result.revert_reason().map(|s| s.to_string()),
r.execution_resources.clone(),
),
TransactionReceipt::L1Handler(r) => (
r.actual_fee.amount,
r.actual_fee.unit,
r.execution_result.status(),
r.finality_status,
r.execution_result.revert_reason().map(|s| s.to_string()),
r.execution_resources.clone(),
),
TransactionReceipt::Declare(r) => (
r.actual_fee.amount,
r.actual_fee.unit,
r.execution_result.status(),
r.finality_status,
r.execution_result.revert_reason().map(|s| s.to_string()),
r.execution_resources.clone(),
),
TransactionReceipt::Deploy(r) => (
r.actual_fee.amount,
r.actual_fee.unit,
r.execution_result.status(),
r.finality_status,
r.execution_result.revert_reason().map(|s| s.to_string()),
r.execution_resources.clone(),
),
TransactionReceipt::DeployAccount(r) => (
r.actual_fee.amount,
r.actual_fee.unit,
r.execution_result.status(),
r.finality_status,
r.execution_result.revert_reason().map(|s| s.to_string()),
r.execution_resources.clone(),
),
};

ReceiptData {
transaction_hash: *receipt.transaction_hash(),
actual_fee_amount,
actual_fee_unit,
execution_status,
finality_status,
revert_reason,
execution_resources,
block_hash,
block_number,
}
}

async fn fetch_preconfirmed_block(
&self,
latest_block_number: u64,
Expand Down Expand Up @@ -382,12 +449,15 @@ impl<P: Provider + Send + Sync + Clone + std::fmt::Debug + 'static> Fetcher<P> {
"Processing preconfirmed block transactions"
);

// For preconfirmed blocks, the block hash is not yet finalized, use zero as placeholder
let block_hash = Felt::ZERO;
let mut transactions: IndexMap<Felt, FetchTransaction> =
IndexMap::from_iter(preconf_block.transactions.iter().map(|t| {
(
*t.receipt.transaction_hash(),
FetchTransaction {
transaction: Some(t.transaction.clone()),
receipt: Some(Self::extract_receipt_data(t, block_hash, block_number)),
events: vec![],
},
)
Expand Down
19 changes: 18 additions & 1 deletion crates/indexer/fetcher/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ pub use error::Error;
pub mod json_rpc;
use bitflags::bitflags;
pub use json_rpc::Fetcher;
use starknet::core::types::{Event, TransactionContent};
use starknet::core::types::{
Event, ExecutionResources, PriceUnit, TransactionContent, TransactionExecutionStatus,
TransactionFinalityStatus,
};
use starknet_crypto::Felt;
use torii_storage::proto::ContractCursor;

Expand Down Expand Up @@ -58,9 +61,23 @@ pub struct FetchTransaction {
// this is Some if the transactions indexing flag
// is enabled
pub transaction: Option<TransactionContent>,
pub receipt: Option<ReceiptData>,
pub events: Vec<Event>,
}

#[derive(Debug, Clone)]
pub struct ReceiptData {
pub transaction_hash: Felt,
pub actual_fee_amount: Felt,
pub actual_fee_unit: PriceUnit,
pub execution_status: TransactionExecutionStatus,
pub finality_status: TransactionFinalityStatus,
pub revert_reason: Option<String>,
pub execution_resources: ExecutionResources,
pub block_hash: Felt,
pub block_number: u64,
}

#[derive(Debug, Clone)]
pub struct FetchRangeResult {
// block_number -> block and transactions
Expand Down
22 changes: 22 additions & 0 deletions crates/migrations/20251124074446_recreate_transaction_receipts.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- Drop the old unused transaction_receipts table
DROP TABLE IF EXISTS transaction_receipts;

-- Recreate transaction_receipts table with comprehensive fields
CREATE TABLE transaction_receipts (
id TEXT NOT NULL PRIMARY KEY,
transaction_hash TEXT NOT NULL,
actual_fee_amount TEXT NOT NULL,
actual_fee_unit TEXT NOT NULL,
execution_status TEXT NOT NULL,
finality_status TEXT NOT NULL,
revert_reason TEXT,
execution_resources TEXT NOT NULL,
block_hash TEXT NOT NULL,
block_number INTEGER NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE (transaction_hash)
);

-- Add index on block_number for efficient queries
CREATE INDEX idx_transaction_receipts_block_number ON transaction_receipts(block_number);

52 changes: 52 additions & 0 deletions crates/sqlite/sqlite/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2201,6 +2201,58 @@ impl Storage for Sql {
Ok(())
}

/// Stores a transaction receipt with the storage.
async fn store_transaction_receipt(
&self,
transaction_hash: Felt,
actual_fee_amount: Felt,
actual_fee_unit: &str,
execution_status: &str,
finality_status: &str,
revert_reason: Option<&str>,
execution_resources_json: &str,
block_hash: Felt,
block_number: u64,
) -> Result<(), StorageError> {
// Store the transaction receipt in the transaction_receipts table
self.executor
.send(QueryMessage::new(
"INSERT INTO transaction_receipts (id, transaction_hash, actual_fee_amount, \
actual_fee_unit, execution_status, finality_status, revert_reason, \
execution_resources, block_hash, block_number) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) \
ON CONFLICT(transaction_hash) DO UPDATE SET \
actual_fee_amount=excluded.actual_fee_amount, \
actual_fee_unit=excluded.actual_fee_unit, \
execution_status=excluded.execution_status, \
finality_status=excluded.finality_status, \
revert_reason=excluded.revert_reason, \
execution_resources=excluded.execution_resources, \
block_hash=excluded.block_hash, \
block_number=excluded.block_number"
.to_string(),
vec![
Argument::FieldElement(transaction_hash),
Argument::FieldElement(transaction_hash),
Argument::FieldElement(actual_fee_amount),
Argument::String(actual_fee_unit.to_string()),
Argument::String(execution_status.to_string()),
Argument::String(finality_status.to_string()),
if let Some(reason) = revert_reason {
Argument::String(reason.to_string())
} else {
Argument::Null
},
Argument::String(execution_resources_json.to_string()),
Argument::FieldElement(block_hash),
Argument::String(block_number.to_string()),
],
QueryType::Other,
))
.map_err(|e| Error::ExecutorQuery(Box::new(ExecutorQueryError::SendError(Box::new(e)))))?;

Ok(())
}

/// Stores an event with the storage.
async fn store_event(
&self,
Expand Down
16 changes: 16 additions & 0 deletions crates/storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,22 @@ pub trait Storage: ReadOnlyStorage + Send + Sync + Debug {
unique_models: &HashSet<Felt>,
) -> Result<(), StorageError>;

/// Stores a transaction receipt with the storage.
/// It should insert or update the receipt if it already exists.
#[allow(clippy::too_many_arguments)]
async fn store_transaction_receipt(
&self,
transaction_hash: Felt,
actual_fee_amount: Felt,
actual_fee_unit: &str,
execution_status: &str,
finality_status: &str,
revert_reason: Option<&str>,
execution_resources_json: &str,
block_hash: Felt,
block_number: u64,
) -> Result<(), StorageError>;

/// Stores an event with the storage.
async fn store_event(
&self,
Expand Down
Loading