Skip to content

test(precompiles): add golden tests for asset V1 (BOP-423)#3972

Open
stephancill wants to merge 10 commits into
mainfrom
stephancilliers/bop-423-asset-golden
Open

test(precompiles): add golden tests for asset V1 (BOP-423)#3972
stephancill wants to merge 10 commits into
mainfrom
stephancilliers/bop-423-asset-golden

Conversation

@stephancill

@stephancill stephancill commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a golden/snapshot test suite pinning current Asset V1 behavior of the B-20 precompile (BOP-423), before/while it is frozen behind the versioning seam from #3969 (BOP-417). Sibling of the stablecoin golden suite (#3971).

Every op is driven through the version-resolver-gated path (inner(..., BaseUpgrade::Beryl)AssetVersions::from_base_upgrade → V1) against the real EVM-backed B20AssetStorage over HashMapStorageProvider, with InMemoryPolicy for deterministic allow/block. Each mutation case asserts:

  1. exact returned ABI bytes (or the typed revert),
  2. resulting state (balances / supply / roles / allowances / multiplier / metadata / storage),
  3. emitted event signature(s), and
  4. a pinned keccak storage hash over the sorted storage triples (hash_state) — a reference baseline for the CI frozen-manifest check (BOP-421).

Plus a dedicated golden_gas_footprints test pinning each mutating op's storage-access footprint — the (SLOAD, SSTORE, KECCAK256) op counts, the deterministic gas-schedule-independent signal that drives real gas — so an extra storage access in V1 is caught even when bytes/state/events match.

Op coverage: the shared B-20 surface (transfer / transferFrom / approve + memo variants, mint / burn / burnBlocked, pause / unpause, updateSupplyCap|Name|Symbol|ContractURI, grant/revoke/renounce/renounceLastAdmin/setRoleAdmin, updatePolicy, permit, all reads + role/policy constants), plus the asset-specific ops: updateMultiplier (operator role, zero-reject), updateExtraMetadata (metadata role, empty-key reject), batchMint (mint role, length-mismatch / empty-batch), announce (operator role, internal-call loop, reused-id, nested-announce, malformed / failing internal call), and the scaled-balance reads (multiplier, toScaledBalance, toRawBalance, scaledBalanceOf, configurable decimals). Privileged vs unprivileged via inner_with_privilege vs inner; the guard envelope (nonpayable / uninitialized / pre-Beryl) via dispatch_with_observer; plus the factory grant_role_unchecked bootstrap.

Edge cases: zero-address (sender / receiver / approver), insufficient balance/allowance, supply-cap, paused, policy-blocked sender + executor policy, empty-feature-set, last-admin protection (revoke/renounce), expired permit, and every unprivileged authorization revert (caller lacking the required role) — including the asset-specific batchMint / updateExtraMetadata / announce / updateMultiplier role checks.

Meta coverage guard: a compile-time v1_op_coverage_checklist — an exhaustive match over the generated IB20Calls / IB20AssetCalls enums whose arms reference the covering golden #[test] fns. Adding an ABI op fails the build (non-exhaustive match) until a golden is added; renaming/removing a golden fails the build (missing fn reference). This intentionally couples to the shared ABI surface: when a future version extends the ABI, this suite is expected to be updated to assert the new op reverts for V1.

No source/logic changes — frozen V1 is untouched; only a new integration test file and one [[test]] stanza are added.

Coverage

Region/line coverage of the frozen logic from this suite alone (via cargo llvm-cov, this test target only):

  • b20_asset/logic/v1.rs: 100% line, 100% function, 91.1% region
  • b20_asset/dispatch.rs: 97.2% line, 92.7% region

Remaining uncovered regions are defensive checked_*().ok_or_else(under_overflow) closures (incl. the scaled-balance overflow guard) and &&/|| short-circuit halves — unreachable without contriving arithmetic overflow.

Testing

  • cargo test -p base-common-precompiles --features test-utils --test b20_asset_v1_golden98 passed
  • full crate (cargo test -p base-common-precompiles --features test-utils) green; clippy (--all-targets) + fmt clean

State hashes are pinned; regenerate with BLESS_GOLDEN=1 cargo test ... -- --nocapture (documented in the file header).

Co-authored-by: OpenCode <opencode-noreply@coinbase.com>
@linear

linear Bot commented Jul 15, 2026

Copy link
Copy Markdown

BOP-423

@cb-heimdall

Copy link
Copy Markdown
Collaborator

🟡 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

stephancill and others added 3 commits July 15, 2026 22:41
Co-authored-by: OpenCode <opencode-noreply@coinbase.com>
Co-authored-by: OpenCode <opencode-noreply@coinbase.com>
…pwire

Co-authored-by: OpenCode <opencode-noreply@coinbase.com>
Comment on lines +24 to +29
use alloy_sol_types::{SolCall, SolError, SolEvent, SolInterface, SolValue};
use base_common_genesis::BaseUpgrade;
use base_common_precompiles::{
Asset, AssetAccounting, AssetV1, AssetVersion, AssetVersions, B20_MAX_SUPPLY_CAP, B20AssetInit,
B20AssetStorage, B20AssetToken, B20PolicyType, B20TokenRole, IB20, IB20Asset, InMemoryPolicy,
NoopPrecompileCallObserver, PermitArgs, TokenAccounting,

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.

Three unused imports: SolError (line 24), Asset and TokenAccounting (lines 27, 29). Clippy should catch these — remove them to keep CI clean.

Suggested change
use alloy_sol_types::{SolCall, SolError, SolEvent, SolInterface, SolValue};
use base_common_genesis::BaseUpgrade;
use base_common_precompiles::{
Asset, AssetAccounting, AssetV1, AssetVersion, AssetVersions, B20_MAX_SUPPLY_CAP, B20AssetInit,
B20AssetStorage, B20AssetToken, B20PolicyType, B20TokenRole, IB20, IB20Asset, InMemoryPolicy,
NoopPrecompileCallObserver, PermitArgs, TokenAccounting,
use alloy_sol_types::{SolCall, SolEvent, SolInterface, SolValue};
use base_common_genesis::BaseUpgrade;
use base_common_precompiles::{
AssetAccounting, AssetV1, AssetVersion, AssetVersions, B20_MAX_SUPPLY_CAP, B20AssetInit,
B20AssetStorage, B20AssetToken, B20PolicyType, B20TokenRole, IB20, IB20Asset, InMemoryPolicy,
NoopPrecompileCallObserver, PermitArgs,

stephancill and others added 6 commits July 16, 2026 15:09
Co-authored-by: OpenCode <opencode-noreply@coinbase.com>
Co-authored-by: OpenCode <opencode-noreply@coinbase.com>
… goldens

Co-authored-by: OpenCode <opencode-noreply@coinbase.com>
Co-authored-by: OpenCode <opencode-noreply@coinbase.com>
Co-authored-by: OpenCode <opencode-noreply@coinbase.com>
… (review)

Co-authored-by: OpenCode <opencode-noreply@coinbase.com>
@github-actions

Copy link
Copy Markdown
Contributor

Review Summary

Test-only PR adding 2711 lines of golden/snapshot tests for Asset V1. No production code changes.

Finding

Unused imports (already flagged inline): SolError, Asset, and TokenAccounting are imported but never used. The existing inline comment covers this — note that its suggestion to add SolInterface is also unused and should be omitted from the fix.

General observations

  • The compile-time v1_op_coverage_checklist (exhaustive match over IB20Calls/IB20AssetCalls without a wildcard arm, with covered(&[...]) referencing actual #[test] fns) is a solid pattern for ensuring golden coverage stays in sync with the ABI surface.
  • hash_state capacity hint (events.len() * 64) underestimates for events with >2 topics or large data payloads, but this only affects pre-allocation efficiency in tests — not correctness.
  • All .unwrap() / .expect() usage is within test code, which is appropriate.
  • Cargo.toml change is clean: [[test]] stanza placed correctly before [features], [lints] workspace = true already present.

@github-actions

github-actions Bot commented Jul 16, 2026

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

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.

2 participants