Skip to content

Conversation

@Himess
Copy link
Contributor

@Himess Himess commented Jan 10, 2026

Summary

Introduces UnifiedReceiptBuilder - a wrapper that handles both deposit and non-deposit transaction receipts seamlessly, eliminating the try-catch pattern required by OpReceiptBuilder.

Changes

  • Added receipt_builder.rs with UnifiedReceiptBuilder<C> struct
  • Single build() method handles both tx types internally
  • Simplified state_builder.rs - removed ~40 lines of manual deposit handling
  • Removed unused alloy-op-evm dependency

Closes #309

…eipt handling

Add a new UnifiedReceiptBuilder that handles both deposit and non-deposit
transactions seamlessly without requiring error handling at the call site.

The builder wraps OpRethReceiptBuilder and automatically handles the deposit
receipt case internally, eliminating the need for callers to implement the
try-catch pattern typically required when using build_receipt followed by
build_deposit_receipt.

Changes:
- Add receipt_builder module with UnifiedReceiptBuilder
- Update PendingStateBuilder to use UnifiedReceiptBuilder
- Simplify execute_with_evm method by removing manual deposit handling
- Remove receipt_builder parameter from PendingStateBuilder::new()

Closes base#309
@cb-heimdall
Copy link
Collaborator

cb-heimdall commented Jan 10, 2026

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

@Himess Himess marked this pull request as draft January 10, 2026 21:58
Removed wrapper around OpRethReceiptBuilder and implemented direct
receipt building logic. This fixes type mismatches between OpTxEnvelope
and OpTransactionSigned that prevented compilation.

- Use direct OpTxType matching instead of build_receipt/build_deposit_receipt pattern
- Accept ExecutionResult<E::HaltReason> for proper generic type handling
- Remove unused alloy-op-evm dependency
@Himess Himess marked this pull request as ready for review January 10, 2026 22:53
input,
&mut self.l1_block_info,
)
.unwrap()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we properly bubble up an error here instead of using .unwrap()? We should have fixed this previously but if we're modifying this, let's cleanup past issues.

self.cumulative_gas_used,
self.pending_block.timestamp,
)
.map_err(|_| ExecutionError::DepositAccountLoad)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's implement From<ReceiptBuildError> for ExecutionError. This should let us remove the .map_err here and just use the ? operator.

So this would just become:

let receipt = self
    .receipt_builder
    .build(
        &mut self.evm,
        &transaction,
        result,
        &state,
        self.cumulative_gas_used,
        self.pending_block.timestamp,
    )?;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

evm: &mut E,
transaction: &Recovered<OpTxEnvelope>,
result: ExecutionResult<E::HaltReason>,
_state: &EvmState,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove this argument if it's unused?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

Copy link
Contributor

@refcell refcell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few changes + nits

- Add From<ReceiptBuildError> for ExecutionError and StateProcessorError
- Remove unused _state parameter from build()
- Replace .unwrap() with proper error handling
- Add comprehensive tests for receipt building logic
- Add EVM-based tests that directly test build() method
- Test legacy receipt building
- Test deposit receipt building
- Test Canyon hardfork activation (deposit_receipt_version)
- Test failed transaction receipt
@Himess
Copy link
Contributor Author

Himess commented Jan 12, 2026

@refcell Ready to review

@Himess Himess requested a review from refcell January 12, 2026 10:53
Comment on lines +93 to +98
impl From<crate::ReceiptBuildError> for ExecutionError {
fn from(_: crate::ReceiptBuildError) -> Self {
Self::DepositAccountLoad
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be the ExecutionError::RpcReceiptBuild() error variant rather than the ExecutionError::DepositAccountLoad unless I'm not understanding something?

Comment on lines +194 to +204
#[test]
fn test_legacy_receipt_type() {
let tx = create_legacy_tx();
assert_eq!(tx.tx_type(), OpTxType::Legacy);
}

#[test]
fn test_deposit_receipt_type() {
let tx = create_deposit_tx();
assert_eq!(tx.tx_type(), OpTxType::Deposit);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rm

Suggested change
#[test]
fn test_legacy_receipt_type() {
let tx = create_legacy_tx();
assert_eq!(tx.tx_type(), OpTxType::Legacy);
}
#[test]
fn test_deposit_receipt_type() {
let tx = create_deposit_tx();
assert_eq!(tx.tx_type(), OpTxType::Deposit);
}

Comment on lines +258 to +268
#[test]
fn test_tx_type_to_receipt_mapping() {
let receipt =
Receipt { status: Eip658Value::Eip658(true), cumulative_gas_used: 21000, logs: vec![] };

// Test all non-deposit variants
assert!(matches!(OpReceipt::Legacy(receipt.clone()), OpReceipt::Legacy(_)));
assert!(matches!(OpReceipt::Eip2930(receipt.clone()), OpReceipt::Eip2930(_)));
assert!(matches!(OpReceipt::Eip1559(receipt.clone()), OpReceipt::Eip1559(_)));
assert!(matches!(OpReceipt::Eip7702(receipt), OpReceipt::Eip7702(_)));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also a no-op test, rm.

Suggested change
#[test]
fn test_tx_type_to_receipt_mapping() {
let receipt =
Receipt { status: Eip658Value::Eip658(true), cumulative_gas_used: 21000, logs: vec![] };
// Test all non-deposit variants
assert!(matches!(OpReceipt::Legacy(receipt.clone()), OpReceipt::Legacy(_)));
assert!(matches!(OpReceipt::Eip2930(receipt.clone()), OpReceipt::Eip2930(_)));
assert!(matches!(OpReceipt::Eip1559(receipt.clone()), OpReceipt::Eip1559(_)));
assert!(matches!(OpReceipt::Eip7702(receipt), OpReceipt::Eip7702(_)));
}

Comment on lines +281 to +297
#[test]
fn test_build_legacy_receipt() {
let chain_spec = Arc::new(OpChainSpecBuilder::base_mainnet().build());
let mut db = InMemoryDB::default();
let mut evm = create_test_evm(chain_spec.clone(), &mut db);

let builder = UnifiedReceiptBuilder::new(chain_spec);
let tx = create_legacy_tx();
let result = create_success_result();

let receipt = builder.build(&mut evm, &tx, result, 21000, 0).expect("build should succeed");

assert!(matches!(receipt, OpReceipt::Legacy(_)));
if let OpReceipt::Legacy(inner) = receipt {
assert!(inner.status.coerce_status());
assert_eq!(inner.cumulative_gas_used, 21000);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice test!

Copy link
Contributor

@refcell refcell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much better. A couple of nits. Will add @meyer9 as a reviewer to get another pair of eyes since this addresses a ticket he wrote.

You'll also need to resolve conflicts with trunk

@refcell refcell requested a review from meyer9 January 12, 2026 15:10
Copy link
Contributor

@meyer9 meyer9 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG other than the comments @refcell left! Will let them approve once comments are addressed!

@danyalprout danyalprout added this to the v0.4.0 milestone Jan 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create a new ReceiptBuilder builds deposit receipts

5 participants