From ff1608b816c3ad593f938aac7362e4c2c8066b15 Mon Sep 17 00:00:00 2001 From: Soheima M Date: Fri, 12 Jun 2026 14:22:25 +0200 Subject: [PATCH 1/3] added agents-md --- .gitignore | 1 + AGENTS.md | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 9d730f9..5372477 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ out/ cache/ broadcast/ +lcov.info # Dependencies (we explicitly do NOT vendor third-party libs; # this is here only for any future Foundry-managed installs). diff --git a/AGENTS.md b/AGENTS.md index b714b8c..8a7742d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,94 @@ # AGENTS.md -## Branching +Solidity interfaces, libraries, and reference mocks for Base's precompiles: **B20** (ERC-20 superset +with roles, policies, pausing, permits, memos), **PolicyRegistry** (allowlist/blocklist singleton), +and **ActivationRegistry** (feature flags). The Solidity mocks must mirror the Rust implementations +in base/base **slot-for-slot** — storage layout parity is the core invariant of this repo. + +## Commands + +```bash +forge build # compile (solc 0.8.30) +forge test -v # unit tests against mocked precompiles (fast; run first) +forge test -v --match-test # one test; --match-contract for one suite +forge fmt # format Solidity (CI gates on `forge fmt --check`) +python3 script/check-coverage.py # every interface function has a test (CI gate) +make coverage # lcov + HTML report (needs genhtml) + +make smoke-setup # one-time venv setup — requires Python 3.13 exactly +make smoke-all # live-RPC smoke journeys (needs .env; see below) +make fork-tests # unit suite vs the real Rust precompiles (patched anvil/forge) +``` + +Test tiers, in the order to reach for them: **unit** (mocks, no network) → **fork** (local anvil +with Rust precompiles, cross-validates layout/behavior) → **smoke** (real txs against a live chain). + +## Testing + +- Unit tests live in `test/unit/{Feature}/{Name}.t.sol`; names follow + `test_{function}_{outcome}_{variant}`, e.g. `test_allowance_success_zeroByDefault`. +- Tests assert precompile storage directly via `vm.load` against the slot constants in + `test/lib/mocks/MockB20Storage.sol`. **A fork-test failure with passing unit tests means the + Solidity reference and the Rust impl have diverged** — see `FORK_TESTING.md`, don't just patch + the test. +- Fork tests need the patched binaries from [base/base-anvil](https://github.com/base/base-anvil) + (`--base` flag); **stock forge/anvil will not work**. Point at them with `ANVIL_BIN` / + `FORGE_BIN`. Details: `script/fork/README.md`. Pass forge args via + `make fork-tests ARGS="-vvvv --match-test "`. +- Smoke tests need `.env` (copy `.env.template`): `RPC_URL`, `DEPLOYER_PK`, `USER2_PK` — + **testnet keys only**, both accounts funded. Journeys **skip (not fail)** when the target chain + hasn't activated the feature — a skip is not a pass. Run one journey with + `make smoke-{factory,asset,stablecoin,policy,invariants}`; audit mode: + `make smoke-all KEEP_GOING=1`. +- Fuzz runs: 256 default, 10 under `FOUNDRY_PROFILE=fork` (RPC round-trips are slow). + +## Project structure + +``` +src/StdPrecompiles.sol # canonical precompile addresses + typed handles +src/interfaces/ # IB20, IB20Asset, IB20Stablecoin, IB20Factory, IPolicyRegistry, IActivationRegistry +src/lib/ # B20Constants (role/policy ids), B20FactoryLib (createB20 encoders) +src/impls/ # reserved for reference impls (currently empty; mocks fill that role) +test/unit/ # one directory per feature; slot-level assertions +test/lib/ # BaseTest.sol, B20Test.sol, mocks/ (reference behavior + storage layout) +script/smoke/ # Python 3.13 live-node smoketest (web3.py); see script/smoke/README.md +script/fork/ # Python fork-test runner (anvil + patched forge); see script/fork/README.md +docs/ # specs: docs/B20/README.md, docs/PolicyRegistry/, docs/ActivationRegistry/ +``` + +Deeper reading: `FORK_TESTING.md` (cross-validation architecture), `docs/B20/README.md` (B20 spec). + +## Code style + +- Solidity formatting comes from `foundry.toml`: 120-char lines, 4-space tabs, double quotes, + long int types (`uint256`, never `uint`). Run `forge fmt` before committing. +- Interfaces use pragma `>=0.8.20 <0.9.0` (consumer compatibility); test/mock code targets 0.8.30. +- Errors are custom types with parameters, e.g. `error InvalidSupplyCap(uint256 currentSupply, + uint256 proposedCap)` — never `require` strings. +- Import via the public remappings `base-std/=src/` and `base-std-test/=test/`. +- Mock state uses ERC-7201 namespaced storage; new state must follow the same pattern. +- Python (`script/`): 3.13, PEP 8, snake_case functions, frozen dataclasses for config. + `web3>=7.6,<8` is the only dependency; both runners share `script/smoke/.venv`. + +## Git workflow - **`main` is the default branch.** Branch from `main` and open PRs against `main`. -- Ignore `master` — it is stale. Do not branch from it, target it in PRs, or use it as a base reference. +- Ignore `master` — it is stale. Do not branch from it, target it in PRs, or use it as a base + reference. +- Conventional Commits with optional scope: `feat(b20): ...`, `fix(smoke): ...`, `test:`, `docs:`, + `chore:`. Put the rationale in the body, not just the what. +- CI on every PR: `forge build`, `forge test`, `forge fmt --check`, + `python3 script/check-coverage.py`, coverage comment. Fork tests run in a separate workflow. + +## Boundaries + +- **Don't change the precompile addresses** in `src/StdPrecompiles.sol` or the feature IDs in + `script/smoke/config.py` / `test/lib/mocks/ActivationRegistryFeatureList.sol` — they are + canonical constants shared with base/base; coordinate changes there first. +- **Don't reshape mock storage layout unilaterally** — match `MockB20Storage.sol` slot constants + and cross-validate with `make fork-tests` before merging. +- **Don't commit `.env` or real private keys** — `.env` is gitignored; use funded testnet keys. +- **Don't hand-edit ABIs for the smoke tests** — `script/smoke/abis.py` loads them from `out/`; + run `forge build` instead. +- **Don't weaken a slot-assertion test to make fork tests pass** — investigate the divergence via + `FORK_TESTING.md` and fix the side that's wrong. From 53cfb403d773d057616c0f5c59a0032cd55172ae Mon Sep 17 00:00:00 2001 From: soheima Date: Wed, 17 Jun 2026 11:33:56 +0100 Subject: [PATCH 2/3] updated agents.md --- .gitmodules | 3 +++ AGENTS.md | 42 ++++++++++++++++++++---------------- consumer-focs/application.md | 0 consumer-focs/quickstart.md | 0 lib/base-std | 1 + 5 files changed, 28 insertions(+), 18 deletions(-) create mode 100644 consumer-focs/application.md create mode 100644 consumer-focs/quickstart.md create mode 160000 lib/base-std diff --git a/.gitmodules b/.gitmodules index 888d42d..87b9c1e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "lib/base-std"] + path = lib/base-std + url = https://github.com/base/base-std diff --git a/AGENTS.md b/AGENTS.md index 8a7742d..21a3438 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,26 +15,32 @@ forge fmt # format Solidity (CI gates on `forge fmt - python3 script/check-coverage.py # every interface function has a test (CI gate) make coverage # lcov + HTML report (needs genhtml) +base-forge test # same suite vs the real Rust precompiles, in-process (no node); auto-detects make smoke-setup # one-time venv setup — requires Python 3.13 exactly make smoke-all # live-RPC smoke journeys (needs .env; see below) -make fork-tests # unit suite vs the real Rust precompiles (patched anvil/forge) +make fork-tests # same suite vs a real base-anvil node (CI harness; patched anvil/forge) ``` -Test tiers, in the order to reach for them: **unit** (mocks, no network) → **fork** (local anvil -with Rust precompiles, cross-validates layout/behavior) → **smoke** (real txs against a live chain). +Test tiers, in the order to reach for them: **unit** (mocks, no network) → **live precompile** +(real Rust precompiles — in-process via `base-forge test`, or vs a node via `make fork-tests`; +cross-validates layout/behavior) → **smoke** (real txs against a live chain). ## Testing - Unit tests live in `test/unit/{Feature}/{Name}.t.sol`; names follow `test_{function}_{outcome}_{variant}`, e.g. `test_allowance_success_zeroByDefault`. - Tests assert precompile storage directly via `vm.load` against the slot constants in - `test/lib/mocks/MockB20Storage.sol`. **A fork-test failure with passing unit tests means the - Solidity reference and the Rust impl have diverged** — see `FORK_TESTING.md`, don't just patch - the test. -- Fork tests need the patched binaries from [base/base-anvil](https://github.com/base/base-anvil) - (`--base` flag); **stock forge/anvil will not work**. Point at them with `ANVIL_BIN` / - `FORGE_BIN`. Details: `script/fork/README.md`. Pass forge args via - `make fork-tests ARGS="-vvvv --match-test "`. + `test/lib/mocks/MockB20Storage.sol`. **A live-precompile failure with passing unit tests means the + Solidity reference and the Rust impl have diverged** — see `LIVE_PRECOMPILE_TESTING.md`, don't just + patch the test. +- Live-precompile tests need the patched binaries from + [base/base-anvil](https://github.com/base/base-anvil); **stock forge/anvil will not work**. Install + them alongside stock Foundry with `base-foundryup` (it never touches your `forge`/`anvil`). Common + path — in-process, no node: `base-forge test`; `BaseTest` auto-detects and `setUp` logs **LIVE + PRECOMPILE mode** vs **REFERENCE mode**. CI/node path: `make fork-tests` boots `anvil --base` and + activates the gated features for you (override the binaries with `ANVIL_BIN` / `FORGE_BIN`). Pass + forge args via `make fork-tests ARGS="-vvvv --match-test "`. Details: + `LIVE_PRECOMPILE_TESTING.md`, `script/fork/README.md`. - Smoke tests need `.env` (copy `.env.template`): `RPC_URL`, `DEPLOYER_PK`, `USER2_PK` — **testnet keys only**, both accounts funded. Journeys **skip (not fail)** when the target chain hasn't activated the feature — a skip is not a pass. Run one journey with @@ -50,19 +56,21 @@ src/interfaces/ # IB20, IB20Asset, IB20Stablecoin, IB20Factory, IPolic src/lib/ # B20Constants (role/policy ids), B20FactoryLib (createB20 encoders) src/impls/ # reserved for reference impls (currently empty; mocks fill that role) test/unit/ # one directory per feature; slot-level assertions +test/regression/ # interface renames/removals guard (B20Renames.t.sol, B20Removals.t.sol) test/lib/ # BaseTest.sol, B20Test.sol, mocks/ (reference behavior + storage layout) script/smoke/ # Python 3.13 live-node smoketest (web3.py); see script/smoke/README.md -script/fork/ # Python fork-test runner (anvil + patched forge); see script/fork/README.md +script/fork/ # node-based live-precompile runner (anvil + patched forge); see script/fork/README.md docs/ # specs: docs/B20/README.md, docs/PolicyRegistry/, docs/ActivationRegistry/ ``` -Deeper reading: `FORK_TESTING.md` (cross-validation architecture), `docs/B20/README.md` (B20 spec). +Deeper reading: `LIVE_PRECOMPILE_TESTING.md` (cross-validation architecture), `docs/B20/README.md` (B20 spec). ## Code style - Solidity formatting comes from `foundry.toml`: 120-char lines, 4-space tabs, double quotes, long int types (`uint256`, never `uint`). Run `forge fmt` before committing. -- Interfaces use pragma `>=0.8.20 <0.9.0` (consumer compatibility); test/mock code targets 0.8.30. +- Interfaces use pragma `>=0.8.20 <0.9.0` (consumer compatibility); test/mock code uses `^0.8.20`, + compiled with the pinned solc 0.8.30 from `foundry.toml`. - Errors are custom types with parameters, e.g. `error InvalidSupplyCap(uint256 currentSupply, uint256 proposedCap)` — never `require` strings. - Import via the public remappings `base-std/=src/` and `base-std-test/=test/`. @@ -73,12 +81,10 @@ Deeper reading: `FORK_TESTING.md` (cross-validation architecture), `docs/B20/REA ## Git workflow - **`main` is the default branch.** Branch from `main` and open PRs against `main`. -- Ignore `master` — it is stale. Do not branch from it, target it in PRs, or use it as a base - reference. - Conventional Commits with optional scope: `feat(b20): ...`, `fix(smoke): ...`, `test:`, `docs:`, `chore:`. Put the rationale in the body, not just the what. - CI on every PR: `forge build`, `forge test`, `forge fmt --check`, - `python3 script/check-coverage.py`, coverage comment. Fork tests run in a separate workflow. + `python3 script/check-coverage.py`, coverage comment. Live-precompile tests run in a separate workflow. ## Boundaries @@ -90,5 +96,5 @@ Deeper reading: `FORK_TESTING.md` (cross-validation architecture), `docs/B20/REA - **Don't commit `.env` or real private keys** — `.env` is gitignored; use funded testnet keys. - **Don't hand-edit ABIs for the smoke tests** — `script/smoke/abis.py` loads them from `out/`; run `forge build` instead. -- **Don't weaken a slot-assertion test to make fork tests pass** — investigate the divergence via - `FORK_TESTING.md` and fix the side that's wrong. +- **Don't weaken a slot-assertion test to make live-precompile tests pass** — investigate the divergence via + `LIVE_PRECOMPILE_TESTING.md` and fix the side that's wrong. diff --git a/consumer-focs/application.md b/consumer-focs/application.md new file mode 100644 index 0000000..e69de29 diff --git a/consumer-focs/quickstart.md b/consumer-focs/quickstart.md new file mode 100644 index 0000000..e69de29 diff --git a/lib/base-std b/lib/base-std new file mode 160000 index 0000000..d4b531c --- /dev/null +++ b/lib/base-std @@ -0,0 +1 @@ +Subproject commit d4b531cde26e920b4166025c2502c5674fa42de6 From 863cb7c6fab7ae0946834847617220ed4ae4f081 Mon Sep 17 00:00:00 2001 From: soheima Date: Thu, 18 Jun 2026 14:48:32 +0200 Subject: [PATCH 3/3] updated --- consumer-focs/application.md | 0 consumer-focs/quickstart.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 consumer-focs/application.md delete mode 100644 consumer-focs/quickstart.md diff --git a/consumer-focs/application.md b/consumer-focs/application.md deleted file mode 100644 index e69de29..0000000 diff --git a/consumer-focs/quickstart.md b/consumer-focs/quickstart.md deleted file mode 100644 index e69de29..0000000