Skip to content

refactor(contracts): EVM accessor pattern — compile-time test-override generator (BBND-1672)#1222

Open
MiguelLZPF wants to merge 4 commits into
developmentfrom
feature/BBND-1672-evm-accessor-pattern
Open

refactor(contracts): EVM accessor pattern — compile-time test-override generator (BBND-1672)#1222
MiguelLZPF wants to merge 4 commits into
developmentfrom
feature/BBND-1672-evm-accessor-pattern

Conversation

@MiguelLZPF
Copy link
Copy Markdown
Contributor

@MiguelLZPF MiguelLZPF commented Jun 1, 2026

Description

Replaces the runtime TimeTravel storage-wrapper time-control pattern with a compile-time, manifest-driven EvmAccessors generator.

Production mode — native-opcode getters (block.timestamp, block.number) with zero test machinery compiled in.
Test mode — slot-fallback getters plus override writers exposed via a test-only EvmAccessorsFacet, injected into the diamond at deploy time.

Key integration points:

Jira: BBND-1672

How it works

One source API — EvmAccessors.getX() — compiled into two different bodies chosen at compile time by isTestMode(). Production gets the bare opcode (zero overhead); tests get an override slot with a sentinel fallback, driven by a test-only facet.

  ┌──────────────────────────────────────────────────────────────────┐
  │  manifest.ts  ·  ONE line per native value (the source of truth)  │
  │     block.timestamp · block.number · msg.sender · block.chainid   │
  └───────────────────────────────┬──────────────────────────────────┘
                                  │  generator.ts reads it at compile time
                   isTestMode()?  │  (ATS_TEST_MODE env / the hardhat task)
              ┌───────────────────┴────────────────────┐
          NO (production)                          YES (tests)
              │                                         │
              ▼                                         ▼
  ┌──────────────────────────┐      ┌───────────────────────────────────────┐
  │ EvmAccessors.sol (PROD)  │      │ EvmAccessors.sol (TEST)               │
  │ getBlockTimestamp() {    │      │ getBlockTimestamp() {                 │
  │   return block.timestamp │      │   v = overrideSlot.timestamp          │
  │ }                        │      │   return v==0 ? block.timestamp : v   │  sentinel
  │ -> just the opcode:      │      │ }                                     │  fallback
  │    no slot, no SLOAD,    │      │ + setTimestampOverride(...)  (writer)  │
  │    no test code. 0 gas.  │      │ + ERC-7201 override storage slot      │
  └──────────────────────────┘      └──────────────────┬────────────────────┘
              │                                         │ writers poked by
   CI gate (accessors:check:prod)                      v
   fails the build if any override    ┌───────────────────────────────────────┐
   machinery leaks into PROD          │ EvmAccessorsFacet (test-only facet)   │
                                      │   changeSystemTimestamp / Sender /     │
                                      │   ChainId / Blocknumber  (+ resets)    │
                                      └───────────────────────────────────────┘

Example — time-travel with no real time passing (the P1.1 guard test):

  test -> facet.changeSystemTimestamp(2030)  writes the override slot
       -> LockFacet.release() reads EvmAccessors.getBlockTimestamp() -> 2030
       -> the lock counts as expired, release succeeds

Why it beats the old pattern: TimeTravelStorageWrapper baked an SLOAD + branch into every native read in both prod and test — production paid a tax for a tests-only feature. Moving the decision to compile time gives prod the bare opcode (smaller bytecode, no override slot to attack) from a single manifest line.

Type of change

  • Bug fix 🐞
  • New feature ✨
  • Breaking change 💥
  • Documentation update 📖
  • Refactor 🔧

Testing

Full contract suite run locally against Hardhat:

  • npm run ats:contracts:build — green (TypeChain + registry regenerated)
  • npm run ats:contracts:format — green (no drift)
  • npm run ats:contracts:lint — green (no new warnings)
  • npm run ats:contracts:test3701 passing, 5 pending, 0 failing

Test Results (if any)

3701 passing · 5 pending · 0 failing

Node version:

  • 20
  • 22
  • 24

Checklist

  • Style Guidelines followed ✅
  • Documentation Updated 📚
  • Linters - No New Warnings ⚠️
  • Local Tests Pass ✅
  • Effective Tests Added ✔️
  • No reduction of Coverage

@MiguelLZPF MiguelLZPF self-assigned this Jun 1, 2026
@github-actions github-actions Bot added the hash-change PR modifies @custom:hash annotations or codegen formula — review on-chain impact carefully label Jun 1, 2026
@MiguelLZPF MiguelLZPF force-pushed the feature/BBND-1672-evm-accessor-pattern branch 4 times, most recently from 205efcf to 9c87a33 Compare June 1, 2026 14:46
@MiguelLZPF MiguelLZPF marked this pull request as ready for review June 1, 2026 14:53
@MiguelLZPF MiguelLZPF requested review from a team as code owners June 1, 2026 14:53
Add a declarative manifest plus generator that emits EvmAccessors.sol in two
variants: production native-opcode getters (zero override machinery) and a
test variant backed by ERC-7201 override storage with per-accessor readers
and writers, selected by Configuration.isTestMode. The generated library is
gitignored and prettier-ignored, regenerated before solc on every compile. A
static-checks CI gate (ats:contracts:accessors:check:prod) compiles in prod
mode and asserts the production artifact carries no override storage,
assembly, reader or writer. Removes the obsolete naming utility.

Signed-off-by: Miguel_LZPF <miguel.carpena@io.builders>
…nd wrappers (BBND-1672)

Migrate every block.timestamp, block.number, msg.sender and block.chainid read
from the TimeTravelStorageWrapper runtime pattern to the compile-time
EvmAccessors library. Delete the entire testTimeTravel tree, the dead
ContextProvider, and the hand-written EvmAccessors.sol. Add the test-only
EvmAccessorsFacet writer (changeSystemTimestamp / Blocknumber / ChainId /
Sender) and restore MockFactory to mark the facet ready post-deploy. Every
facet sender read now routes through EvmAccessors.getMsgSender().

Signed-off-by: Miguel_LZPF <miguel.carpena@io.builders>
…nvironment (BBND-1672)

Replace the TimeTravel-variant branching in the deployment and registry
tooling with a single isTestMode-driven facet list built by facetEnvironment.
Drop the useTimeTravel parameter from every createConfiguration entry point
and update the registry generator, scanner, CLI, and workflow scripts.

Signed-off-by: Miguel_LZPF <miguel.carpena@io.builders>
…t (BBND-1672)

Add unit coverage for the generator (with drift-guard snapshots), the
isTestMode config helper and buildFacetList, plus integration coverage for
EvmAccessorsFacet: timestamp/blocknumber/chainid/sender override change,
reset and zero-guards, override coexistence, and override-observed-by-a-real-
facet guards (Lock release gate for timestamp, AccessControl resolution for
sender). Refresh fixtures and developer guides, and add the release changeset.

Signed-off-by: Miguel_LZPF <miguel.carpena@io.builders>
@MiguelLZPF MiguelLZPF force-pushed the feature/BBND-1672-evm-accessor-pattern branch from 9c87a33 to 1439268 Compare June 1, 2026 15:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hash-change PR modifies @custom:hash annotations or codegen formula — review on-chain impact carefully

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant