Skip to content

fix: F-2026-18145 | [Dual Defense] Missing OP-Stack L1 Data-Fee Accounting Under-Reports Outbound Cost - #304

Merged
Aman035 merged 8 commits into
audit-fixesfrom
F-2026-18145
Aug 17, 2026
Merged

fix: F-2026-18145 | [Dual Defense] Missing OP-Stack L1 Data-Fee Accounting Under-Reports Outbound Cost#304
Aman035 merged 8 commits into
audit-fixesfrom
F-2026-18145

Conversation

@Aman035

@Aman035 Aman035 commented Aug 17, 2026

Copy link
Copy Markdown
Member

F-2026-18145 — OP-Stack L1 data-fee under-reporting

Problem

Outbound settlement recorded GasFeeUsed = gasUsed * gasPrice, capturing only the L2 execution cost. On OP-Stack destinations (Base, Optimism, …) a transaction also pays an L1 data fee, which was dropped. Core's applyGasRefund computes gasFee − GasFeeUsed, so the omitted L1 component was silently over-refunded to users, and the TSS/relayer balance absorbed it.

Fix

GasFeeUsed now includes the L1 fee and uses the price actually paid, read from a single eth_getTransactionReceipt call:

GasFeeUsed = gasUsed × effectiveGasPrice + l1Fee
  • l1Fee — the OP-Stack L1 data fee. go-ethereum's typed types.Receipt doesn't expose it, so we read it from the raw receipt JSON. Absent on non-OP chains → 0, so those are unaffected.
  • effectiveGasPrice — the price actually paid (from the receipt), correct for both legacy and EIP-1559 txs. This avoids the tx.GasPrice() pitfall, where a type-2 tx returns maxFeePerGas (the cap) and would over-report.
  • Guard, not fallbackeffectiveGasPrice is present on every post-London chain (all targets), so it is not defended with a secondary lookup. If a receipt ever omits it, both paths return "0" / skip-and-retry rather than silently emit an L2-less fee.

Both settlement paths use it:

  • success — event_confirmer.go
  • revert/resolver — TxBuilder.GetGasFeeUsed (consumed by txresolver)

The receipt fetch was consolidated to one raw call returning the fields we use (status, blockNumber, gasUsed, effectiveGasPrice, l1Fee); the separate transaction fetch was removed.

Tests (l1fee_test.go)

  • OP destination: GasFeeUsed = execFee + l1Fee.
  • Non-OP destination: GasFeeUsed = execFee only.
  • Missing effectiveGasPrice"0" (guard; never an L2-less fee).
  • Missing receipt → "0".

Verified against live Base Sepolia receipts (real l1Fee, e.g. 0x2a38522d9).

Out of scope (flagged separately)

  • Pre-outbound L1 quoting (getL1Fee upper bound in quotes) and payload-size caps for OP destinations — quoting/policy, partly core-side.

Severity: Low (relayer gas accounting on OP/Base-class chains; no principal theft).

@Aman035 Aman035 changed the title fix: include OP-Stack L1 data fee in outbound GasFeeUsed accounting (F-2026-18145) fix: correct outbound gas-fee accounting for OP-Stack L1 data fee (F-2026-18145) Aug 17, 2026
@Aman035 Aman035 changed the title fix: correct outbound gas-fee accounting for OP-Stack L1 data fee (F-2026-18145) fix: F-2026-18145 | [Dual Defense] Missing OP-Stack L1 Data-Fee Accounting Under-Reports Outbound Cost Aug 17, 2026
@Aman035
Aman035 merged commit d28d095 into audit-fixes Aug 17, 2026
4 checks passed
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.

1 participant