docs(b20): document factory bootstrap policy asymmetry (MintReceiver always enforced)#150
Merged
stevieraykatz merged 1 commit intoJun 10, 2026
Conversation
…always enforced) The `IB20Factory.createB20` natspec claimed the creation (bootstrap) window bypasses the token's "role / policy / pause" gates for factory-originated calls. That overstated the bypass on two counts (BOP-332 / PSRC #23): - `MINT_RECEIVER_POLICY` is always enforced, even for privileged mints, so new supply is never issued to a policy-denied recipient at creation. - Pause is never bypassed; it defaults to nothing-paused and must be opted into via initCalls. Only role gates and the transfer-side policies (sender / receiver / executor) are actually bypassed. base-std natspec is the source of truth, so pin the intent there rather than in implementation comments: - Rewrite the `createB20` bootstrap-window natspec to spell out the asymmetry. - Add cross-referencing notes to the IB20 policy-type getters. - Document the asymmetry in docs/B20/Factory.md. - Add a unit test pinning that a privileged bootstrap mint to a denied MINT_RECEIVER recipient reverts (the transfer-bypass side is already covered in transfer.t.sol). Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
Interface Coverage✅ All interface functions have test coverage. |
📊 Forge Coverage (
|
| File | Lines | Stmts | Branches | Funcs |
|---|---|---|---|---|
| 🟢 B20FactoryLib.sol | 100.00% | 100.00% | 100.00% | 100.00% |
| 🟢 MockActivationRegistry.sol | 100.00% | 100.00% | 100.00% | 100.00% |
| 🟢 MockActivationRegistryStorage.sol | 100.00% | 100.00% | 100.00% | 100.00% |
| 🟢 MockB20.sol | 100.00% | 100.00% | 100.00% | 100.00% |
| 🟢 MockB20Asset.sol | 100.00% | 100.00% | 100.00% | 100.00% |
| 🟡 MockB20Factory.sol | 98.95% | 99.08% | 100.00% | 100.00% |
| 🟢 MockB20Stablecoin.sol | 100.00% | 100.00% | 100.00% | 100.00% |
| 🟢 MockB20Storage.sol | 100.00% | 100.00% | 100.00% | 100.00% |
| 🟢 MockPolicyRegistry.sol | 100.00% | 100.00% | 100.00% | 100.00% |
| 🟢 MockPolicyRegistryStorage.sol | 100.00% | 100.00% | 100.00% | 100.00% |
| Total | 99.86% | 99.88% | 100.00% | 100.00% |
Full report: download artifact. To browse locally: make coverage (runs forge coverage + genhtml + opens the HTML report).
✅ Fork tests: all 609 passedbase/base is fully in sync with the base-std spec. |
rayyan224
approved these changes
Jun 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Resolves the comment mismatch flagged in BOP-332 / PSRC #23. The
IB20Factory.createB20natspec stated that the creation (bootstrap) window bypasses the token's "role / policy / pause" authorization gates for factory-originated calls. That overstated the bypass on two counts:MINT_RECEIVER_POLICYis always enforced, even for privileged (factory) mints — new supply is never issued to a policy-denied recipient, even at creation.initCalls.Only the role gates and the transfer-side policies (
TRANSFER_SENDER_POLICY,TRANSFER_RECEIVER_POLICY,TRANSFER_EXECUTOR_POLICY) are actually bypassed during the window. This matches the implementedMockB20reference semantics and the production Rust precompile.Per the team discussion, base-std natspec is the source of truth, so the intent is pinned here rather than in implementation comments.
Changes
src/interfaces/IB20Factory.sol— rewrite thecreateB20bootstrap-window@devto spell out the asymmetry (role + transfer-side policies bypassed;MINT_RECEIVER_POLICYalways enforced; pause never bypassed; token invariants never bypassed).src/interfaces/IB20.sol— add cross-referencing@devnotes to the four policy-type getters.docs/B20/Factory.md— document the asymmetry in theinitCallssection.test/unit/B20/supply/mint.t.sol— addtest_mint_revert_privilegedStillEnforcesReceiverPolicy, which pins (via a genuine factory bootstrap, novm.storecheat) that a privileged mint to a deniedMINT_RECEIVERrecipient revertsPolicyForbids. The transfer-bypass side is already covered bytest_transfer_success_privilegedBypasses{Sender,Receiver}Policyintransfer.t.sol.Testing
forge test— 620 tests passing (619 prior + 1 new).forge fmt --checkclean.