Skip to content

test(precompiles): add golden tests for factory V1 (BOP-424)#4015

Open
stephancill wants to merge 1 commit into
mainfrom
stephancilliers/bop-424-factory-goldens
Open

test(precompiles): add golden tests for factory V1 (BOP-424)#4015
stephancill wants to merge 1 commit into
mainfrom
stephancilliers/bop-424-factory-goldens

Conversation

@stephancill

Copy link
Copy Markdown
Contributor

What

Adds a golden/snapshot suite pinning Factory V1 behavior of the B-20 precompile (crates/common/precompiles/tests/b20_factory_v1_golden.rs), driven through the real B20FactoryStorage dispatch entry. Covers all 4 ops and every validation guard:

  • createB20 — asset + stablecoin success (created token state/code + B20Created event), with initCalls (asset & stablecoin), zero-admin (skips role grant), and guards: TokenAlreadyExists, UnsupportedVersion, InvalidDecimals, MissingRequiredField, InvalidCurrency, InitCallFailed, typed-revert propagation from an init call, malformed params, not-activated, and NonPayable.
  • getB20Address — deterministic address derivation (asset + stablecoin).
  • isB20 / isB20Initialized — prefix + factory-initialized reads.

Each case asserts exact returned bytes (or typed revert), the created token's resulting state, emitted events, and a per-case keccak storage-hash snapshot scoped to the factory + created token (excludes activation scaffolding); plus per-op storage-access gas footprints and a compile-time op-coverage checklist over IB20FactoryCalls.

Why

Baseline for the frozen-manifest check (BOP-422/BOP-424). The pins were authored and blessed against the shipped v1.1.1 (pre-versioned) factory implementation, then confirmed to pass unchanged on the versioned (resolver-gated) structure — proving the SOLID/versioned migration (BOP-418) is behavior-preserving.

Testing

cargo test -p base-common-precompiles --features test-utils --test b20_factory_v1_golden   # 21 passed
cargo test -p base-common-precompiles --features test-utils                                # full crate green
cargo clippy -p base-common-precompiles --features test-utils --all-targets                # clean
cargo +nightly fmt -p base-common-precompiles -- --check                                   # clean

~96% line coverage of b20_factory/logic/v1.rs (remainder are defensive ? error edges + the unused non-observer create_b20 alt-API). Test-only: new integration test + one Cargo.toml [[test]] stanza; no source/logic changes.

@linear

linear Bot commented Jul 17, 2026

Copy link
Copy Markdown

BOP-424

@cb-heimdall

cb-heimdall commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

✅ Heimdall Review Status

Requirement Status More Info
Reviews 1/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

@stephancill
stephancill force-pushed the stephancilliers/bop-424-factory-goldens branch from 7318c75 to b67edca Compare July 17, 2026 16:53
Comment on lines +642 to +656
#[test]
fn golden_create_reverts_malformed_params() {
let mut s = fresh();
let (rev, _bytes) = call_factory(
&mut s,
CREATOR,
create_call(
IB20Factory::B20Variant::ASSET,
SALT,
Bytes::from(vec![0xaa_u8, 0xbb, 0xcc]),
vec![],
),
);
assert!(rev);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: For a golden test that pins exact behavior, discarding the revert bytes (_bytes) means the specific revert selector/payload isn't pinned. If the factory changes which error it returns for malformed params, this test would still pass. Same applies to golden_create_reverts_when_not_activated below. Consider asserting the exact revert bytes (or at minimum the error selector) to match the thoroughness of the other revert tests.

Pins Factory V1 behavior of the B-20 precompile: token creation flows
(createB20 asset + stablecoin, with/without initCalls, zero-admin), deterministic
address derivation (getB20Address), prefix/initialized reads (isB20,
isB20Initialized), and every validation guard (TokenAlreadyExists,
UnsupportedVersion, InvalidDecimals, MissingRequiredField, InvalidCurrency,
InitCallFailed, typed-revert propagation, malformed params, not-activated,
NonPayable). Each case asserts returned bytes/typed reverts, the created token's
state + code, emitted events (B20Created + init events), and a per-case keccak
storage-hash snapshot scoped to the factory + created token; plus per-op gas
footprints and a compile-time op-coverage checklist.

Authored and blessed against the shipped v1.1.1 (pre-versioned) factory, then
confirmed identical pins pass on the versioned (resolver-gated) structure -
proving the SOLID/versioned migration (BOP-418) is behavior-preserving. ~96%
line coverage of b20_factory/logic/v1.rs (remainder are defensive error edges).

Test-only: new integration test + one Cargo.toml [[test]] stanza; no
source/logic changes.

Co-authored-by: OpenCode <opencode-noreply@coinbase.com>
@stephancill
stephancill force-pushed the stephancilliers/bop-424-factory-goldens branch from b67edca to bd96a39 Compare July 17, 2026 21:00
@stephancill
stephancill enabled auto-merge July 17, 2026 21:06
@stephancill
stephancill requested a review from rayyan224 July 17, 2026 21:06
Comment on lines +684 to +688
StorageCtx::enter(&mut s, |ctx| {
B20FactoryStorage::new(ctx).dispatch(ctx, &calldata, BaseUpgrade::Beryl)
})
.expect("gas-footprint op must succeed");
(s.counter_sload(), s.counter_sstore(), s.counter_keccak256())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The gas() helper unwraps the outer Result but doesn't assert that the dispatch actually succeeded (i.e. !output.is_revert()). If a future refactor introduces a bug that makes one of these calls revert, this test would silently measure the revert-path gas footprint rather than failing.

Suggested change
StorageCtx::enter(&mut s, |ctx| {
B20FactoryStorage::new(ctx).dispatch(ctx, &calldata, BaseUpgrade::Beryl)
})
.expect("gas-footprint op must succeed");
(s.counter_sload(), s.counter_sstore(), s.counter_keccak256())
let out = StorageCtx::enter(&mut s, |ctx| {
B20FactoryStorage::new(ctx).dispatch(ctx, &calldata, BaseUpgrade::Beryl)
})
.expect("gas-footprint op must not fatally error");
assert!(!out.is_revert(), "gas-footprint op must not revert");

@github-actions

Copy link
Copy Markdown
Contributor

Review Summary

Well-structured golden test suite with thorough coverage of Factory V1 operations. The compile-time op-coverage checklist is a clever pattern for ensuring new ABI ops get test coverage. Two items noted:

Findings

  1. gas() helper doesn't assert success (b20_factory_v1_golden.rs:684-688): The helper unwraps the outer Result but never checks is_revert() on the dispatch output. If a future change causes one of the measured calls to revert, the test would silently measure the revert-path gas footprint instead of failing. See inline comment with suggested fix.

  2. Discarded revert bytes in two tests (b20_factory_v1_golden.rs:645,662): golden_create_reverts_malformed_params and golden_create_reverts_when_not_activated bind the revert bytes to _bytes without asserting the error selector or payload. For a golden suite that pins exact behavior, this leaves the specific revert type unpinned. (Already raised in prior review.)

@github-actions

Copy link
Copy Markdown
Contributor

❌ base-std fork tests did not run

The build or setup step failed before any tests could execute. Check the workflow logs for details.

Dependency Ref Commit
base-std main 4658f1b7
base-anvil f4370bc97756557d0ef1a6c325ad7d2532e71112 f4370bc9

@stephancill
stephancill added this pull request to the merge queue Jul 17, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Jul 17, 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.

3 participants