diff --git a/docs/ActivationRegistry/README.md b/docs/ActivationRegistry/README.md index d8ba905..3d630a7 100644 --- a/docs/ActivationRegistry/README.md +++ b/docs/ActivationRegistry/README.md @@ -10,7 +10,6 @@ The canonical IDs in use today, defined in [`ActivationRegistryFeatureList`](../ | Constant | Preimage | Value | |---|---|---| -| `B20_FACTORY` | `"base.b20_factory"` | `0x78751e29c8bcc0d609ab18e9fbc4158e73f7db25ae2ee095dad42e2578b1e800` | | `B20_ASSET` | `"base.b20_asset"` | `0xcdcc772fe4cbdb1029f822861176d09e646db96723d4c1e82ddfdeb8163ef54c` | | `B20_STABLECOIN` | `"base.b20_stablecoin"` | `0xecfa0def2c10020caaf65e6155aa69c84b24892aaef76eeac52e0e2b3a0b8601` | | `POLICY_REGISTRY` | `"base.policy_registry"` | `0xb582ebae03f16fee49a6763f78df482fb11ae73f103ed0d330bbe556aa90a43f` | @@ -28,7 +27,7 @@ sequenceDiagram Admin->>ActivationRegistry: activate(featureId) Note over ActivationRegistry: features[featureId] = true - ActivationRegistry-->>Admin: emit FeatureActivated(featureId) + ActivationRegistry-->>Admin: emit FeatureActivated(feature, caller) ``` Reverts: `Unauthorized` (non-admin caller), `AlreadyActivated`, `DelegateCallNotAllowed` / `StaticCallNotAllowed`. @@ -44,7 +43,7 @@ sequenceDiagram Admin->>ActivationRegistry: deactivate(featureId) Note over ActivationRegistry: features[featureId] = false - ActivationRegistry-->>Admin: emit FeatureDeactivated(featureId) + ActivationRegistry-->>Admin: emit FeatureDeactivated(feature, caller) ``` Reverts: `Unauthorized`, `AlreadyDeactivated`, `DelegateCallNotAllowed` / `StaticCallNotAllowed`. diff --git a/docs/B20/Asset.md b/docs/B20/Asset.md index c819999..b979224 100644 --- a/docs/B20/Asset.md +++ b/docs/B20/Asset.md @@ -4,7 +4,7 @@ The Asset variant of B20 — designed for assets of all kinds. Everything in [B2 ## Multiplier -Each account's stored balance is the **raw** balance. A uniform on-chain **multiplier** scales that raw balance into a derived **scaled** view that consumers display. The multiplier applies to all accounts equally, which lets issuers rescale every balance — for splits, reverse-splits, or rebases — without rewriting individual balances — the shape is similar to wstETH wrapping stETH, where the stored unit is the unwrapped quantity and the derived unit is the rebased view. +Each account's stored balance is the **raw** balance. A uniform on-chain **multiplier** scales that raw balance into a derived **scaled** view that consumers display. The multiplier applies to all accounts equally, which lets issuers rebase every balance at once — without rewriting individual balances — the shape is similar to wstETH wrapping stETH, where the stored unit is the unwrapped quantity and the derived unit is the rebased view. Read the current multiplier with `multiplier()`; the value is in WAD precision (`1e18`, exposed as `WAD_PRECISION()`). `toScaledBalance(rawBalance)` converts a raw amount to its scaled view, `toRawBalance(scaledBalance)` is the reverse converter (integer-floored, so the round-trip can lose up to one ULP), and `scaledBalanceOf(account)` is a convenience over ERC-20's `balanceOf` that returns the same account's raw balance in its scaled form. @@ -25,25 +25,18 @@ Indexers should treat every `Announcement` log as the start of exactly one brack Wrap a set of operations in a single announcement by calling `announce(internalCalls, id, description, uri)`. The function (gated by `OPERATOR_ROLE`) emits `Announcement`, dispatches each internal call via self-`delegatecall` (which preserves `msg.sender` so the inner role checks see the operator), then emits `EndAnnouncement`. Inner reverts are wrapped in `InternalCallFailed` rather than bubbled — replay the call directly to debug. Nested calls to `announce` revert with `AnnouncementInProgress`; calls shorter than 4 bytes revert with `InternalCallMalformed`. ```solidity -// Disclose and execute a 2-for-1 forward split atomically. +// Disclose and apply a community-ratified rebase atomically. bytes[] memory internalCalls = new bytes[](1); internalCalls[0] = abi.encodeCall(IB20Asset.updateMultiplier, (newMultiplier)); IB20Asset(token).announce({ internalCalls: internalCalls, - id: "2026-Q3-split", - description: "2-for-1 forward split", + id: "2026-Q3-rebase", + description: "Community governance proposal #42: ratified rebase", uri: "https://disclosures.example.com/..." }); ``` -The two supply-action setters should be wrapped in `announce()`: - -- `updateMultiplier(...)` -- `batchMint(...)` - -Direct invocation by a role holder is permitted as an **emergency override** — it succeeds but produces no bracket events. Suitable only for break-glass scenarios where the inability to emit an announcement is itself part of the response. - ## Batch Mint `batchMint(recipients, amounts)` mints to many accounts in one call, gated by `MINT_ROLE`. It should be wrapped in `announce()`, which additionally requires the operator to hold `OPERATOR_ROLE` (typically granted as a single bundle). @@ -58,10 +51,8 @@ Each Asset token can carry an arbitrary set of named metadata entries — a gene ### `OPERATOR_ROLE` -Gates the two supply-action setters (`updateMultiplier`, `batchMint`) and the `announce` wrapper itself. Held separately from `DEFAULT_ADMIN_ROLE` so supply-action operators don't need full admin authority. Operationally paired with `METADATA_ROLE` — when granting one, you typically grant the other to the same address. +Gates the `updateMultiplier` and the `announce`. These are metadata-like operations — they post disclosures and rescale the displayed balance rather than moving raw balances directly — but a compromised operator carries materially higher severity than ordinary metadata edits, so the capability is elevated into its own independent role instead of being folded into `METADATA_ROLE`. Held separately from `DEFAULT_ADMIN_ROLE` so operators don't need full admin authority. ## Configurable Decimals -`decimals()` is chosen at creation via `B20AssetCreateParams.decimals` and immutable thereafter. The factory enforces the inclusive range `[6, 18]` (exposed as `B20Constants.MIN_ASSET_DECIMALS` and `MAX_ASSET_DECIMALS`); out-of-range values revert `InvalidDecimals(decimals)`. `6` is the smallest unit any common stablecoin uses and the floor most integrations expect; `18` is the ERC-20 community ceiling that every wallet and indexer renders correctly. - -The stablecoin variant is unchanged — it hardcodes `decimals()` to `6`. +`decimals()` is chosen at creation via `B20AssetCreateParams.decimals` and immutable thereafter. The factory enforces the inclusive range `[6, 18]` (exposed as `B20Constants.MIN_ASSET_DECIMALS` and `MAX_ASSET_DECIMALS`); out-of-range values revert `InvalidDecimals(decimals)`. `6` is the smallest unit any asset should use and `18` is a reasonable ceiling that encompasses the supermajority of assets. diff --git a/docs/B20/Factory.md b/docs/B20/Factory.md index b24a7c0..8c25b60 100644 --- a/docs/B20/Factory.md +++ b/docs/B20/Factory.md @@ -16,7 +16,7 @@ Variant-specific creation arguments, ABI-encoded as a versioned struct (one stru ### `initCalls` -An optional array of ABI-encoded calls dispatched on the new token immediately after creation. These let you configure anything beyond the variant's defined `params` — role grants, mint operations, policy slot wiring, contract URI, and so on. They execute on the new token as if the factory were the admin, so admin-gated operations are permitted within this window. The factory itself receives no official roles and has no persisted access to the token. +An optional array of ABI-encoded calls dispatched on the new token immediately after creation. These let you configure anything beyond the variant's defined `params` — role grants, mint operations, policy scopes, contract URI, and so on. They execute on the new token as if the factory were the admin, so admin-gated operations are permitted within this window. The factory itself receives no official roles and has no persisted access to the token. Build the array with [`B20FactoryLib`](../../src/lib/B20FactoryLib.sol) helpers (or encode manually): diff --git a/docs/B20/README.md b/docs/B20/README.md index a6072d2..2171d65 100644 --- a/docs/B20/README.md +++ b/docs/B20/README.md @@ -24,7 +24,7 @@ Standard role taxonomy: | `DEFAULT_ADMIN_ROLE` | All admin operations: role grants, policy updates, supply-cap changes | | `MINT_ROLE` | `mint`, `mintWithMemo` | | `BURN_ROLE` | Caller-side burns (`burn`, `burnWithMemo`) | -| `BURN_BLOCKED_ROLE` | Sanctions-burns against policy-blocked accounts (`burnBlocked`) | +| `BURN_BLOCKED_ROLE` | Burns against policy-blocked accounts (`burnBlocked`) | | `PAUSE_ROLE` | `pause` | | `UNPAUSE_ROLE` | `unpause` | | `METADATA_ROLE` | `updateName`, `updateSymbol`, `updateContractURI` | @@ -54,7 +54,7 @@ Because scopes are per-actor, send-side and receive-side rules can be configured > ⚠️ **Every scope defaults to `ALWAYS_ALLOW` at token creation** unless overridden in the bootstrap `initCalls`. Token behavior must be intentionally constrained — an unattended deployment of B20 is fully open. -Scopes are read via `policyId(scope)` and written via `updatePolicy(scope, policyId)`. `updatePolicy` is admin-gated and reverts if the scope isn't recognized — typo'd scopes hard-revert rather than silently no-op'ing. +Scopes are read via `policyId(scope)` and written via `updatePolicy(scope, policyId)`. `updatePolicy` is admin-gated and reverts if the scope isn't recognized. See [PolicyRegistry](../PolicyRegistry/README.md) for registry mechanics (built-in policy IDs, encoding, admin lifecycle). @@ -114,4 +114,4 @@ ERC-1271 contract signatures are deliberately NOT accepted — permit recovers v | Variant | Decimals | What it adds | |---|---|---| | [Asset](Asset.md) | 6-18 (configurable per token) | multiplier, announcements, extra metadata, batched issuance | -| [Stablecoin](Stablecoin.md) | 6 (fixed) | currency ISO code | +| [Stablecoin](Stablecoin.md) | 6 (fixed) | self-declared currency code | diff --git a/docs/B20/Stablecoin.md b/docs/B20/Stablecoin.md index fa74575..62e5fb5 100644 --- a/docs/B20/Stablecoin.md +++ b/docs/B20/Stablecoin.md @@ -2,12 +2,12 @@ The Stablecoin variant of B20. Everything in [B20/README.md](README.md) applies; this page covers the deltas only. See [`IB20Stablecoin`](../../src/interfaces/IB20Stablecoin.sol) for the Solidity interface. -## Fixed Decimals (6) - -`decimals()` is hard-wired to `6`. The choice matches existing popular stablecoins. - ## Currency Codes `currency()` returns the ISO-style currency code as a `string` (e.g., `"USD"`, `"EUR"`). It is set once via `B20StablecoinCreateParams.currency` at creation, immutable thereafter, and restricted to `A`–`Z` bytes (no lowercase, no digits, no separators). The value is **self-declared** — the contract does not verify it against any registry or allowlist. Wallets and indexers can use it to group stablecoins by underlying fiat without an external lookup, but it is not a proof of fiat backing. + +## Fixed Decimals (6) + +`decimals()` is hard-wired to `6`. The choice matches existing popular stablecoins.