You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: correct B20 native token standard docs against base-std (#1629)
Verified docs/base-chain/specs/upgrades/beryl/b20.mdx against the
base/base-std interfaces (origin/main) and the Rust precompile
implementation, and corrected several inaccuracies:
- createB20 signature arg order is (variant, salt, params, initCalls)
- Supply cap sentinel/max is type(uint128).max, not type(uint256).max
(and updateSupplyCap also reverts above that bound)
- PolicyRegistry admin fns are stageUpdateAdmin / finalizeUpdateAdmin /
renounceAdmin (not transfer/accept/renouncePolicyAdmin)
- createPolicy arg order is (admin, policyType)
- Membership setters are updateBlocklist / updateAllowlist (bool-based,
type-specific), not addToPolicy / removeFromPolicy
- Clarify OPERATOR_ROLE only gates the multiplier and announcements;
batch mint uses MINT_ROLE and extra metadata uses METADATA_ROLE
- Clarify initCalls bootstrap bypass covers role gates and transfer-side
policy gates; note createB20 activation gating
- Minor: TRANSFER_EXECUTOR_POLICY only checked when msg.sender != from;
add language to the address-derivation code block
Generated with Claude Code
Co-authored-by: Claude <noreply@anthropic.com>
User-defined roles are supported via `setRoleAdmin` and `grantRole`. They carry no built-in effect - B20 only enforces gates against the seven roles above.
37
+
User-defined roles are supported via `setRoleAdmin` and `grantRole`. They carry no built-in effect - B20 only enforces gates against the seven base-surface roles above. The Asset variant adds an eighth role, `OPERATOR_ROLE` (see [Variants](#variants)).
38
38
39
39
### Admin Renunciation
40
40
@@ -82,17 +82,20 @@ Consumers that write a policy ID (e.g. `updatePolicy`) MUST validate `policyExis
82
82
83
83
### Admin Model
84
84
85
-
Each policy has one admin. Admin transfers are two-step: the current admin calls `transferPolicyAdmin(policyId, newAdmin)`, then the pending admin calls `acceptPolicyAdmin(policyId)`. `renouncePolicyAdmin(policyId)` permanently freezes the policy - membership can never be changed again.
85
+
Each policy has one admin. Admin transfers are two-step: the current admin calls `stageUpdateAdmin(policyId, newAdmin)`, then the pending admin calls `finalizeUpdateAdmin(policyId)`. `renounceAdmin(policyId)` permanently freezes the policy - membership can never be changed again.
// Update membership (batched). The setter is type-specific; the bool sets membership state.
96
+
policyRegistry.updateBlocklist(policyId, true, accounts); // block these accounts
97
+
policyRegistry.updateBlocklist(policyId, false, accounts); // unblock these accounts
98
+
// For ALLOWLIST policies: policyRegistry.updateAllowlist(policyId, allowed, accounts)
96
99
```
97
100
98
101
### Read Interface
@@ -112,7 +115,7 @@ B20 declares a fixed set of policy scopes. Each scope stores a `uint64` policy I
112
115
|-------|-------|
113
116
|`TRANSFER_SENDER_POLICY`| The `from` of `transfer` / `transferFrom`|
114
117
|`TRANSFER_RECEIVER_POLICY`| The `to` of `transfer` / `transferFrom`|
115
-
|`TRANSFER_EXECUTOR_POLICY`| The `msg.sender` of `transferFrom` (not checked on `transfer`) |
118
+
|`TRANSFER_EXECUTOR_POLICY`| The `msg.sender` of `transferFrom`, when distinct from `from` (not checked on `transfer`) |
116
119
|`MINT_RECEIVER_POLICY`| The `to` of `mint`|
117
120
118
121
`approve` is not policy-gated - only actual balance movement via `transfer` / `transferFrom` is checked.
@@ -136,7 +139,7 @@ Two burn paths exist:
136
139
137
140
## Supply Cap
138
141
139
-
The supply cap is optional. The sentinel `type(uint256).max` indicates no cap (the default at creation). `updateSupplyCap(newCap)` is admin-gated and emits `SupplyCapUpdated`. Lowering below the current `totalSupply`reverts with `InvalidSupplyCap`.
142
+
The supply cap is optional. The sentinel `type(uint128).max` indicates no cap (the default at creation); it is also the maximum permitted cap, so `totalSupply` can never exceed it. `updateSupplyCap(newCap)` is admin-gated and emits `SupplyCapUpdated`. It reverts with `InvalidSupplyCap` if `newCap` is below the current `totalSupply`or above `type(uint128).max`.
140
143
141
144
## Memos
142
145
@@ -165,28 +168,30 @@ B20 implements ERC-2612 (signed approvals) using an EIP-712 domain shaped as `(n
165
168
166
169
## Factory
167
170
168
-
All B20 tokens are created through the singleton `IB20Factory` precompile via `createB20(variant, params, initCalls, salt)`.
171
+
All B20 tokens are created through the singleton `IB20Factory` precompile via `createB20(variant, salt, params, initCalls)`.
169
172
170
173
| Parameter | Description |
171
174
|-----------|-------------|
172
175
|`variant`|`ASSET` or `STABLECOIN`|
173
-
|`params`| ABI-encoded, variant-specific creation struct (versioned by leading byte) |
174
-
|`initCalls`| Optional array of ABI-encoded calls dispatched post-creation with admin privilege bypass |
175
176
|`salt`| Caller-chosen entropy for address derivation |
177
+
|`params`| ABI-encoded, variant-specific creation struct (versioned by leading byte) |
178
+
|`initCalls`| Optional array of ABI-encoded calls dispatched post-creation; factory-originated calls bypass role gates and transfer-side policy gates during this window |
179
+
180
+
`createB20` reverts with `IActivationRegistry.FeatureNotActivated` if the requested variant's feature is not yet activated on the chain.
176
181
177
182
### Address Derivation
178
183
179
184
B20 addresses are deterministic and encode the variant directly:
The variant is recoverable from the address alone without an RPC call. Helper functions `getB20Address(variant, deployer, salt)`, `isB20(addr)`, and `isB20Initialized(addr)` are available on the factory.
186
191
187
192
### initCalls Semantics
188
193
189
-
`initCalls` are dispatched after token creation with admin privilege bypass, allowing configuration (e.g. setting policies, granting roles) in the same transaction as deployment. The bypass is partial:
194
+
`initCalls` are dispatched after token creation. During this bootstrap window, factory-originated calls bypass the token's role gates and its transfer-side policy gates (`TRANSFER_SENDER_POLICY`, `TRANSFER_RECEIVER_POLICY`, `TRANSFER_EXECUTOR_POLICY`), allowing admin-gated configuration (e.g. setting policies, granting roles) and bootstrap transfers in the same transaction as deployment. The bypass is deliberately not total:
190
195
191
196
-`MINT_RECEIVER_POLICY` is always enforced even during `initCalls`.
192
197
- Pause state is never bypassed.
@@ -198,7 +203,7 @@ The variant is recoverable from the address alone without an RPC call. Helper fu
198
203
199
204
The general-purpose variant for assets of all kinds. Decimals are configurable between 6 and 18 at deployment time and are immutable after creation.
200
205
201
-
In addition to the base B20 surface, Asset tokens add an `OPERATOR_ROLE`that gates the following capabilities:
206
+
In addition to the base B20 surface, Asset tokens add several capabilities. A new `OPERATOR_ROLE` gates the multiplier and announcements; batch mint and extra metadata reuse the existing `MINT_ROLE` and `METADATA_ROLE` respectively.
0 commit comments