Demo
New to B20? See the B20 Token Standard for the concepts and a full launch walkthrough. These samples target
base-std@be6d045, viem@2.55.11, and Base Foundry v1.1.1.How Policies Work
The Policy Registry is a singleton precompile that stores each member list once. A token stores only auint64 policy ID per scope. When a gated function runs, the token calls isAuthorized(policyId, account) on the registry. Many tokens can share one policy; an update to the policy is immediately visible to every token that references it.
Scopes gate specific functions. TRANSFER_SENDER_POLICY and TRANSFER_RECEIVER_POLICY check the sender and recipient on every transfer and transferFrom. MINT_RECEIVER_POLICY checks the recipient on every mint. All three default to ALWAYS_ALLOW (0) until you bind a policy.
An allowlist authorizes only accounts in the set. An empty allowlist authorizes nobody, so seed your intended holders before binding the policy.
Inverted policy IDs
Bit 63 of auint64 policy ID is the invert (NOT) flag. When that bit is set, isAuthorized resolves the base policy and returns the opposite result. If the base does not exist, the result is false — a mistyped inverted ID never becomes allow-everyone. Call invertedPolicyId(policyId) on the registry to toggle the bit. The inverted ID shares the base’s member set; updating the base updates the inverse immediately.
You can pass an inverted simple policy ID as a child of an INTERSECT or UNION composite to express “A AND NOT X” without maintaining a mirror list.
Create and Bind a Holder Allowlist
The token returns the new policy ID for both transfer policy scopes.
Update Membership
After creation, only the policy admin can add or remove accounts. CallupdateAllowlist(policyId, true, accounts) to add and updateAllowlist(policyId, false, accounts) to remove. The change is visible to every token that references the policy on the next call. No second updatePolicy is needed on the token.
To combine a KYC allowlist with a sanctions blocklist, create an INTERSECT composite policy referencing both simple policies, then bind the composite ID to the token’s scopes. You can also pass an inverted policy ID as a composite child — for example, [kycId, invertedPolicyId(exclusionId)] — to express “KYC’d and not on the exclusion list” without duplicating members. See the B20 token standard for the full policy type reference.
See Also
Block an Account
Deny a specific address.
Policy Integration (B20 Standard)
Policy hooks in the B20 standard.