diff --git a/docs/AGENTS.md b/docs/AGENTS.md index 000bd734e..9a1a7a8f7 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -50,7 +50,7 @@ npx skills add base/base-skills |Build on Base/Accept Payments/Confirm and Reconcile:build-on-base/accept-payments/verify-a-payment,build-on-base/accept-payments/watch-for-payments,build-on-base/accept-payments/reconcile-payments |Build on Base/Accept Payments/Return and Pay Out:build-on-base/accept-payments/refund-a-payment,build-on-base/accept-payments/send-a-payout,build-on-base/accept-payments/split-a-payment |Build on Base/Accept Payments/Accept Agentic Payments:build-on-base/accept-payments/charge-for-an-api,build-on-base/accept-payments/settle-usage-based-payments,build-on-base/accept-payments/batch-high-frequency-payments,build-on-base/accept-payments/call-a-paid-service -|Specifications/Specifications:specifications/overview,specifications/native-account-abstraction,specifications/flashblocks +|Specifications/Specifications:specifications/overview,specifications/flashblocks |Specifications/Specifications/Base Protocol:specifications/base-protocol/overview,specifications/base-protocol/batcher,specifications/base-protocol/design-goals |Specifications/Specifications/Base Protocol/Consensus:specifications/base-protocol/consensus/specification,specifications/base-protocol/consensus/derivation,specifications/base-protocol/consensus/p2p,specifications/base-protocol/consensus/rpc |Specifications/Specifications/Base Protocol/Execution:specifications/base-protocol/execution/l2-execution-engine,specifications/base-protocol/execution/precompiles,specifications/base-protocol/execution/predeploys,specifications/base-protocol/execution/preinstalls @@ -73,7 +73,7 @@ npx skills add base/base-skills |SDKs & APIs/Base Verify API:sdks/base-verify/overview,sdks/base-verify/verify-social-accounts,sdks/base-verify/verify-users-onchain |SDKs & APIs/Migrated Documentation:sdks/migrated-products |Upgrades/Overview:upgrades/overview,base-chain/network-information/configuration-changelog -|Upgrades/Denim:upgrades/denim/overview,upgrades/denim/200ms-blocks,upgrades/denim/migrate-from-flashblocks +|Upgrades/Denim:upgrades/denim/overview,specifications/native-account-abstraction,upgrades/denim/200ms-blocks,upgrades/denim/migrate-from-flashblocks |Upgrades/Cobalt:upgrades/cobalt/overview,upgrades/cobalt/dynamic-upgrades,base-chain/specs/reference/b20/changelog/02-cobalt-b20asset-multiplier,base-chain/specs/reference/b20/changelog/02-cobalt-b20-seize,base-chain/specs/reference/b20/changelog/02-cobalt-policyregistry-composite-policy,upgrades/cobalt/validity-transactions |Upgrades/Beryl:upgrades/beryl/overview,upgrades/beryl/reth-v2,upgrades/beryl/reducing-canonical-withdrawal-delay,upgrades/beryl/b20 |Upgrades/Azul:upgrades/azul/overview,upgrades/azul/node-upgrade,upgrades/azul/exec-engine,upgrades/azul/proofs diff --git a/docs/llms-full.txt b/docs/llms-full.txt index 21f4fae66..ffe83016d 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -300,8 +300,6 @@ const client = createPublicClient({ chain: base, transport: http() }) - [Changelog](https://docs.base.org/specifications/b20/changelog): Per-hardfork, per-feature migration notes for the B20 token standard, newest first, including new methods, deprecations, and activation dates. -- [Native Account Abstraction](https://docs.base.org/specifications/native-account-abstraction): EIP-8130 reference for Base: vibenet chain details, client setup, transaction structure, account configuration, authenticators, and payers. - #### Transactions - [Transaction Ordering](https://docs.base.org/specifications/transactions/transaction-ordering): Transactions are ordered based priority fee and arrival time, which determines which Flashblock they are included in. @@ -484,6 +482,8 @@ const client = createPublicClient({ chain: base, transport: http() }) - [Overview](https://docs.base.org/upgrades/denim/overview): Denim introduces native blocks at a 200ms cadence, onchain millisecond time through BaseTime, and millisecond-resolution RPC timestamps. +- [Native Account Abstraction](https://docs.base.org/specifications/native-account-abstraction): EIP-8130 reference for Base: vibenet chain details, client setup, transaction structure, account configuration, authenticators, and payers. + - [200ms Native Blocks](https://docs.base.org/upgrades/denim/200ms-blocks): Specification for Denim's canonical 200ms blocks, including BaseTime, derivation, validation, and RPC behavior. - [Migrate From Flashblocks](https://docs.base.org/upgrades/denim/migrate-from-flashblocks): Migrate your Flashblocks integration to 200ms blocks. @@ -500,7 +500,7 @@ const client = createPublicClient({ chain: base, transport: http() }) - [PolicyRegistry: Composite Policies (UNION / INTERSECT)](https://docs.base.org/base-chain/specs/reference/b20/changelog/02-cobalt-policyregistry-composite-policy): Cobalt adds UNION and INTERSECT composite policies to PolicyRegistry so B20 integrations can combine simple authorization policies without flattening their member lists. -- [Validity Transactions](https://docs.base.org/upgrades/cobalt/validity-transactions): Validity transactions ship with the Cobalt upgrade. Read the full specification in the Specifications tab. +- [Validity Transactions](https://docs.base.org/upgrades/cobalt/validity-transactions): Validity transactions ship with the Cobalt upgrade. ### Beryl diff --git a/docs/llms.txt b/docs/llms.txt index d9a6f2e19..ca4d49d96 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -234,8 +234,6 @@ - [Changelog](https://docs.base.org/specifications/b20/changelog): Per-hardfork, per-feature migration notes for the B20 token standard, newest first, including new methods, deprecations, and activation dates. -- [Native Account Abstraction](https://docs.base.org/specifications/native-account-abstraction): EIP-8130 reference for Base: vibenet chain details, client setup, transaction structure, account configuration, authenticators, and payers. - #### Transactions - [Transaction Ordering](https://docs.base.org/specifications/transactions/transaction-ordering): Transactions are ordered based priority fee and arrival time, which determines which Flashblock they are included in. @@ -418,6 +416,8 @@ - [Overview](https://docs.base.org/upgrades/denim/overview): Denim introduces native blocks at a 200ms cadence, onchain millisecond time through BaseTime, and millisecond-resolution RPC timestamps. +- [Native Account Abstraction](https://docs.base.org/specifications/native-account-abstraction): EIP-8130 reference for Base: vibenet chain details, client setup, transaction structure, account configuration, authenticators, and payers. + - [200ms Native Blocks](https://docs.base.org/upgrades/denim/200ms-blocks): Specification for Denim's canonical 200ms blocks, including BaseTime, derivation, validation, and RPC behavior. - [Migrate From Flashblocks](https://docs.base.org/upgrades/denim/migrate-from-flashblocks): Migrate your Flashblocks integration to 200ms blocks. @@ -434,7 +434,7 @@ - [PolicyRegistry: Composite Policies (UNION / INTERSECT)](https://docs.base.org/base-chain/specs/reference/b20/changelog/02-cobalt-policyregistry-composite-policy): Cobalt adds UNION and INTERSECT composite policies to PolicyRegistry so B20 integrations can combine simple authorization policies without flattening their member lists. -- [Validity Transactions](https://docs.base.org/upgrades/cobalt/validity-transactions): Validity transactions ship with the Cobalt upgrade. Read the full specification in the Specifications tab. +- [Validity Transactions](https://docs.base.org/upgrades/cobalt/validity-transactions): Validity transactions ship with the Cobalt upgrade. ### Beryl diff --git a/docs/specifications/b20/reference/constants-addresses.mdx b/docs/specifications/b20/reference/constants-addresses.mdx index d3dc5c3c3..ddc30b0ee 100644 --- a/docs/specifications/b20/reference/constants-addresses.mdx +++ b/docs/specifications/b20/reference/constants-addresses.mdx @@ -62,6 +62,17 @@ Policy type bytes: | `UNION` | `0x02` | | `INTERSECT` | `0x03` | +## Policy Constants + +| Name | Value | Description | +|---|---|---| +| `TRANSFER_SENDER_POLICY` | `keccak256("TRANSFER_SENDER_POLICY")`
`0xb81736c875ab819dd97f59f2a6542cfb731ad52b4ae15a6f24df2fb02b0327f5` | Consulted for `from` on `transfer` and `transferFrom`. | +| `TRANSFER_RECEIVER_POLICY` | `keccak256("TRANSFER_RECEIVER_POLICY")`
`0x8a4b3fa2d8b921852bc0089c6ef0958aa6961897be36fd731330fe2cd23f8363` | Consulted for `to` on `transfer` and `transferFrom`. | +| `TRANSFER_EXECUTOR_POLICY` | `keccak256("TRANSFER_EXECUTOR_POLICY")`
`0x10be5173aff2a44e748bd9acd8b19fe34689581398a9db7ba2fb671e786ff7d8` | Consulted for `msg.sender` on every transfer entrypoint (`transfer`, `transferFrom`, and memo'd variants). | +| `MINT_RECEIVER_POLICY` | `keccak256("MINT_RECEIVER_POLICY")`
`0xa0d5ae037e66a09119acf080a1d807abb9b6d03b6b9130eb19f7c1e6bdb8ffc8` | Consulted for `to` on `mint`. | +| `SEIZE_HOLDER_POLICY` | `keccak256("SEIZE_HOLDER_POLICY")`
`0x1497ab2b67ebb0a75dd9cdd6aec9f0e64620e6b87e911af7a088ac12e58d9ef2` | Consulted for `from` on `seizeWithMemo`; `from` is seizable when unauthorized under this policy. | +| `SEIZE_RECEIVER_POLICY` | `keccak256("SEIZE_RECEIVER_POLICY")`
`0xbf15b19caf5c77422c038bc25f26b8b815c3a14f6d04c6616076b81bcfe07b3d` | Consulted for `to` on `seizeWithMemo`. | + ## Variant Bytes | Variant | Byte | Address shape | diff --git a/docs/specifications/b20/reference/interfaces/ib20/transfer-executor-policy.mdx b/docs/specifications/b20/reference/interfaces/ib20/transfer-executor-policy.mdx index c705ecd3b..f55a5c0bc 100644 --- a/docs/specifications/b20/reference/interfaces/ib20/transfer-executor-policy.mdx +++ b/docs/specifications/b20/reference/interfaces/ib20/transfer-executor-policy.mdx @@ -1,6 +1,6 @@ --- title: "IB20.TRANSFER_EXECUTOR_POLICY" -description: "Generated B20 reference for TRANSFER_EXECUTOR_POLICY()." +description: "Returns the policy slot consulted against msg.sender on every transfer entrypoint in IB20." --- @@ -18,21 +18,25 @@ function TRANSFER_EXECUTOR_POLICY() external view returns (bytes32); ## Description -Policy slot consulted against `msg.sender` on `transferFrom` when distinct from `from`. Not consulted on `transfer`. +Policy slot consulted against `msg.sender` (the initiator) on every transfer, including when `msg.sender == from`. This applies to `transfer`, `transferFrom`, and their memo variants. Bypassed for factory-originated calls during the creation (bootstrap) window; see `IB20Factory.createB20`. + +`TRANSFER_EXECUTOR_POLICY` now applies to all transfer entrypoints — including `transfer` and self-`transferFrom` where `msg.sender == from`. Previously it applied only when `msg.sender != from` on the `transferFrom` path. Tokens with a restrictive executor policy will block holder-initiated transfers unless those holders are authorized as executors. + + ## Returns Policy scope constant. ## Access Control -Read-only or ERC-20-standard access rules unless the NatSpec states otherwise. +Read-only view function; no access restriction. ## Policy Interaction -No direct policy interaction. +The executor policy is checked against `msg.sender` on every transfer path (`transfer`, `transferFrom`, `transferWithMemo`, `transferFromWithMemo`). There is no exemption when `msg.sender == from`. An unset policy slot is always-allow, so tokens that never configured this policy are unaffected. ## Example diff --git a/docs/specifications/b20/reference/interfaces/ib20/transfer-from-with-memo.mdx b/docs/specifications/b20/reference/interfaces/ib20/transfer-from-with-memo.mdx index 450d08192..a529a26c2 100644 --- a/docs/specifications/b20/reference/interfaces/ib20/transfer-from-with-memo.mdx +++ b/docs/specifications/b20/reference/interfaces/ib20/transfer-from-with-memo.mdx @@ -1,10 +1,8 @@ --- title: "IB20.transferFromWithMemo" -description: "Generated B20 reference for transferFromWithMemo(address,address,uint256,bytes32)." +description: "Transfers tokens on behalf of another address and emits a Memo event, with executor, sender, and receiver policy enforcement." --- - - ## Signature ```solidity IB20.sol @@ -33,13 +31,26 @@ Same as `transferFrom`, plus emits `Memo` immediately after the standard `Transf Always `true` on success. -## Access Control +## Policy Interaction + +`TRANSFER_EXECUTOR_POLICY` is checked against `msg.sender` on every call, including when `msg.sender == from`. Sender (`from`) and receiver (`to`) policies are also enforced. All three checks are bypassed during the factory bootstrap window; see `IB20Factory.createB20`. -Read-only or ERC-20-standard access rules unless the NatSpec states otherwise. + +`TRANSFER_EXECUTOR_POLICY` now applies even when `msg.sender == from`. Tokens that have configured a restrictive executor policy will block holder-initiated transfers unless the holder is explicitly authorized as an executor. + -## Policy Interaction +## Revert Conditions -Checks `TRANSFER_EXECUTOR_POLICY` when `msg.sender != from`, plus sender and receiver transfer scopes. +| Revert | Condition | +|---|---| +| `ContractPaused(TRANSFER)` | The `TRANSFER` operation is paused. | +| `InvalidReceiver` | `to == address(0)`. | +| `InvalidSender` | `from == address(0)`. | +| `InsufficientAllowance` | The caller's allowance from `from` is below `amount`. | +| `PolicyForbids(TRANSFER_EXECUTOR_POLICY, ...)` | `msg.sender` is not authorized by the executor policy. | +| `PolicyForbids(TRANSFER_SENDER_POLICY, ...)` | `from` is not authorized by the sender policy. | +| `PolicyForbids(TRANSFER_RECEIVER_POLICY, ...)` | `to` is not authorized by the receiver policy. | +| `InsufficientBalance` | `from`'s balance is below `amount`. | ## Example diff --git a/docs/specifications/b20/reference/interfaces/ib20/transfer-from.mdx b/docs/specifications/b20/reference/interfaces/ib20/transfer-from.mdx index 4dc9d89cf..be6360c56 100644 --- a/docs/specifications/b20/reference/interfaces/ib20/transfer-from.mdx +++ b/docs/specifications/b20/reference/interfaces/ib20/transfer-from.mdx @@ -1,10 +1,8 @@ --- title: "IB20.transferFrom" -description: "Generated B20 reference for transferFrom(address,address,uint256)." +description: "Transfers tokens from a specified address using the caller's allowance, with executor, sender, and receiver policy enforcement." --- - - ## Signature ```solidity IB20.sol @@ -38,7 +36,7 @@ Always `true` on success. - `InvalidReceiver` when `to == address(0)`. - `InvalidSender` when `from == address(0)`. - `InsufficientAllowance` when the caller's allowance from `from` is below `amount`. -- `PolicyForbids(TRANSFER_EXECUTOR_POLICY, ...)` when `msg.sender != from` and `msg.sender` is not authorized. +- `PolicyForbids(TRANSFER_EXECUTOR_POLICY, ...)` when `msg.sender` is not authorized. - `PolicyForbids(TRANSFER_SENDER_POLICY, ...)` when `from` is not authorized. - `PolicyForbids(TRANSFER_RECEIVER_POLICY, ...)` when `to` is not authorized. - `InsufficientBalance` when `from`'s balance is below `amount`. @@ -49,7 +47,7 @@ Read-only or ERC-20-standard access rules unless the NatSpec states otherwise. ## Policy Interaction -Checks `TRANSFER_EXECUTOR_POLICY` when `msg.sender != from`, plus sender and receiver transfer scopes. +Checks `TRANSFER_EXECUTOR_POLICY` against `msg.sender` on every call, including when `msg.sender == from`. An executor allowlist can therefore restrict holder-initiated transfers, with no self-transfer exemption. Also checks sender and receiver transfer scopes. All policy checks are bypassed during the factory bootstrap window. ## Example diff --git a/docs/specifications/b20/reference/interfaces/ib20/transfer.mdx b/docs/specifications/b20/reference/interfaces/ib20/transfer.mdx index 214930928..a7c0c8870 100644 --- a/docs/specifications/b20/reference/interfaces/ib20/transfer.mdx +++ b/docs/specifications/b20/reference/interfaces/ib20/transfer.mdx @@ -1,6 +1,6 @@ --- title: "IB20.transfer" -description: "Generated B20 reference for transfer(address,uint256)." +description: "Transfers tokens from msg.sender to a destination address, subject to executor, sender, and receiver policy checks." --- @@ -36,17 +36,22 @@ Always `true` on success. - `ContractPaused(TRANSFER)` when `TRANSFER` is paused. - `InvalidReceiver` when `to == address(0)`. - `InvalidSender` when `msg.sender == address(0)`. -- `PolicyForbids(TRANSFER_SENDER_POLICY, ...)` when `msg.sender` is not authorized. +- `PolicyForbids(TRANSFER_EXECUTOR_POLICY, ...)` when `msg.sender` is not authorized as an executor. +- `PolicyForbids(TRANSFER_SENDER_POLICY, ...)` when `msg.sender` is not authorized as a sender. - `PolicyForbids(TRANSFER_RECEIVER_POLICY, ...)` when `to` is not authorized. - `InsufficientBalance` when `msg.sender`'s balance is below `amount`. ## Access Control -Read-only or ERC-20-standard access rules unless the NatSpec states otherwise. +Standard ERC-20 caller rules apply. The factory bootstrap bypass suppresses all three policy checks during the creation window; see `IB20Factory.createB20`. ## Policy Interaction -Checks `TRANSFER_SENDER_POLICY` and `TRANSFER_RECEIVER_POLICY`; `approve` and `permit` are not policy-gated. +Checks `TRANSFER_EXECUTOR_POLICY`, `TRANSFER_SENDER_POLICY`, and `TRANSFER_RECEIVER_POLICY` on every call. The executor check runs even when `msg.sender == from`, so an executor allowlist can restrict holder-initiated transfers with no self-transfer exemption. + + +`TRANSFER_EXECUTOR_POLICY` now applies to `transfer` (not only `transferFrom`). If you have configured a restrictive executor allowlist, holders who are not on that list can no longer initiate transfers directly — they must use an authorized executor. + ## Example diff --git a/docs/specifications/b20/reference/invariants-tests.mdx b/docs/specifications/b20/reference/invariants-tests.mdx index 0fafbfc47..e7552a0c6 100644 --- a/docs/specifications/b20/reference/invariants-tests.mdx +++ b/docs/specifications/b20/reference/invariants-tests.mdx @@ -26,7 +26,7 @@ These invariants and conformance test cases are the normative behavioral guarant ### Transfer Policies 10. `approve` is never policy-gated. -11. `TRANSFER_EXECUTOR_POLICY` is checked only on `transferFrom`, never on `transfer`. +11. `TRANSFER_EXECUTOR_POLICY` is checked on every transfer entrypoint (`transfer`, `transferFrom`, and memo'd variants), including when `msg.sender == from`. 12. `MINT_RECEIVER_POLICY` is always enforced, even during factory `initCalls`. 13. All three transfer-side scopes are bypassed during `initCalls`. 14. Every scope defaults to `ALWAYS_ALLOW` at token creation. @@ -104,74 +104,75 @@ These invariants and conformance test cases are the normative behavioral guarant | 15 | Sender on blocklist calls `transfer` | Reverts with `PolicyForbids` | | 16 | Sender on blocklist calls `approve` | Succeeds | | 17 | `transferFrom` where executor is on executor blocklist | Reverts | -| 18 | Direct `transfer` by sender on executor blocklist (not sender blocklist) | Succeeds | -| 19 | During `initCalls`, transfer from blocklisted sender | Succeeds — bypass | -| 20 | During `initCalls`, mint to receiver on mint-receiver blocklist | Reverts — never bypassed | +| 18 | Direct `transfer` by sender on executor blocklist (not sender blocklist) | Reverts with `PolicyForbids` | +| 19 | `transferFrom` where `msg.sender == from` and executor is on executor blocklist | Reverts with `PolicyForbids` | +| 20 | During `initCalls`, transfer from blocklisted sender | Succeeds — bypass | +| 21 | During `initCalls`, mint to receiver on mint-receiver blocklist | Reverts — never bypassed | ### Mint and Supply Cap | # | Scenario | Expected | |---|----------|----------| -| 21 | Mint that would push `totalSupply` above cap | Reverts with `SupplyCapExceeded` | -| 22 | Mint exactly to cap | Succeeds | -| 23 | `updateSupplyCap` below current `totalSupply` | Reverts with `InvalidSupplyCap` | -| 24 | Burn tokens, then mint up to cap | Succeeds | -| 25 | Mint while `MINT` is paused | Reverts | +| 22 | Mint that would push `totalSupply` above cap | Reverts with `SupplyCapExceeded` | +| 23 | Mint exactly to cap | Succeeds | +| 24 | `updateSupplyCap` below current `totalSupply` | Reverts with `InvalidSupplyCap` | +| 25 | Burn tokens, then mint up to cap | Succeeds | +| 26 | Mint while `MINT` is paused | Reverts | ### Burn and Seize | # | Scenario | Expected | |---|----------|----------| -| 26 | `burnBlocked` on frozen account | Succeeds | -| 27 | `burnBlocked` on non-frozen account | Reverts | -| 28 | `burnBlocked` by holder of `BURN_ROLE` (not `BURN_BLOCKED_ROLE`) | Reverts | -| 29 | Freeze, seize full balance, re-mint to recovery | Succeeds | -| 30 | Burn while `BURN` is paused | Reverts | +| 27 | `burnBlocked` on frozen account | Succeeds | +| 28 | `burnBlocked` on non-frozen account | Reverts | +| 29 | `burnBlocked` by holder of `BURN_ROLE` (not `BURN_BLOCKED_ROLE`) | Reverts | +| 30 | Freeze, seize full balance, re-mint to recovery | Succeeds | +| 31 | Burn while `BURN` is paused | Reverts | ### Pause | # | Scenario | Expected | |---|----------|----------| -| 31 | Pause `TRANSFER`, call `transfer` | Reverts | -| 32 | Pause `TRANSFER`, call `mint` | Succeeds | -| 33 | Pause `TRANSFER`, call `approve` | Succeeds | -| 34 | Pauser calls `unpause` without `UNPAUSE_ROLE` | Reverts | +| 32 | Pause `TRANSFER`, call `transfer` | Reverts | +| 33 | Pause `TRANSFER`, call `mint` | Succeeds | +| 34 | Pause `TRANSFER`, call `approve` | Succeeds | +| 35 | Pauser calls `unpause` without `UNPAUSE_ROLE` | Reverts | ### Memos | # | Scenario | Expected | |---|----------|----------| -| 35 | `transferWithMemo` | Emits `Transfer` then `Memo` at consecutive log indices | -| 36 | `transferFromWithMemo` | `Memo.caller` is `msg.sender`, not `from` | -| 37 | `transfer` (non-memo variant) | No `Memo` event | +| 36 | `transferWithMemo` | Emits `Transfer` then `Memo` at consecutive log indices | +| 37 | `transferFromWithMemo` | `Memo.caller` is `msg.sender`, not `from` | +| 38 | `transfer` (non-memo variant) | No `Memo` event | ### Permit | # | Scenario | Expected | |---|----------|----------| -| 38 | Valid permit with correct signature, nonce, deadline | Succeeds | -| 39 | Permit with expired deadline | Reverts | -| 40 | Replay used permit signature | Reverts | -| 41 | Permit signed before `updateName`, submitted after | Reverts | -| 42 | Contract wallet signature (ERC-1271) | Reverts | -| 43 | Permit while `TRANSFER` is paused | Succeeds | +| 39 | Valid permit with correct signature, nonce, deadline | Succeeds | +| 40 | Permit with expired deadline | Reverts | +| 41 | Replay used permit signature | Reverts | +| 42 | Permit signed before `updateName`, submitted after | Reverts | +| 43 | Contract wallet signature (ERC-1271) | Reverts | +| 44 | Permit while `TRANSFER` is paused | Succeeds | ### Factory | # | Scenario | Expected | |---|----------|----------| -| 44 | `getB20Address` then deploy with same params | Addresses match | -| 45 | Inspect byte 10 of deployed Asset address | Returns `0x00` | -| 46 | Deploy same `(deployer, variant, salt)` twice | Second reverts | -| 47 | Deploy when variant feature not activated | Reverts | -| 48 | `initCalls` that pause `TRANSFER`, then transfer in next initCall | Transfer reverts | +| 45 | `getB20Address` then deploy with same params | Addresses match | +| 46 | Inspect byte 10 of deployed Asset address | Returns `0x00` | +| 47 | Deploy same `(deployer, variant, salt)` twice | Second reverts | +| 48 | Deploy when variant feature not activated | Reverts | +| 49 | `initCalls` that pause `TRANSFER`, then transfer in next initCall | Transfer reverts | ### Variants | # | Scenario | Expected | |---|----------|----------| -| 49 | Deploy Asset with `decimals = 5` | Reverts | -| 50 | Update multiplier to `2e18`, check `balanceOf` for raw balance 100 | Returns 200 | -| 51 | Reuse announcement ID | Reverts with `DuplicateAnnouncementId` | -| 52 | `batchMint` where one recipient is not on allowlist | Reverts | -| 53 | Deploy Stablecoin with `currency = "usd"` | Reverts — `A`–`Z` only | +| 50 | Deploy Asset with `decimals = 5` | Reverts | +| 51 | Update multiplier to `2e18`, check `balanceOf` for raw balance 100 | Returns 200 | +| 52 | Reuse announcement ID | Reverts with `DuplicateAnnouncementId` | +| 53 | `batchMint` where one recipient is not on allowlist | Reverts | +| 54 | Deploy Stablecoin with `currency = "usd"` | Reverts — `A`–`Z` only | diff --git a/docs/specifications/b20/specification-overview.mdx b/docs/specifications/b20/specification-overview.mdx index 85c1a6f42..38eb8e3f1 100644 --- a/docs/specifications/b20/specification-overview.mdx +++ b/docs/specifications/b20/specification-overview.mdx @@ -95,13 +95,15 @@ B20 tokens store one `uint64 policyId` per supported policy scope. |---|---|---| | `TRANSFER_SENDER_POLICY` | `from` | `transfer`, `transferFrom`, and memo variants | | `TRANSFER_RECEIVER_POLICY` | `to` | `transfer`, `transferFrom`, and memo variants | -| `TRANSFER_EXECUTOR_POLICY` | `msg.sender` | `transferFrom` when `msg.sender != from` | +| `TRANSFER_EXECUTOR_POLICY` | `msg.sender` | `transfer`, `transferFrom`, and memo variants | | `MINT_RECEIVER_POLICY` | `to` | `mint`, `mintWithMemo` | | `SEIZE_EXEMPT_POLICY` | `from` | `seizeWithMemo`; an authorized holder is seize-exempt, so only unauthorized holders are seizable | | `SEIZE_RECEIVER_POLICY` | `to` | `seizeWithMemo`; destination must be authorized | All scopes default to `ALWAYS_ALLOW` at creation. `approve` and `permit` are not policy-gated. +`TRANSFER_EXECUTOR_POLICY` checks `msg.sender` on every transfer entrypoint — including `transfer` and self-`transferFrom` where `msg.sender == from`. An executor allowlist therefore gates who may initiate any transfer, not just delegated ones. Factory `initCalls` transfers bypass this check. + ## Mint `mint` and `mintWithMemo` are gated by `MINT_ROLE`, checked against `MINT_RECEIVER_POLICY`, and bounded by `supplyCap`.