diff --git a/CHANGELOG.md b/CHANGELOG.md index 58b824fe261..7d86a513773 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,26 @@ This changelog covers Firehose-specific changes only. For upstream reth changes, The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## Unreleased + +### Added + +- `RootJournalReasons`, set per transaction with `FirehoseInspectorApi::set_root_journal_reasons`, + for transactions whose fees and protocol writes are journal writes made outside their calls + (Base's account abstraction transactions, type `0x79`). For such a transaction the inspector + records the state written before the root call (balances, nonces, code, storage and logs) inside + the root call, gives the balance changes read outside the calls below the root call the + configured reasons, and synthesizes no gas purchase, refund or fee reward of its own. +- The mapper accepts type `0x79` transactions (`firehose-tracer` 5.7.0 + `TxType::AccountAbstraction`). The chain's `PreTxAdjust` fills their + `account_abstraction_trx_input`. + +### Changed + +- `PreTxAdjust::adjust_tx_event` receives the transaction environment about to execute. The EVM + context does not hold it yet when the hook runs, so a chain could not read transaction fields the + envelope-derived event does not carry. + ## base-v2.5.2.8-fh3.1 ### Added diff --git a/Cargo.lock b/Cargo.lock index 183cdd4767c..987ffad7152 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1030,7 +1030,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1041,7 +1041,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1975,7 +1975,7 @@ version = "3.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dee98b0db6a962de883bf5d20362dee4d7ca0d12fe39a7c6c73c844e1cd7c1f" dependencies = [ - "darling 0.23.0", + "darling 0.21.3", "ident_case", "prettyplease", "proc-macro2", @@ -3437,7 +3437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3969,9 +3969,8 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "firehose-tracer" -version = "5.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a53eb86c6d17bd4e5517e46f56498d33d128d0641d78d15b86cf2c0605c814ee" +version = "5.7.0" +source = "git+https://github.com/streamingfast/evm-firehose-tracer-rs.git?branch=feature%2Faa-transaction-type#2bd8bd6a983fcd76461833346cd1cdc0c8122125" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5206,7 +5205,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -6283,7 +6282,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -11482,7 +11481,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -11562,7 +11561,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs 1.0.7", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -12195,7 +12194,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -12454,7 +12453,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -13723,7 +13722,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0e368da7ace..359c751aa3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -708,7 +708,7 @@ vergen-git2 = "10.0.1" ipnet = "2.11" ## Firehose Additions -firehose-tracer = "5.5.0" +firehose-tracer = { git = "https://github.com/streamingfast/evm-firehose-tracer-rs.git", branch = "feature/aa-transaction-type" } reth-firehose = { path = "crates/firehose" } reth-firehose-tests = { path = "crates/firehose-tests" } diff --git a/crates/firehose/src/executor.rs b/crates/firehose/src/executor.rs index 444ab5b8b93..907c4f98271 100644 --- a/crates/firehose/src/executor.rs +++ b/crates/firehose/src/executor.rs @@ -113,12 +113,14 @@ where E: reth_evm::Evm, E::Inspector: FirehoseInspectorApi, { - /// Mutate `tx_event` in place. `sender` is the recovered signer; `evm` exposes pre-tx state - /// for DB reads. Implementations should leave the event untouched for tx types they do not - /// care about. + /// Mutate `tx_event` in place. `tx_env` is the transaction about to execute, before the EVM + /// context holds it; `sender` is the recovered signer; `evm` exposes pre-tx state for DB + /// reads. Implementations should leave the event untouched for tx types they do not care + /// about. fn adjust_tx_event( &self, evm: &mut E, + tx_env: &E::Tx, tx_event: &mut firehose_tracer::types::TxEvent, sender: Address, ); @@ -138,6 +140,7 @@ where fn adjust_tx_event( &self, _evm: &mut E, + _tx_env: &E::Tx, _tx_event: &mut firehose_tracer::types::TxEvent, _sender: Address, ) { @@ -379,7 +382,7 @@ where // Chain-specific pre-tx patching of the event (e.g. OP Stack deposit nonce override). // Runs after the envelope-derived mapper and before `on_tx_start` so the tracer sees the // final, chain-consistent event. - self.adjust.adjust_tx_event(self.inner.evm_mut(), &mut tx_event, sender); + self.adjust.adjust_tx_event(self.inner.evm_mut(), &tx_env, &mut tx_event, sender); self.inner.evm_mut().inspector_mut().tracer_mut().on_tx_start(tx_event, None); diff --git a/crates/firehose/src/inspector.rs b/crates/firehose/src/inspector.rs index 8d96dfb0c18..0dca1ff0cac 100644 --- a/crates/firehose/src/inspector.rs +++ b/crates/firehose/src/inspector.rs @@ -39,6 +39,44 @@ struct StepContext { keccak_preimage: Option<(B256, Vec)>, } +/// Balance change reasons for a transaction whose fees and protocol writes are journal writes +/// made outside its calls, rather than revm's handler charging the caller and rewarding the +/// beneficiary. Base's account abstraction transactions (type `0x79`) work this way: their +/// executor charges a payer, applies account changes and increments nonces before the root call +/// starts, and refunds the payer and credits the fee recipients after the last call returns. +/// +/// Set with [`FirehoseInspector::set_root_journal_reasons`] before the transaction starts. For +/// that transaction the inspector: +/// +/// - emits the state written before the root call (balances, nonces, code, storage and logs) inside +/// the root call, read from the journal, instead of deriving the caller's gas purchase and nonce +/// increment; +/// - gives each balance change read from the journal outside the calls below the root call the +/// reason listed here for its account and direction, and `Transfer` otherwise; +/// - emits no gas refund or fee reward of its own after the transaction, since the journal already +/// holds them. +#[derive(Debug, Clone, Default)] +pub struct RootJournalReasons { + /// Reason of a balance decrease, by account. + pub decreases: Vec<(Address, pb::sf::ethereum::r#type::v2::balance_change::Reason)>, + /// Reason of a balance increase, by account. The first entry of an account applies. + pub increases: Vec<(Address, pb::sf::ethereum::r#type::v2::balance_change::Reason)>, +} + +impl RootJournalReasons { + fn reason( + &self, + address: Address, + increase: bool, + ) -> pb::sf::ethereum::r#type::v2::balance_change::Reason { + let reasons = if increase { &self.increases } else { &self.decreases }; + reasons + .iter() + .find(|(account, _)| *account == address) + .map_or(pb::sf::ethereum::r#type::v2::balance_change::Reason::Transfer, |(_, r)| *r) + } +} + /// FirehoseInspector captures execution traces for the Firehose format /// It hooks into EVM execution via the Inspector trait to build a complete call tree pub struct FirehoseInspector<'a> { @@ -120,6 +158,15 @@ pub struct FirehoseInspector<'a> { /// follow the same pattern from its own `PreTxAdjust`. root_balance_reason_override: Option, + /// Set for a transaction whose fees and protocol writes are journal writes made outside its + /// calls, see [`RootJournalReasons`]. Cleared by `process_post_tx_balance_changes`. + root_journal_reasons: Option, + + /// While `root_journal_reasons` is set, the journal entries up to `journal_processed_up_to`, + /// used to detect a rollback the inspector did not see (see + /// `rewind_to_processed_journal_prefix`). + root_journal_processed: Vec, + // increments at the end of each transaction, to get proper block index for logs log_block_index: u32, @@ -168,6 +215,8 @@ impl<'a> FirehoseInspector<'a> { tx_journal_snapshot: Vec::new(), tx_post_pre_exec_sender_balance: None, root_balance_reason_override: None, + root_journal_reasons: None, + root_journal_processed: Vec::new(), log_block_index: 0, trx_logs_count: 0, storage_processed_up_to: 0, @@ -190,6 +239,25 @@ impl<'a> FirehoseInspector<'a> { self.root_balance_reason_override = Some(reason); } + /// Records the next transaction's state from the journal around its root call, with the + /// given balance change reasons. See [`RootJournalReasons`]. Intended for chain-specific + /// `PreTxAdjust` impls. + pub fn set_root_journal_reasons(&mut self, reasons: RootJournalReasons) { + self.root_journal_reasons = Some(reasons); + } + + /// Emits the state the transaction wrote before its root call, read from the journal, inside + /// the root call that was just entered. + fn replay_pre_root_journal(&mut self, context: &mut CTX) + where + CTX: ContextTr, + CTX::Journal: JournalExt, + { + self.process_journal_changes(context); + self.gather_precompile_storage_changes(context); + self.gather_precompile_logs(context); + } + /// Capture KECCAK256 preimage from the interpreter state. /// /// Called from `step` before the opcode executes. The stack still holds @@ -270,6 +338,9 @@ impl<'a> FirehoseInspector<'a> { if self.journal_processed_up_to > journal_len { self.journal_processed_up_to = journal_len; } + if self.root_journal_reasons.is_some() { + self.rewind_to_processed_journal_prefix(context); + } if self.journal_processed_up_to == journal_len { return; @@ -283,8 +354,15 @@ impl<'a> FirehoseInspector<'a> { .cloned() .collect(); self.journal_processed_up_to = journal_len; + if self.root_journal_reasons.is_some() { + self.root_journal_processed.extend(entries.iter().cloned()); + } let reason = pb::sf::ethereum::r#type::v2::balance_change::Reason::Transfer; + // Entries read outside the calls below the root call carry the transaction's own + // reasons when it records its fees from the journal. + let root_reasons = + self.root_journal_reasons.as_ref().filter(|_| context.journal().depth() == 0); for entry in entries { match entry { @@ -296,6 +374,9 @@ impl<'a> FirehoseInspector<'a> { .map(|a| a.info.balance) .unwrap_or(U256::ZERO); if old_balance != new_balance { + let reason = root_reasons.map_or(reason, |reasons| { + reasons.reason(address, new_balance > old_balance) + }); self.tracer.on_balance_change(address, old_balance, new_balance, reason); } } @@ -578,6 +659,19 @@ impl<'a> FirehoseInspector<'a> { return; } + // The journal written before the root call is replayed inside it once it is entered + // (see `replay_pre_root_journal`), in place of the sender's gas purchase derived below. + if self.root_journal_reasons.is_some() { + self.tx_journal_snapshot.clear(); + // The block's system calls leave these positions behind; this transaction's + // journal starts empty. + self.journal_processed_up_to = 0; + self.storage_processed_up_to = 0; + self.trx_logs_count = 0; + self.root_journal_processed.clear(); + return; + } + self.journal_processed_up_to = context.journal().journal().len(); // Clear the previous tx's journal snapshot at the start of this tx. It is re-seeded @@ -754,6 +848,34 @@ impl<'a> FirehoseInspector<'a> { // Advance past the selfdestruct journal entries so process_journal_changes // doesn't re-emit them with wrong reasons. self.journal_processed_up_to = context.journal().journal().len(); + if self.root_journal_reasons.is_some() { + let processed = self.root_journal_processed.len(); + self.root_journal_processed + .extend(context.journal().journal()[processed..].iter().cloned()); + } + } + + /// Moves `journal_processed_up_to` back to the end of the longest journal prefix that still + /// holds the entries already processed. + /// + /// revm truncates the journal of a failed call before the inspector's `call_end`, so + /// clamping the watermark to the journal length is enough for ordinary transactions. A + /// transaction recording its fees from the journal can also roll back a group of calls after + /// they returned (Base's account abstraction phases) and then append new entries outside any + /// call. The journal can then be longer than the watermark while its tail past the rollback + /// point holds entries never processed, which the length alone does not reveal. + fn rewind_to_processed_journal_prefix(&mut self, context: &mut CTX) + where + CTX: ContextTr, + CTX::Journal: JournalExt, + { + let journal = context.journal().journal(); + let mut prefix = self.journal_processed_up_to.min(self.root_journal_processed.len()); + while prefix > 0 && journal[prefix - 1] != self.root_journal_processed[prefix - 1] { + prefix -= 1; + } + self.journal_processed_up_to = prefix; + self.root_journal_processed.truncate(prefix); } /// Process EIP-7702 auth list delegations that occur during pre-execution. @@ -1085,6 +1207,14 @@ impl<'a> FirehoseInspector<'a> { { use pb::sf::ethereum::r#type::v2::balance_change::Reason; + // A transaction that records its fees from the journal already emitted its refund and + // fee payouts at its root call exit. + if self.root_journal_reasons.take().is_some() { + self.tx_post_pre_exec_sender_balance = None; + self.finish_tx_post_processing(committed_log_count); + return; + } + let gas_buy_cost = U256::from(gas_limit) * U256::from(effective_gas_price); let remaining_gas = gas_limit.saturating_sub(gas_used); let refund_amount = U256::from(remaining_gas) * U256::from(effective_gas_price); @@ -1146,6 +1276,14 @@ impl<'a> FirehoseInspector<'a> { ); } + self.finish_tx_post_processing(committed_log_count); + } + + /// Emits the self-destruct cleanups and resets the per-transaction state, the part of + /// `process_post_tx_balance_changes` that every transaction needs. + fn finish_tx_post_processing(&mut self, committed_log_count: u32) { + self.root_journal_processed.clear(); + // Emit nonce reset and code clearing for self-destructed accounts. // This matches Geth 1.17.x's Finalise() ordering: nonce/code cleanup happens // AFTER gas refund and coinbase reward, so ordinals are sequenced correctly. @@ -1396,6 +1534,10 @@ where inputs.value.get(), ); + if depth == 0 && self.root_journal_reasons.is_some() { + self.replay_pre_root_journal(context); + } + // EIP-7702: override address_delegates_to using the live EVM state. // on_call_enter uses the pre-block state reader which misses delegations committed // by earlier transactions in the same block. At call-hook time, first_frame_input @@ -1680,6 +1822,10 @@ pub trait FirehoseInspectorApi { reason: pb::sf::ethereum::r#type::v2::balance_change::Reason, ); + /// Records the next transaction's state from the journal around its root call. See + /// [`RootJournalReasons`]. + fn set_root_journal_reasons(&mut self, reasons: RootJournalReasons); + /// Type-erased version of [`FirehoseInspector::process_post_tx_balance_changes`]. /// /// `get_pre_tx_balance` is passed as a trait object so the call site does not need to be @@ -1722,6 +1868,10 @@ impl<'a> FirehoseInspectorApi for FirehoseInspector<'a> { Self::set_root_balance_reason(self, reason); } + fn set_root_journal_reasons(&mut self, reasons: RootJournalReasons) { + Self::set_root_journal_reasons(self, reasons); + } + fn process_post_tx_balance_changes_erased( &mut self, sender: Address, @@ -2433,6 +2583,215 @@ mod tests { ); } + /// A transaction recording its fees from the journal (Base's account abstraction type) gets + /// the payer debit, storage write and log made before its root call inside that call, its + /// settlement credits with the configured reasons, and no synthesized refund or tip. + #[test] + fn root_journal_reasons_record_writes_around_the_root_call() { + use pb::sf::ethereum::r#type::v2::balance_change::Reason; + + let block = decode_fire_block(&drive_root_journal_transaction()); + let trx = block.transaction_traces.first().expect("one transaction"); + let root = trx.calls.first().expect("root call"); + + let balance_changes: Vec<_> = root + .balance_changes + .iter() + .map(|change| (Address::from_slice(&change.address), change.reason)) + .collect(); + assert_eq!( + balance_changes, + vec![ + (PAYER, Reason::GasBuy as i32), + (PAYER, Reason::GasRefund as i32), + (VAULT, Reason::RewardTransactionFee as i32), + ], + "payer debit before the root call, settlement after it, nothing synthesized" + ); + assert!( + root.balance_changes.iter().all(|change| change.ordinal > root.begin_ordinal), + "the pre-root writes are recorded inside the root call" + ); + + assert_eq!(root.storage_changes.len(), 1, "pre-root storage write"); + assert_eq!(root.storage_changes[0].address, PRECOMPILE.to_vec()); + assert_eq!(root.logs.len(), 1, "pre-root log"); + assert_eq!(root.logs[0].address, PRECOMPILE.to_vec()); + } + + const PAYER: Address = Address::repeat_byte(0xbb); + const VAULT: Address = Address::repeat_byte(0x19); + + /// Drives one transaction that writes a payer debit, a storage slot and a log on the journal + /// before its root call, and credits the payer and a vault after it, the way Base's account + /// abstraction executor does. Returns the raw FIRE output buffer. + fn drive_root_journal_transaction() -> Vec { + use pb::sf::ethereum::r#type::v2::balance_change::Reason; + use reth_revm::{ + bytecode::Bytecode, + revm::{ + context::{journaled_state::account::JournaledAccountTr, Context}, + database::{CacheDB, EmptyDB}, + interpreter::{ + interpreter_action::CallScheme, CallInput, CallValue, Gas, InstructionResult, + InterpreterResult, + }, + state::AccountInfo, + MainContext, + }, + }; + + let mut db = CacheDB::new(EmptyDB::default()); + db.insert_account_info(PRECOMPILE, AccountInfo::default()); + db.insert_account_info(SENDER, AccountInfo::default()); + db.insert_account_info(VAULT, AccountInfo::default()); + db.insert_account_info( + PAYER, + AccountInfo { balance: U256::from(1_000_000u64), ..Default::default() }, + ); + let mut ctx = Context::mainnet().with_db(db); + + let (mut tracer, buffer) = firehose_tracer::Tracer::with_buffer( + firehose_tracer::config::Config::default(), + firehose_tracer::config::ChainConfig { + chain_id: 8453, + shanghai_time: Some(0), + cancun_time: Some(0), + prague_time: None, + verkle_time: None, + }, + "reth-firehose-test", + "0", + ); + + { + let mut insp = FirehoseInspector::new(&mut tracer); + insp.tracer_mut().on_block_start(firehose_tracer::types::BlockEvent { + block: firehose_tracer::types::BlockData { number: 2, ..Default::default() }, + finalized: None, + flash_block: None, + }); + insp.tracer_mut().on_tx_start(legacy_tx_event(), None); + insp.set_root_journal_reasons(RootJournalReasons { + decreases: vec![(PAYER, Reason::GasBuy)], + increases: vec![(PAYER, Reason::GasRefund), (VAULT, Reason::RewardTransactionFee)], + }); + + // Before the root call: payer debit, storage write and log, all on the journal. + ctx.journal_mut() + .load_account_mut(PAYER) + .expect("load payer") + .data + .set_balance(U256::from(900_000u64)); + ctx.journal_mut().load_account(PRECOMPILE).expect("load account"); + ctx.journal_mut().sload(PRECOMPILE, U256::from(1u64)).expect("sload"); + ctx.journal_mut() + .sstore(PRECOMPILE, U256::from(1u64), U256::from(2u64)) + .expect("sstore"); + ctx.journal_mut().log(AlloyLog { + address: PRECOMPILE, + data: alloy_primitives::LogData::new_unchecked( + vec![B256::repeat_byte(0x01)], + Bytes::new(), + ), + }); + + let mut inputs = CallInputs { + input: CallInput::Bytes(Bytes::new()), + return_memory_offset: 0..0, + gas_limit: 100_000, + reservoir: 0, + bytecode_address: SENDER, + known_bytecode: (KECCAK_EMPTY, Bytecode::default()), + target_address: SENDER, + caller: SENDER, + value: CallValue::Transfer(U256::ZERO), + scheme: CallScheme::Call, + is_static: false, + charged_new_account_state_gas: false, + }; + let _ = insp.call(&mut ctx, &mut inputs); + + // A child call that writes state, then is rolled back after it returned, the way a + // failed account abstraction phase is. The settlement entries below then reuse the + // rolled-back journal positions. + let checkpoint = ctx.journal_mut().checkpoint(); + let mut child = + CallInputs { target_address: VAULT, bytecode_address: VAULT, ..inputs.clone() }; + let _ = insp.call(&mut ctx, &mut child); + ctx.journal_mut() + .load_account_mut(SENDER) + .expect("load sender") + .data + .set_balance(U256::from(7u64)); + ctx.journal_mut() + .load_account_mut(VAULT) + .expect("load vault") + .data + .set_balance(U256::from(8u64)); + let mut child_outcome = CallOutcome { + result: InterpreterResult { + result: InstructionResult::Return, + output: Bytes::new(), + gas: Gas::new(50_000), + }, + memory_offset: 0..0, + was_precompile_called: false, + precompile_call_logs: Vec::new(), + charged_new_account_state_gas: false, + }; + insp.call_end(&mut ctx, &child, &mut child_outcome); + ctx.journal_mut().checkpoint_revert(checkpoint); + + // After the calls, still inside the root call: payer refund and vault credit. + ctx.journal_mut() + .load_account_mut(PAYER) + .expect("load payer") + .data + .set_balance(U256::from(950_000u64)); + ctx.journal_mut().balance_incr(VAULT, U256::from(40_000u64)).expect("credit vault"); + + let mut outcome = CallOutcome { + result: InterpreterResult { + result: InstructionResult::Return, + output: Bytes::new(), + gas: Gas::new(100_000), + }, + memory_offset: 0..0, + was_precompile_called: false, + precompile_call_logs: Vec::new(), + charged_new_account_state_gas: false, + }; + insp.call_end(&mut ctx, &inputs, &mut outcome); + + // The generic accounting would refund the sender and tip the coinbase. + insp.process_post_tx_balance_changes( + SENDER, + Address::repeat_byte(0xcb), + 100_000, + 21_000, + 7, + 1, + 1, + |_| U256::ZERO, + ); + + let mut receipt = firehose_tracer::types::ReceiptData::new(0, 21_000, 1, 21_000); + receipt.add_log(firehose_tracer::types::LogData::new( + PRECOMPILE, + vec![B256::repeat_byte(0x01)], + Bytes::new(), + 0, + )); + insp.tracer_mut().on_tx_end(Some(&receipt), None); + } + + tracer.on_block_end(None); + drop(tracer); + + buffer.get_bytes() + } + /// Address of the B-20 activation registry precompile, used here as a stand-in for any /// native precompile that writes storage / emits logs directly on the journal. const PRECOMPILE: Address = @@ -2826,6 +3185,7 @@ mod tests { blob_gas_fee_cap: None, blob_hashes: Vec::new(), set_code_authorizations: Vec::new(), + account_abstraction_trx_input: None, } } diff --git a/crates/firehose/src/mapper.rs b/crates/firehose/src/mapper.rs index 7112c6194c3..579e1c0ae0c 100644 --- a/crates/firehose/src/mapper.rs +++ b/crates/firehose/src/mapper.rs @@ -275,6 +275,7 @@ where .authorization_list() .map(|auths| auths.iter().map(map_signed_authorization).collect()) .unwrap_or_default(), + account_abstraction_trx_input: None, } }