Skip to content

fix/beryl b20 logs#14

Closed
sduchesneau wants to merge 348 commits into
firehose/0.xfrom
fix/beryl-b20-logs
Closed

fix/beryl b20 logs#14
sduchesneau wants to merge 348 commits into
firehose/0.xfrom
fix/beryl-b20-logs

Conversation

@sduchesneau

Copy link
Copy Markdown

refcell and others added 30 commits May 22, 2026 15:22
…for invalid IDs (base#2844)

* feat(precompiles): soften policy_exists and is_authorized error handling for malformed and missing policies

policy_exists now returns Ok(false) for malformed policyIds (type byte > 1)
instead of reverting. is_authorized similarly returns Ok(false) for malformed
IDs, drops the existence check (so unwritten slots are treated with default
type semantics: ALLOWLIST=not authorized, BLOCKLIST=authorized), and never
returns PolicyNotFound. Fast-paths for ALWAYS_ALLOW_ID and ALWAYS_BLOCK_ID
are preserved.

Signed-off-by: Eric Liu <ericliu@base.org>
Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>

* fix(policy): replace dead wildcard arm with unreachable, document is_authorized/policy_exists divergence

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>

---------

Signed-off-by: Eric Liu <ericliu@base.org>
Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>
base#2857)

* refactor(policy): align IPolicyRegistry errors with base-std interface

Remove `InvalidPolicyType` and `MalformedPolicyId` from the Rust ABI
definition; neither appears in `base/base-std`'s `IPolicyRegistry.sol`,
so Solidity callers have no way to decode reverts carrying those selectors.

Behavioral consequences:
- Write ops with a malformed policy ID (type byte > 1) now reach
  `PolicyNotFound` via the zero-slot read in `require_custom`, which is
  the correct error: a malformed ID was never created.
- `policy_exists` with a malformed ID now returns `false` (zero slot,
  `exists()` bit unset) and never reverts, matching the base-std
  `Never reverts` contract.
- `InvalidPolicyType` in `create_policy` was dead code: `PolicyType` is
  decoded from ABI before the function body runs, so out-of-range values
  are rejected by the ABI decoder. The guard is dropped; the `_` arm in
  `create_policy_with_accounts` becomes `unreachable!()`.
- The `_ =>` fallback in `is_authorized`'s match becomes `unreachable!()`
  for the same reason: `packed.exists()` is the gatekeeper and no stored
  policy can carry a type byte > 1.

Add unit tests covering the new behavior:
- `policy_exists_malformed_id_returns_false`
- `write_op_with_malformed_id_returns_policy_not_found`

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>

* fix(policy): update PolicyAdminStaged callers to use currentAdmin/pendingAdmin field names

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>

---------

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>
Unifies all step-security/harden-runner pins to v2.15.0
(a90bcbc6539c36a85cdfeb73f7e2f433735f215b) across all workflows for a
consistent security posture and single source of truth.

Closes base#2829
…ion (base#2856)

* feat(transferable): add privileged flag to skip authorization on transfer

Adds `privileged: bool` to `Transferable::transfer`, `transfer_from`,
`transfer_with_memo`, and `transfer_from_with_memo`. When true (factory
bootstrap window) the pause check and all policy guards are skipped,
matching the `_isPrivileged()` bypass in the Solidity mock's `_transfer`.
Balance invariants are always enforced regardless of privilege.

All three dispatch variants (b20, b20_stablecoin, b20_security) now
forward the existing `privileged` flag through to transfer calls. Adds
coverage for balance boundaries, receiver overflow, external policy
registry paths, and Transfer event field content.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* Apply suggestion from @github-actions[bot]

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(transferable): prevent self-transfer balance inflation

Debit the sender before crediting the receiver so aliased addresses match Solidity semantics, and add regression tests for repeated self-transfers.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…ase#2863)

* feat(consensus): add EIP-8130 AA transaction types

Introduces standalone type definitions for EIP-8130 Account Abstraction
transactions in base-common-consensus: TxAa8130 (unsigned body), AaSigned
(signed envelope with sender_auth and payer_auth), AccountChange tagged
union (Create / ConfigChange / Delegation), and Call. Includes RLP and
EIP-2718 round-trip coverage plus the two domain-separated signing-hash
helpers.

The new types are not yet wired into BaseTxEnvelope; that integration
follows in a separate commit.

* feat(consensus): wire EIP-8130 AA8130 variant into envelope/receipt/pool

Plumb the new TxAa8130/AaSigned types into BaseTxEnvelope, BaseTypedTransaction,
BasePooledTransaction, BaseReceipt, and BaseReceiptEnvelope at type byte 0x7D.
Adds OpTxType::Aa8130, the EIP8130_TX_TYPE_ID constant, reth_compat Compact
arms (unimplemented placeholders for the binary codec to be filled in later),
and exhaustive match arms across all dispatch sites. AaSigned caches its
EIP-2718 hash at construction so envelope hash() continues to return &B256.

All 87 unit tests in base-common-consensus pass.

* feat(evm,execution,rpc-types): handle EIP-8130 OpTxType::Aa8130 variants

Add Aa8130 arms to every match site that dispatches on OpTxType/BaseReceipt
across the EVM, execution, RPC, and flashblocks crates. The arms wire receipts
through unchanged (Receipt<T> shape) and stub EVM execution with unimplemented!
so consensus-layer changes stay decoupled from execution semantics until the
8130 verifier and gas accounting land.

* chore(consensus): satisfy clippy on EIP-8130 modules

Make address_opt_encoded_length const, collapse Option::Some match into
map_or, merge identical Deposit/Aa8130 None arms, and rephrase a payer
signature-hash docstring to avoid the doc-markdown false positive on
nested bracket sequences inside backticked code.

* fix(consensus): return RecoveryError for EIP-8130 EOA-path signer recovery

Previously SignerRecoverable for BaseTxEnvelope::Aa8130 (and the same
arm in BasePooledTransaction) returned Ok(explicit_sender.unwrap_or(ZERO)).
On the EOA path (tx.sender == None) the spec requires recovering the
signer from the 65-byte ECDSA sender_auth, which is not yet implemented;
silently returning Ok(Address::ZERO) misrepresented unauthenticated
transactions as having a valid recovered sender. The tx pool and any
caller relying on SignerRecoverable would key on / log a zero address
and accept the transaction as if recovery succeeded.

Return Err(RecoveryError::new()) on the EOA path until real recovery
lands. The configured-owner path (explicit_sender is Some) still returns
that address.

* fix(consensus): recompute EIP-8130 AaSigned hash on deserialize and arbitrary

Replace the derived Serde and Arbitrary impls on AaSigned with manual ones
that route through AaSigned::new, ensuring the cached hash field is always
recomputed from the canonical EIP-2718 encoding rather than trusted from an
on-wire value or generated independently of the payload.

The Serialize path now omits the hash field entirely (it is fully derivable
from the other fields), and Deserialize reconstructs the AaSigned through
the constructor so a malicious or stale hash in the input cannot disagree
with the transaction body.

Adds two regression tests proving (a) the serialized form does not include
hash and round-trips, and (b) a deliberately-zeroed hash in the input is
ignored in favor of the recomputed value.

* fix(consensus): use unimplemented! over unreachable! for EIP-8130 trait stubs

The Aa8130 arms in BaseTypedTransaction's RlpEcdsaEncodableTx impl and in
BasePooledTransaction's signature/signature_hash/into_envelope/From paths
are reachable from generic alloy and reth Transaction code (e.g. anything
that calls Transaction::tx_hash on a typed transaction without first
checking the variant). unreachable! signals UB-on-violation, which is the
wrong contract here: these arms are intentionally not-yet-implemented and
will be filled in once EIP-8130 has its own encoding plumbing.

unimplemented! conveys the correct semantics: 'this is a TODO that will
panic if hit', not 'this can never happen'.

* fix(consensus): debug-assert configured-owner is unset on Signed AA path

The From<Signed<BaseTypedTransaction>> for BaseTxEnvelope conversion
blindly stuffs the ECDSA signature bytes into AaSigned::sender_auth.
That is correct for the EOA path (tx.sender == None), but for the
configured-owner path (tx.sender == Some(addr)) the sender_auth is
supposed to be an authentication payload bound to the configured owner,
not a raw ECDSA signature over an unrelated digest.

Guard the invariant with a debug_assert so misuse is caught in tests and
debug builds while keeping release builds free of extra checks. Callers
that need to wrap a configured-owner AA tx must construct
BaseTxEnvelope::Aa8130(AaSigned::new(...)) directly with the correct
sender_auth, not go through the ECDSA Signed wrapper.

* fix(consensus): encode EIP-8130 Scope as a bare RLP byte

EIP-8130 specifies Scope as a uint8. The derived RLP impls produced by
alloy_rlp's #[derive(RlpEncodable)] always emit a list header, so Scope(0x05)
was being encoded as [0xc1, 0x05] (single-element list) instead of [0x05]
(bare byte). The same wrapping propagated to every container that reached
Scope through a derived encoding (InitialOwner.scope, OwnerChange.scope).

Hand-roll Encodable/Decodable for Scope to forward to u8's impls. The
roundtrip tests still pass because they were symmetric; the new pinned
wire-format test catches any future regression to the derived impl.

* fix(consensus): also debug-assert payer.is_none() on Signed AA path

Symmetric extension of the previous sender-assert. The
From<Signed<BaseTypedTransaction>> for BaseTxEnvelope path constructs
AaSigned with payer_auth = Bytes::new(). For sponsored AA transactions
(tx.payer == Some(_)), this silently drops the payer authentication and
would leave the resulting envelope unable to be validated against the
sponsoring payer. Guard the invariant so callers must route sponsored
AA transactions through BaseTxEnvelope::Aa8130 directly with the proper
payer_auth populated.

* fix(consensus,rpc-types): unimplemented! over default() for AA TransactionRequest conversions

The four From<BaseTxEnvelope|BaseTypedTransaction> for TransactionRequest
(both alloy and Base) arms previously returned Self::default() for the
Aa8130 variant, silently handing callers an empty request with no chain
id, gas, value, or sender. Any RPC path that hit one of these conversions
with an AA transaction would see a blank request rather than a clear
error.

Match the convention established by the trait stubs in BaseTypedTransaction
and BasePooledTransaction (commit 4be29ffd6) and panic via unimplemented!()
with an explanatory message. AA transactions have no single recipient or
value to project onto the legacy request shape, so silent default conversion
was incorrect by construction.

* fix(consensus): satisfy nightly fmt and no_std for EIP-8130 modules

- aa8130/account_changes.rs missed an `alloc::vec::Vec` import (the crate
  is `#![no_std]` outside the std feature), breaking the no_std CI check
- nightly rustfmt reorders the inner `use` block in aa8130/signed.rs and
  drops an unused `b256` import plus a redundant `.clone()` in tx.rs tests

Caught by CI on the just-opened PR.

* fix(consensus): early-return RecoveryError on AA path for the other two SignerRecoverable methods

The earlier RecoveryError fix on BasePooledTransaction only covered
recover_unchecked_with_buf; the sibling methods recover_signer and
recover_signer_unchecked still unconditionally call self.signature_hash()
and self.signature(), both of which unimplemented!() on the Aa8130 arm.

That meant an AA transaction entering the mempool would panic the node
the moment the pool tried to resolve its sender. Mirror the same
early-return as the BaseTxEnvelope impl: defer to AaSigned::explicit_sender
when present, otherwise return a RecoveryError so the caller can reject
the tx cleanly.

* docs(consensus): warn that reth Envelope::signature returns a zero placeholder for AA/Deposit

reth_codecs::Envelope::signature must return &Signature, so there is no
way to express absence the way BaseTxEnvelope::signature (Option<&Signature>)
does for Deposit and EIP-8130 AA variants. Returning DEPOSIT_SIGNATURE
(all zeros) is the least-bad option, but anyone passing that value into
ECDSA recovery would silently get back garbage instead of a clear error.

Add a comment naming the constraint and instructing callers not to feed
the value into recovery. No behavior change.

* fix(consensus): reject AA transactions in try_into_eth_pooled instead of panicking

try_into_pooled returns Ok(BasePooledTransaction::Aa8130(...)) because AA
txs DO live in our pool, but try_into_eth_pooled chained .map(Into::into),
and that From<BasePooledTransaction> for alloy_consensus::PooledTransaction
impl unimplemented!()s on Aa8130. So calling try_into_eth_pooled on an AA
envelope would silently compile and then panic at runtime, violating the
try_ contract.

Match the pattern already used by try_into_eth_envelope: reject Aa8130
explicitly with a ValueError naming the variant, then delegate the
remaining variants to the existing try_into_pooled path. No new variants
added; pure correctness fix.
* test(policy): add PackedPolicy newtype unit tests (BOP-117)

Add five targeted unit tests for the PackedPolicy newtype covering:
round-trip fidelity for all four PolicyType discriminants, the
zero-word == never-created invariant relied on by policyExists, the
renounced-admin (zero admin + non-zero type) not being confused with
never-created, and bidirectional bit-isolation between the admin field
(bits 167:8) and the type field (bits 7:0).

* chore: apply nightly fmt and fix clippy

- Run cargo +nightly fmt --all
- Fix b20_stablecoin/dispatch.rs to use ActivationFeature::B20Stablecoin.id()
  instead of the removed ActivationRegistryStorage::B20_STABLECOIN constant
- Update PackedPolicy unit tests to match the refactored API: policy type is
  no longer stored in the packed word, exists() replaces is_zero(), and the
  constructor takes only an admin address

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>

* refactor(test): remove redundant PackedPolicy unit tests

- Remove zero_word_means_never_created: subsumes packed_policy_zero_signals_never_created;
  fold the unique admin()==Address::ZERO assertion into the existing test
- Remove zero_admin_is_not_confused_with_never_created: identical constructor
  args and assertions to packed_policy_zero_admin_is_non_zero
- Remove admin_with_low_byte_ff_roundtrips: subsumed by
  packed_policy_new_roundtrips_admin_for_various_addresses which already
  tests the all-0xff address

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>

---------

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>
* test(policy): add error path action tests for PolicyRegistry (BOP-109)

Covers five error cases via full block execution (BerylTestEnv):
Unauthorized, PolicyNotFound, IncompatiblePolicyType, StaticCallNotAllowed,
and NoPendingAdmin.

* chore: apply nightly fmt

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>

---------

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>
…ase#2866)

* refactor(consensus): rename Aa8130 -> Eip8130 for naming consistency

Align EIP-8130 transaction types with the project's existing Eip7702 /
Eip1559 / Eip2930 naming convention. The 'Aa' prefix was a transitional
shorthand; using the EIP number matches every other typed transaction
in the workspace and the upstream alloy convention.

Rename map:
  Aa8130                -> Eip8130           (envelope/typed/receipt variants)
  AaSigned              -> Eip8130Signed
  TxAa8130              -> TxEip8130
  Aa8130Constants       -> Eip8130Constants
  AA_TX_TYPE            -> EIP8130_TX_TYPE
  AA_PAYER_TYPE         -> EIP8130_PAYER_TYPE
  AA_BASE_COST          -> EIP8130_BASE_COST
  is_aa8130()           -> is_eip8130()
  as_aa8130()           -> as_eip8130()
  aa_signed.rs          (file content reference updates)
  aa8130/ (directory)   -> eip8130/

Pure rename, no behavior change. All 90 base-common-consensus tests
pass; full workspace builds clean.

* refactor(consensus): reorder match arms so Deposit comes last

Across every match expression that fans out over BaseTxEnvelope /
BaseTypedTransaction variants, reorder the arms so:

  Legacy / Eip2930 / Eip1559 / Eip7702 / Eip8130 -> normal ECDSA path
  Deposit                                        -> always last

Rationale: the Deposit transaction is the special, unsigned, L2-only
variant that requires distinct handling (e.g. no signature recovery,
no pool admission, explicit `from` field). Keeping it as the final arm
in every match makes the ECDSA path read top-to-bottom as a single
cohesive block and makes the special-case handling visually obvious.
Eip8130 is an ECDSA-class L2 transaction, so it sits with the other
typed variants rather than next to Deposit.

Pure reorder, no behavior change. Pipe-chain arms (e.g.
`Self::Eip8130(_) | Self::Deposit(_)`) follow the same ordering rule.
All 90 base-common-consensus tests pass; full workspace builds clean;
nightly rustfmt clean.
* chore: upgrade reth to v2 (rev d2327cb) with migrate-v2 support

Upgrades all reth dependencies from v1.11.3 to rev d2327cb14f4fc42b3ab7afd37dd1c9e6346ee620
(paradigmxyz/reth migrate-v2 PR branch) with associated API changes:

- Update Cargo.toml workspace deps to reth v2 rev
- Adapt to reth v2 type/trait API changes
- Restore CompactBaseReceipt field order (OpTxType first) for DB compatibility
- Remove duplicate trait impls conflicting with v2 blanket impls

* add snapshot-manifest command wiring

* chore: upgrade to reth v2.1.0 stable with alloy 2.0

* fix: resolve alloy 2.0 test breakage, fpvm-precompile fixes, deny.toml updates

* fix: resolve post-merge alloy 2.0 test breakage and deprecation warnings

* fix: resolve post-rebase build errors for reth v2.1.0 and alloy 2.0

- Rename OpPayloadBuilderAttributes -> BasePayloadBuilderAttributes across workspace
- Rename OpMinerExtApi -> BaseMinerExtApi, OpDebugWitnessApi -> BaseDebugWitnessApi
- Upgrade revm-inspectors 0.34.3 -> 0.39.0 to match revm v38
- Upgrade revm-precompile 32.0.0 -> 34.0.0 to unify with revm v38
- Update precompile APIs for revm-precompile v34 (3-arg execute, PrecompileHalt)
- Update test code for alloy 2.0 (block_timestamp, ResultGas, Runtime::test())
- Remove unused alloy-node-bindings deps from succinct crates
- Clean up deny.toml skip list for resolved duplicates

* fix: update base-precompile-storage for revm-precompile v34 API

- PrecompileError::OutOfGas -> PrecompileOutput::halt(PrecompileHalt::OutOfGas)
- PrecompileOutput::new_reverted -> PrecompileOutput::revert with reservoir arg
- PrecompileOutput::new takes 3 args (add reservoir=0)
- JournalCheckpoint needs selfdestructed_i field

* fix: resolve post-rebase build failures and pin SP1 deps

* fix: resolve post-rebase build failures from 3rd rebase

- Fix bls12_381.rs precompile closures for revm-precompile v34 API
  (3-arg execute, PrecompileError::Fatal, call_eth_precompile wrapper)
- Fix macros.rs: PrecompileOutput::new_reverted -> revert with reservoir
- Fix factory/storage.rs and activation/storage.rs: .reverted -> .is_revert()
- Fix factory.rs: .install() -> PrecompilesMap::from_static(precompiles())
- Fix ingress-rpc main.rs: remove kafka code (removed upstream), use
  RootProvider::new_http, IngressService::new takes 4 args
- Remove stale mod execute declaration (removed upstream in a00cb26)
- Re-export BaseExecutorProvider from execution-evm lib.rs
- Re-export JOVIAN precompile constants from precompiles lib.rs
- Fix succinct client test for SP1-patched revm API
- Add revm-primitives and revm-precompile to deny.toml skip list
  (SP1 patch brings v32/v22, workspace uses v34/v23)

* chore: upgrade reth to v2.1.0, revm to v38, alloy to v2.0, and cleanup deny.toml

- Upgrade reth from v1.11.4 to v2.1.0
- Upgrade revm from v34 to v38
- Upgrade alloy from v1.8 to v2.0
- Upgrade SP1 from v6.1.0 to v6.2.1
- Upgrade sp1-cluster from v2.1.5 to v2.3.2
- Upgrade ethereum_ssz from v0.9 to v0.10
- Migrate crates to crates.io: reth-codecs, reth-primitives-traits, reth-zstd-compressors
- Update precompiles API for revm v38 (.install() instead of .precompiles())
- Add clippy::too_many_arguments lint suppression in factory/abi.rs
- Cleanup deny.toml skip list: remove 25+ unnecessary entries, add 9 actual duplicates

* chore: upgrade reth to v2.2.0, alloy-evm to 0.34.0, alloy to 2.0.4

* fix: remove ssz feature leak from workspace dep to fix no_std builds

* fix: resolve post-rebase build failures from 4th rebase

- Add ExecutionWitnessMode param to witness() and import from reth_trie_common
- Inline storage_by_hashed_key into storage() (removed from StateProvider trait)
- Replace .reverted with .is_revert() in test assertions (alloy-evm 0.34.0)
- Replace IB20::Uninitialized with empty revert check (error removed from ABI)
- Remove unused PrecompileError import in SP1 precompiles

* fix(cli): use LenientRpcModuleValidator for custom 'base' RPC module

reth v2.2.0 added RPC module validation at CLI parse time.
DefaultRpcModuleValidator rejects unknown modules, causing a panic
when http.api includes 'base'. LenientRpcModuleValidator accepts
custom module names, which is required for our custom RPC namespace.

* fix(flashblocks): reconstruct depositor AccountInfo from cached receipt

Fixes deposit receipt nonce in the cached execution path. When
get_tx_result builds a BaseTxResult for a deposit transaction, it now
extracts deposit_nonce from the cached receipt and constructs an
AccountInfo so commit_transaction can set deposit_nonce correctly.

* fix clippy

* fix(evm): update precompile over-max-input test assertion

Jovian precompile wrappers return Err(Fatal(...)) for oversized inputs,
not Ok(output) with a halt reason. Update the test assertion to match
the actual behavior.

* fix precompile failing test
* fix(precompiles): harden PolicyRegistry audit findings

Enforce batch size and zero-address validation on membership updates,
short-circuit pending admin lookups for built-in policies, and add
regression tests for the new guards.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: apply rustfmt to PolicyRegistry storage tests

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(precompiles): rename batch limit error to BatchSizeTooLarge

Return the max permitted batch size in the revert data so callers know
the registry limit when createPolicyWithAccounts or membership updates
exceed 64 accounts.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
…torageProvider static enforcement (base#2871)

Mutating operations in PolicyRegistryStorage called require_write() as
an early static-context guard. The EVM storage provider already enforces
this at sstore/tstore/emit_event, so the precompile-level check was
redundant in production.

The HashMapStorageProvider (used in unit tests) did not enforce the
static flag on writes, which is why require_write existed in the first
place. Fix the test provider to match EVM behavior, then remove the
now-unnecessary early checks.
* feat(devnet): run devnet zk prover in dry-run mode

Add the ZK prover service and local Postgres storage to the Docker devnet so proof requests can execute the dry-run backend against local L1/L2 nodes by default.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: address comments

* chore: add healthcheck

* chore: script executable

* chore: add cache entry

* chore: address comments

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
… and isB20Initialized (base#2865)

* rename(factory): ITokenFactory -> IB20Factory, TokenVariant -> B20Variant, createToken -> createB20

Aligns the Rust precompile surface with base-std PR base#63.

Renames:
- ITokenFactory          -> IB20Factory
- TokenFactoryStorage    -> B20FactoryStorage
- TokenFactoryPrecompile -> B20FactoryPrecompile
- TokenVariant           -> B20Variant
- TokenCreated event     -> B20Created
- createToken()          -> createB20()
- getTokenAddress()      -> getB20Address()
- isInitialized()        -> isB20Initialized() (also adds B20 prefix guard: returns
                           false for non-B20 addresses even if they have bytecode)
- ActivationFeature::TokenFactory -> ActivationFeature::B20Factory
- activation key "base.token_factory" -> "base.b20_factory"
  (0xceff...8a5b -> 0x7875...b800, matching base-std ActivationRegistryFeatureList)

Functional changes:
- UnsupportedVersion now carries the B20Variant as a second field, matching base-std
- is_b20_initialized() guards on B20 prefix before checking bytecode presence

274 precompile tests pass.

* fix: rename remaining token_factory references missed in initial pass

- actions/harness/tests/beryl/env.rs and b20.rs
- crates/proof/succinct/utils/client/src/precompiles/mod.rs
- crates/common/precompiles/benches/base_precompiles.rs
- b20_factory_feature() helper renamed from token_factory_feature()

* fix: address PR review comments

- devnet/src/b20.rs: fix stale doc comments referencing createToken and
  getTokenAddress; fix error message string
- etc/scripts/devnet/check-factory-live.sh: rename createToken/getTokenAddress
  call signatures to createB20/getB20Address; remove getB20Variant cast call
  (function does not exist in IB20Factory ABI)

* chore: rename src/factory/ -> src/b20_factory/ per nit

* fix: benches/base_precompiles.rs: create_token -> create_b20 missed call site

* fix: update stale createToken label string in devnet E2E test

* fix(factory): align B20Variant discriminants with ABI enum ordinals (0,1,2)

Previously B20Variant used internal discriminants 1,2,3 (reserving 0 as
NONE_DISCRIMINANT), which caused the byte written at address[10] to differ
from the ABI enum ordinal. A Solidity caller computing getB20Address for
DEFAULT would get byte[10]=0, but the precompile produced byte[10]=1,
resulting in different addresses for the same inputs.

Fix: change to B20=0, Stablecoin=1, Security=2 so the discriminant written
into address[10] equals uint8(B20Variant) in Solidity. Remove
NONE_DISCRIMINANT — the B-20 prefix bytes [0..9] are the sole indicator
that an address is factory-created; byte[10] does not need a sentinel.

Update test_supported_variants_are_b20_prefixes to use discriminants 1 and
2 (was 2 and 3).
* fix(precompiles): align registry precompile addresses

* test(precompiles): update registry address fixtures
* rename(b20): policyType -> policyScope in IB20 ABI and call sites (BOP-147)

Aligns the Rust IB20 interface with base/base-std#66 which renames the
bytes32 parameter from policyType to policyScope to eliminate the semantic
collision with IPolicyRegistry.PolicyType (the BLOCKLIST/ALLOWLIST enum).

The token's policyType was a bytes32 slot identifier (e.g.
keccak256("TRANSFER_SENDER_POLICY")), entirely unrelated to the registry's
PolicyType enum. Renaming to policyScope makes the distinction explicit.

Changes:
- b20/abi.rs: rename policyScope in IB20 error, event, and function params
- All IB20/IB20Security call sites: .policyType -> .policyScope field accesses
  and struct literals across dispatch, storage, policies, and ops files

IPolicyRegistry and its PolicyType enum are untouched.

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>

* rename(b20): policy_type -> policy_scope in Rust call sites (BOP-147)

Renames the local variable `policy_type` (a `B256` bytes32 slot identifier) to
`policy_scope` across all function parameters and call sites in the B-20
precompile crates. Completes the rename that started in the IB20 ABI layer.

The shadowed locals that hold the converted `B20PolicyType` enum (produced by
`require_policy_type` / `require_b20_policy_type`) are intentionally kept as
`policy_type` to reflect their actual type. The `B20PolicyType` param in
`B20Guards::ensure_policy_type` is also unchanged.

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>

* fix(b20): rename stale policy_type references in guards doc and test mock (BOP-147)

- guards.rs: update "raw policy_type" doc comment to policy_scope
- test_utils.rs: rename policy_type param to policy_scope in TokenAccounting mock impl

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>

---------

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>
* test(policy): add renounceAdmin immutability action tests (BOP-112)

Verify that after renounceAdmin, all mutating operations (updateAllowlist,
updateBlocklist, stageUpdateAdmin, finalizeUpdateAdmin) revert, while read
operations (isAuthorized, policyAdmin, policyExists, policyType) continue
to return correct values.

* fix(policy): remove nonexistent policyTypeCall and fix finalizeUpdateAdmin comment

IPolicyRegistry has no policyType() view function, so the policyTypeCall
assertion would not compile. Remove it.

Also correct the finalizeUpdateAdmin comment: the revert reason is
NoPendingAdmin (renounce_admin clears the pending entry before the
Unauthorized check runs), not Unauthorized.
…ation for stablecoin currency (base#2882)

* fix(precompiles): replace ISO 4217 allowlist with A-Z character validation for stablecoin currency

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* style: apply rustfmt formatting

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: refcell <abigger87@gmail.com>
…t creation (BOP-152) (base#2887)

* feat(b20-security): default REDEEM_SENDER_POLICY to ALWAYS_BLOCK_ID at creation (BOP-152)

Security token redemption was open by default because initialize() never
wrote to redeem_policy_ids, leaving the slot at zero (ALWAYS_ALLOW_ID).

Add write_redeem_default_policy_ids() helper that packs ALWAYS_BLOCK_ID
into the REDEEM_SENDER_POLICY lane and call it from initialize(). Also:
- Export REDEEM_SENDER_POLICY as pub from the crate so the mock can use it.
- Update InMemoryTokenAccounting::policy_id() to return ALWAYS_BLOCK_ID
  for REDEEM_SENDER_POLICY when not explicitly set, matching production.
- Update make_token() in dispatch tests to explicitly open redemption so
  non-policy tests are not blocked by the new default.

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>

* fix(b20-security): group REDEEM_SENDER_POLICY re-export with mod ids per CLAUDE.md convention

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>

* nit: rename write_redeem_default_policy_ids -> write_default_redeem_policy_ids

---------

Signed-off-by: Eric Shenghsiung Liu <ericliu121187@gmail.com>
refcell and others added 26 commits June 10, 2026 23:06
* fix(policy-registry): classify calldata before activation gate (BOP-378/PSRC-26)

Unknown selectors, short calldata, and write calls with malformed arguments
previously returned FeatureNotActivated when the policy registry was inactive,
masking the real error and making dispatch behavior activation-state-dependent.

Replace the is_view_selector / write-gate two-branch split with a single match
on the first 4 calldata bytes. Short calldata and unknown selectors return
UnknownFunctionSelector immediately; view selectors bypass the activation gate
and reach inner directly; known write selectors are ABI-validated before
ensure_activated is called so AbiDecodeFailed is always surfaced regardless of
activation state.

Add regression tests for all three cases: inactive unknown selector, malformed
view selector, and malformed write selector.

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(precompile-storage): deduct gas before sload warms slot in journal (BOP-380)

`internals.sload` marks the slot warm in the revm journal before gas is
deducted. If either `deduct_gas` call then returns `OutOfGas` the spurious
warm entry persists, causing a subsequent cold read of the same slot to be
billed at warm cost instead of cold cost, breaking EIP-2929 accounting.

Apply the same checkpoint/revert pattern already used by `sstore`: take a
journal checkpoint before the read and revert it on any error so that a
failed OOG sload leaves the slot's cold/warm state unchanged.

Add a regression test that verifies an OOG sload does not warm the slot:
a second unlimited-gas read of the same slot must still pay the full cold
penalty.

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

* remove comment

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
* fix(precompile-macros): track args presence with a flag instead of emptiness check (base#3405)

The `args` option in `#[precompile]` used `!args.is_empty()` to detect
duplicates, which fails to catch `args(), args()` or `args(), args(x: u8)`
because an empty first occurrence leaves the vec empty. Replace the check
with an `args_seen` boolean flag, matching the `reject_duplicate` pattern
used by every other option (`id`, `storage`, `macro_path`, `install`).

Adds two regression tests covering both previously-undetected cases.

* fix(precompile-macros): forward all struct-level attrs through #[contract] (BOP-360) (base#3407)

* fix(precompile-macros): forward all struct-level attrs through #[contract] (BOP-360)

Previously gen_output kept only #[derive] attributes and silently
discarded doc comments, #[cfg], #[cfg_attr], #[allow], #[serde], etc.
Now all outer attributes except #[namespace] (the macro's own consumed
attribute) are threaded into the generated layout struct. The generic
fallback doc string is suppressed when the user supplies their own doc
comment, preventing silent replacement.

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(precompile-storage): wrap offset_bytes in backticks in test doc comments

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>

* fix(precompile-storage): replace saturating/unchecked slot arithmetic with checked_add (BOP-356) (base#3415)

* fix(precompile-storage): replace saturating/unchecked slot arithmetic with checked_add (BOP-356)

Cantina finding base#33: slot offset arithmetic used saturating_add (which
silently aliases overflowing offsets to U256::MAX) and unchecked + (which
panics in debug builds). Replace all slot additions with checked_add,
returning BasePrecompileError::SlotOverflow in Result contexts and using
expect in constructors/Index impls where returning Result would break
trait contracts.

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

* chore(precompile-storage): apply nightly rustfmt

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>

* fix(precompile-storage): restore all mutable fields in checkpoint_revert (BOP-359)

HashMapStorageProvider::checkpoint_revert was only restoring `internals`
and `events`, silently leaving `transient`, `accounts`, `gas_refunded`,
and `state_gas_used` in their post-mutation state after a revert. This
caused the test mock to diverge from production EVM semantics, where all
state is fully rolled back on revert.

Expand `Snapshot` to capture all four missing fields and restore them in
`checkpoint_revert`. Add per-field regression tests to pin the behavior.

* fix(precompile-storage): complete checked-add migration for BOP-356

Fixes three remaining sites missed by the initial BOP-356 PR (base#3415):

- VecHandler::truncate: two bare + in packed-element branch now use
  checked_add().ok_or(SlotOverflow)?

- VecHandler: rename compute_handler to try_compute_handler returning
  Result<T::Handler> so callers can propagate SlotOverflow via ?.
  Adds get_or_try_insert / get_or_try_insert_mut to HandlerCache to
  support fallible initialization in cache-closure call sites.

- SetHandler::new: drops const fn, returns Result<Self> so the
  positions-slot arithmetic uses ok_or(SlotOverflow)? instead of
  expect(). StorableType::handle for Set<T> retains .expect() at the
  trait boundary since StorableType::handle is infallible by design.

- ArrayHandler::compute_handler: Index/IndexMut are infallible traits
  and cannot propagate Result; documents the invariant that base_slot
  is a keccak256 output (never U256::MAX) and N <= 32.

* fix(precompile-storage): bubble SlotOverflow from ArrayHandler instead of panicking

Remove Index/IndexMut impls and change compute_handler to try_compute_handler
returning Result, with at() and at_mut() returning Result<Option<>>. No
external callers used the Index operator on ArrayHandler.

* fix(precompile-storage): make SetHandler::new infallible via wrapping_add

SetHandler::new was checking for U256::MAX overflow via checked_add and
returning Result<Self>, requiring the StorableType::handle impl to call
.expect(). Changing handle() to propagate a Result would require modifying
the StorableType trait and all proc-macro-generated code, which is
disproportionate.

Use wrapping_add instead: slot addresses are keccak256 outputs uniformly
distributed over [0, 2^256), so U256::MAX is unreachable in practice.
This makes new() infallible, removing the expect() entirely.

* fix(precompile-storage): update tests to use at()/at_mut() instead of Index

* fix(precompile-storage): pass usize by value to at()/at_mut() in tests

* style: fix rustfmt in contract tests

* fix(precompile-storage): make SetHandler::new const fn

* fix(precompile-storage): replace wrapping_add with checked_add in SetHandler::new

* fix(precompile-storage): add missing import and remove duplicate test after cherry-pick

---------

Co-authored-by: Rayyan Alam <rayyan.alam@coinbase.com>
Co-authored-by: Claude <noreply@anthropic.com>
…it (BOP-350) (base#3406) (base#3454)

* fix(precompile-storage): remove unused token arg from checkpoint_commit (BOP-350)

alloy-evm's checkpoint_commit takes no token; the JournalCheckpoint arg
was silently discarded in EvmPrecompileStorageProvider, making the trait
API misleading. Drop the parameter from the trait and both implementations.
HashMapStorageProvider now asserts non-empty snapshots internally instead.

Generated with Claude Code



* fix(precompile-storage): drop unused cp binding in checkpoint test

Generated with Claude Code



---------

Co-authored-by: Claude <noreply@anthropic.com>
…e Sepolia and Mainnet (BOP-382) (base#3463)

* fix(chains): update B20 activation admin addresses for Base Sepolia and Mainnet

Replaces the old admin addresses with the newly provisioned coreKMS keys
for activation testing and smoke tests on Base Sepolia and Base Mainnet.

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(chainspec): update activation_admin_matches_chain_config test addresses

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
* fix(precompile-macros): improve #[contract] macro diagnostic for unrecognized arguments (base#3388)

The error for unrecognized argument keys now names the unknown key and
lists the only supported argument, addr = "0x...". The previous message
"only `addr` attribute is supported" also silently accepted `address` as
an alias; that alias is removed so the canonical form is enforced.

Fixes BOP-346 (PSRC base#25).

* refactor(b20-asset, activation): remove unreachable announcement guard and redundant AlreadyDeactivated error (base#3253)

* refactor(b20-asset): remove unreachable in_announcement guard

The is_announcement_active() check in announce() could never fire:
begin_announcement() is called after the check (so the flag is always
false when the guard runs), and the only re-entry vector — the inner
call loop — already rejects the announce selector before dispatch.

The flag also had no reset path, leaving the token permanently stuck
with in_announcement=true after announce() completed. The selector
check in the inner loop is the sole recursion defense, matching the
MockB20Asset reference implementation which carries no flag at all.

Remove in_announcement, is_announcement_active(), and begin_announcement()
from B20AssetToken, and drop the dead guard and call-site from dispatch.

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor(activation): remove redundant AlreadyDeactivated error

AlreadyDeactivated and FeatureNotActivated both represent the same
underlying state (features[feature] == false). The intended distinction
between mutation context (deactivate) and query context (checkActivated)
was never implemented: the mock collapses both into FeatureNotActivated.

Remove AlreadyDeactivated from the ABI to eliminate the dead declaration
and bring the Rust interface in line with the updated IActivationRegistry
spec and MockActivationRegistry behavior. Also clarifies set_activated
variable names to make the activate/deactivate branch intent explicit.

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

* style: apply rustfmt to activation/storage.rs

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

* chore: clippy

* style: apply rustfmt to activation/storage.rs

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>

* fix(chainspec,activation): reject zero activation admin address (base#3344)

* fix(chainspec,precompiles): reject zero activation admin address

Closes a backdoor where a chain misconfigured with
activation_admin_address == Some(Address::ZERO) would allow any deposit
transaction with msg.sender == Address::ZERO to toggle Beryl activation
state.

Layer 1 (config-time): add ZeroActivationAdminAddress error variant to
BaseChainSpecError and check for it in validate_beryl_activation_admin,
covering try_from_genesis, try_from_chainspec, and try_build paths.

Layer 2 (runtime defense-in-depth): add an unconditional zero-caller
guard at the top of set_activated in ActivationRegistryStorage so that
Address::ZERO callers are always rejected, even if a future code path
were to bypass config-time validation.

Adds tests for both layers.

* docs(activation): clarify admin() returns Address::ZERO to mean no admin, not a valid zero admin

Address::ZERO returned from admin() signals that no activation admin is
configured (activation_admin_address was None). Config-time validation
already rejects Some(Address::ZERO), so the zero return cannot mean a
configured admin in a valid chain spec. Add a doc comment making this
semantic explicit so future callers do not conflate the two cases.

* style(chainspec): move Address import to mod-level in tests

Two new test functions placed `use alloy_primitives::Address` inside
their function bodies. Move it to the existing module-level
alloy_primitives import block per project import placement rules.

* refactor(chainspec): extract beryl_scheduled local in validate_beryl_activation_admin

The Beryl fork condition was evaluated twice, once for the missing-admin
check and once for the zero-admin check. Extract it into a single
beryl_scheduled binding so both guards share the same expression and
cannot drift independently.

* fix(chainspec,precompiles): run cargo fmt, fix em dash, update stale doc comment

- Run cargo +nightly fmt to fix the ci/Format failure (the beryl_scheduled
  refactor commit was not formatted before pushing)
- Replace em dash with colon in admin() doc comment per project convention
- Update validate_beryl_activation_admin doc to describe both guards (missing
  and zero address) rather than only the original missing-address check

* refactor(chainspec): make ChainConfig.activation_admin_address required

All Base chains have Beryl scheduled and need an activation admin.
Using Address instead of Option<Address> lets the type reflect this;
Address::ZERO is still rejected by validate_beryl_activation_admin.

The missing-admin test for ChainConfig is removed since that code path
is now impossible. The genesis and builder paths retain Option<Address>
to handle JSON deserialization where the field may be absent.

* fix(chainspec,proof): update callsites for ChainConfig.activation_admin_address: Address

Three callsites that treated activation_admin_address as Option<Address>:
- boot.rs: and_then -> map to produce Option<Address> from the now-bare Address
- boot.rs test assert: wrap chain_config value in Some() for comparison
- succinct client boot.rs: wrap in Some() when assigning to BootInfo field
- spec.rs test: assign Address::ZERO directly instead of Some(Address::ZERO)

* fix(chains): add backticks to Address::ZERO in doc comment

* fix(precompile-storage): remove redundant drop in with_caller and harden CallerGuard (base#3404)

* fix(precompile-storage): remove redundant drop in with_caller and harden CallerGuard::drop

The explicit `let result = f(); drop(guard); result` pattern in `with_caller`
was redundant since `CallerGuard`'s `Drop` impl already restores the previous
caller on scope exit. Simplify to `let _guard = ...; f()` so RAII is the
sole restore mechanism.

Additionally, `CallerGuard::drop` previously called `with_storage` which uses
`RefCell::borrow_mut()` and panics on a conflicting borrow. This could abort
the process if `drop` ran during unwinding from a panic inside a borrowed
storage scope. Switch to `try_borrow_mut` so a conflicting borrow silently
skips the restore instead of panicking inside `Drop`.

Fixes BOP-337.

* fix(precompile-storage): harden CheckpointGuard::drop against double-panic

Apply the same try_borrow_mut hardening from CallerGuard::drop to
CheckpointGuard::drop. Using with_storage (which calls borrow_mut
internally) risks a second panic during unwinding if the RefCell is
already mutably borrowed, causing a process abort. Silently skipping
the revert on a conflicting borrow is the safe alternative.

* style(precompile-storage): collapse nested if-let in CallerGuard and CheckpointGuard drop

* docs(precompile-storage): document commit vs drop borrow strategy in CheckpointGuard

* fix(precompile-storage): replace let chains with nested if-let for edition compat

* feat(precompile-storage): add tracing::warn in CallerGuard and CheckpointGuard drop failure paths

* chore: update Cargo.lock for tracing dependency

* fix(precompile-storage): propagate tracing/std through std feature

* [backport] straggler fixes for releases/v1.1.0

Applies remaining small fixes and doc/test cleanups that were present
on main but missing from releases/v1.1.0 after the batch backports:

- set.rs: em-dash in test doc comment
- vec.rs: truncate/clear tests from base#3384
- activation/storage.rs: test ordering alignment
- b20_asset/token.rs: batch_mint authorization boundary doc (base#3367)
- b20_stablecoin/dispatch.rs: BOP-349/PSRC-27 reference in comment
- metrics.rs: use ctx.result_output() instead of into_precompile_result()

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Eric Liu <ericliu121187@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
* chore: update reth to v2.3.0

Align the workspace with the reth v2.3.0 release and migrate the related alloy and revm API changes.

Also fix the follow-up clippy and test breakages surfaced by the requested Justfile validation targets.

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* Fix reth 2.3 CI fallout

Persist test-harness canonical blocks immediately so follow-up payloads can resolve parent headers under reth 2.3.0, and update Jovian fixture headers plus deny skips for the new dependency graph.

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* Drop dead BAL post-execution check

Reth 2.3.0 added a BAL hash argument to post-execution consensus validation for Amsterdam-capable Ethereum paths. Base does not have an execution-derived BAL hash to compare, so stop threading the header value through and remove the tautological check.

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* Keep BAL hash at the trait boundary only

Accept the reth 2.3.0 BAL hash argument in the Base consensus trait impl, but stop threading it through Base-only helpers and tests where it is unused.

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* Fix rustfmt fallout in engine-tree validator

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* Use branch-based base-anvil workflow checkouts

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* Align release-branch precompile tests with reth 2.3

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* Refresh backport lockfile

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* Fix Jovian blob gas mismatch test fixture

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* ci: use default base-anvil branch

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* test(runner): avoid finalizing harness blocks

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* test(runner): trim harness persistence workaround

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* docs(execution): clarify reth 2.3 migration notes

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* fix(consensus): drop post-exec requests hash check

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* refactor(reth): trim non-essential migration behavior

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* ci: revert workflow-only reth migration changes

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* refactor(engine-tree): inline overlay builder construction

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

* ci: bump base-anvil workflow pin

Amp-Thread-ID: https://ampcode.com/threads/T-019eb1f4-e95e-7346-b575-6af1be02f038
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: Amp <amp@ampcode.com>
* Set Base EL peer defaults to 80

Apply Base-specific execution peer defaults in the execution CLI runtime config so EL nodes default to 80 inbound peers and 80 outbound peers when operators do not pass explicit overrides.

Add regression tests to verify the new defaults and preserve explicit CLI values.

Amp-Thread-ID: https://ampcode.com/threads/T-019eb8fc-371b-71ab-901d-499925b7f017
Co-authored-by: Amp <amp@ampcode.com>
(cherry picked from commit 41b523d)

* Fix rustfmt import order in backport

Amp-Thread-ID: https://ampcode.com/threads/T-019eb8fc-371b-71ab-901d-499925b7f017
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: Amp <amp@ampcode.com>
Conflicts resolved:
- Cargo.toml: version bumped to 1.1.0, reth v2.2.0→v2.3.0, revm/alloy
  version bumps; preserved firehose-only deps (reth-db-models,
  reth-tokio-util, alloy-rpc-types-trace)
- crates/common/chains/src/chain.rs: beryl activation test updated to
  upstream values (sepolia scheduled, zeronet timestamp updated)
- crates/common/chains/src/config.rs: zeronet beryl timestamp updated
  to 1_780_678_800, added cobalt_timestamp field
Upstream v1.1.0 includes a backport of reth v2.3.0, which is
binary-incompatible with reth-firehose (built against reth v2.2.0 /
revm-inspector v19). Reverted all reth-v2.3.0-specific API changes to
keep the workspace on reth v2.2.0 (streamingfast/reth firehose/2.x)
while still landing the v1.1.0 upstream features (Beryl/Cobalt
hardfork plumbing, chain config additions).

Also adapts the firehose-flashblocks streamer for the upstream
FlashblocksSubscriber API change (added ping_interval parameter).
Use the final streamingfast/reth v2.3.0-fh tag (reth v2.3.0, revm 40,
alloy-evm 0.36) instead of the intermediate alpha. Patch alloy-evm to the
SF fork so EVM system calls route through the Inspector, restoring Firehose
tracing of the EIP-4788 beacon-roots system call.
Merge v1.1.0, bump reth to 2.3.0, evm-alloy to 0.36.0-fh

Does generate a difference in system calls' gas limit:

```
-      "gasLimit": "30000000",
+      "gasLimit": "31566720",
```

this is due to how reth upstream changed that limit to that new value, which we reflect in our tracing.
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* chore(common): set mainnet activation date

Co-authored-by: Codex <codex-noreply@coinbase.com>

* fix ci after beryl mainnet scheduling

Co-authored-by: Codex <codex-noreply@coinbase.com>

* fix basectl unscheduled hardfork test

Co-authored-by: Codex <codex-noreply@coinbase.com>

---------

Co-authored-by: Codex <codex-noreply@coinbase.com>
* insert parent block hash into pending EVM

* typed errors for FlashblockId, use pair of Atomics

* fix test failures due to harness adjustment

* fmt fix

* chore: retrigger CI for release retarget
v1.1.1 (PR base#3603) added a prev_flashblock_id predecessor-link arg to
FlashblockSequenceValidator::validate and a NonSequentialPredecessor
result variant. Pass metadata.prev_flashblock_id from the firehose
processor, handle the new variant (reset + wait for base), and update
test Metadata literals for the added field.
Tests for the v1.1.1 NonSequentialPredecessor path in the firehose
flashblocks processor: mismatched prev_flashblock_id resets state,
matching id is accepted, default (unset) id stays backward-compatible.
Adds flash_delta_with_prev_id test helper.

Docs under docs/firehose/ answer four review questions: the reset
tests, Dockerfile.sf build status, Beryl block-model impact, and the
pending-EVM parent-hash fix.
Committed unintentionally via PR #13; these were review notes, not
meant to land in the tree.
@sduchesneau sduchesneau requested a review from maoueh June 19, 2026 18:43
@sduchesneau

Copy link
Copy Markdown
Author

wrong target for branch

@maoueh maoueh deleted the fix/beryl-b20-logs branch June 19, 2026 19:12
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.