This page is the call-level companion to the B20 Native Token Standard writeup. That page explains how the standard behaves; this one gives you the wire details — every function with its four-byte selector, every event with its topic0, and every revert with the selector you’ll see come back.
The same three addresses apply on every network that has B20 turned on.
Surface Address B20Factory 0xB20f000000000000000000000000000000000000ActivationRegistry 0x8453000000000000000000000000000000000001PolicyRegistry 0x8453000000000000000000000000000000000002
The base token surface. Every B20 token exposes it, whichever variant it was created as.
Function Selector What it returns DEFAULT_ADMIN_ROLE0xa217fddfThe top-level admin role, which is bytes32(0). Holders can manage the role graph, policy slots, and the supply cap. MINT_ROLE0xe9a9c850The role a caller needs for mint and mintWithMemo. BURN_ROLE0xb930908fThe role a caller needs for burn and burnWithMemo. BURN_BLOCKED_ROLE0x32ad9be8The role behind the deprecated burnBlocked path, kept for older integrations. SEIZE_ROLE0x3c7e9ba5The role a caller needs for seizeWithMemo. PAUSE_ROLE0x389ed267The role a caller needs for pause. UNPAUSE_ROLE0x309756fbThe role a caller needs for unpause. METADATA_ROLE0x38841782The role behind updateName, updateSymbol, and updateContractURI.
Each of these names one slot on the token. The slot holds a uint64 policy ID, and the token consults it against the account listed below.
Function Selector Account checked TRANSFER_SENDER_POLICY0xd116fc21from, on every transfer — transferFrom included.TRANSFER_RECEIVER_POLICY0x210f521bto, on every transfer.TRANSFER_EXECUTOR_POLICY0x724e9c53msg.sender on transferFrom, but only where it differs from from.MINT_RECEIVER_POLICY0x6e5b013dto, on every mint.SEIZE_HOLDER_POLICY0xb279d311from, when seizeWithMemo runs.SEIZE_RECEIVER_POLICY0xb31da27fto, when seizeWithMemo runs.
Function Selector Behavior name0x06fdde03The token name. Fixed at creation, but updateName can change it later. symbol0x95d89b41The token symbol. Fixed at creation, but updateSymbol can change it later. decimals0x313ce567The decimal count. Each variant fixes this and it never moves. totalSupply0x18160dddHow much of the token is in circulation right now. balanceOf0x70a08231The balance held by account. allowance0xdd62ed3eHow much spender is still cleared to move on behalf of owner. transfer0xa9059cbbMoves amount from the caller to to, emitting Transfer. transferFrom0x23b872ddMoves amount from from to to, drawing on the caller’s allowance. Emits Transfer. approve0x095ea7b3Rewrites spender’s allowance to amount. Neither policies nor the pause state apply. Emits Approval.
Each of these does exactly what its plain counterpart does, then emits Memo in the log slot directly after the primary event.
Function Selector Wraps transferWithMemo0x95777d59transfertransferFromWithMemo0x929c2539transferFrommintWithMemo0xe44f0b12mintburnWithMemo0x38f23b0bburn
Function Selector Behavior mint0x40c10f19Issues amount to to and emits Transfer(address(0), to, amount). burn0x42966c68Destroys amount out of the caller’s own balance. No policy applies. burnBlocked0xec0cf3dcDeprecated. Destroys amount belonging to a from that TRANSFER_SENDER_POLICY currently denies. seizeWithMemo0xf916d81bTakes amount from from and reassigns it to to in one privileged step. supplyCap0x8f770ad0The ceiling mint enforces. It tops out at type(uint128).max, which doubles as the no-cap sentinel. updateSupplyCap0xe5a97f07Moves the cap up or down. It can never sit below the supply already outstanding.
Function Selector Behavior hasRole0x91d14854Whether account belongs to role. The graph accepts user-defined roles, though the token itself enforces none of them. getRoleAdmin0x248a9ca3Which role can hand out or take away role. Falls back to DEFAULT_ADMIN_ROLE where nothing was set. grantRole0x2f2ff15dAdds account to role and emits RoleGranted. revokeRole0xd547741fDrops account from role and emits RoleRevoked. setRoleAdmin0x1e4e0091Reassigns which role administers role. Emits RoleAdminChanged. renounceRole0x36568abeLets the caller give up a role they hold. Emits RoleRevoked. renounceLastAdmin0x6f79e3d7The one route to a permanently admin-less token. It strips DEFAULT_ADMIN_ROLE from the last holder for good.
Caution
The final admin cannot leave through renounceRole or revokeRole — both revert with LastAdminCannotRenounce. Once renounceLastAdmin goes through, every admin-gated function is dead for the life of the token, and no one can restore the role.
Function Selector Behavior pausedFeatures0xde9997e3The set of PausableFeature values paused on this token. Don’t rely on the ordering. isPaused0x165c44bcWhether one specific feature is paused. Constant time. pause0x9f98bd2dPauses everything in features. It adds to the existing set, so repeats are harmless. unpause0xb61b2ebcClears the pause on everything in features, leaving anything unlisted alone. Repeats are harmless.
Function Selector Behavior policyId0xdb3de624The policy currently wired into policyScope. An unset slot reads back as 0, the always-allow built-in. updatePolicy0xadf9c4eaPoints policyScope at a different policy ID. The next gated call already sees it.
Function Selector Behavior nonces0x7ecebe00The permit nonce for owner, which climbs by one after each permit that lands. permit0xd505accfThe EIP-2612 signed approval. It recovers owner from (v, r, s) by ECDSA, so only EOA signatures work. DOMAIN_SEPARATOR0x3644e515The token’s live EIP-712 domain separator, recomputed per call. eip712Domain0x84b0196eERC-5267 introspection over that domain. contractURI0xe8a3d485Where the token’s contract-level metadata lives offchain, per ERC-7572. updateContractURI0x7e5b1e24Repoints contractURI. Per ERC-7572 the resulting event carries no arguments. updateName0x84da92a7Rewrites name. Emits NameUpdated, then EIP712DomainChanged, because the separator rotates with it. updateSymbol0x537f5312Rewrites symbol and emits SymbolUpdated.
Extra surface carried only by tokens created as the ASSET variant.
Function Selector Behavior OPERATOR_ROLE0xf5b541a6The role behind announce and the multiplier operations. WAD_PRECISION0x664808a8The fixed-point scale the multiplier uses, equal to 1e18. MAX_UI_MULTIPLIER0x785c0cf0The largest multiplier the setters will take. multiplier0x1b3ed722The multiplier in force, at WAD_PRECISION. Stored balances stay raw underneath it. uiMultiplier0xa60bf13dThe ERC-8056 UI multiplier as it stands now. newUIMultiplier0xdc767007The UI multiplier waiting to take over, if one is scheduled. effectiveAt0x97a4064fWhen that pending multiplier is due to apply. updateUIMultiplier0x628e600fQueues a UI-multiplier change for the effectiveAt timestamp. cancelUIMultiplierUpdate0x2c97a0f0Drops the one pending change and returns the token to having none queued. updateMultiplier0x5ffe6146The emergency override. It sets the multiplier on the spot rather than scheduling it. toScaledBalance0x04f04c99Scales a raw balance up: rawBalance * multiplier / WAD_PRECISION. toRawBalance0x0ca06c44Scales a scaled balance back down to its raw form. scaledBalanceOf0x1da24f3eShorthand for toUIAmount(balanceOf(account)). balanceOfUI0x437a9958An account’s balance with the UI adjustment already applied. totalSupplyUI0x9bea6429Total supply with the UI adjustment already applied. toUIAmount0x3248d4ffConverts a raw amount into its UI form. fromUIAmount0x65cd9b3cConverts a UI amount back into its raw form. announce0x595135ddPublishes an announcement that affects holders and fires off each bundled call atomically. isAnnouncementIdUsed0xc0da474eWhether announce has already spent id. IDs never come back around. batchMint0x68573107Issues amounts[i] to recipients[i] across the arrays. It’s all or nothing — one bad element reverts the lot. extraMetadata0x4ddf9da0Reads a named metadata entry, giving back an empty string where none is set. updateExtraMetadata0xb2851ef5Writes a metadata entry, or clears it when you pass an empty value.
Tokens created as the STABLECOIN variant add one call.
Function Selector Behavior currency0xe5a6b10fThe currency code the stablecoin says it tracks, such as "USD", "EUR", or "JPY".
Note
B20 only checks that the code is well formed — uppercase A-Z and nothing else. It makes no judgment about the issuer’s claim, reserves, legal standing, or registration anywhere.
Every B20 token comes into existence through this one precompile.
Function Selector Behavior createB200x62975e6aDeploys a token of the requested variant at the address derived deterministically for it. getB20Address0x8c30260fTells you which address createB20 would pick for (variant, sender, salt). It never reverts. isB200xfa19b927Whether this factory made token, read straight off the address prefix. It never reverts. isB20Initialized0x6a45ba98Whether createB20 finished at token. It flips once and stays there.
The singleton registry holding every policy, keyed by uint64 policyId.
Function Selector Behavior createPolicy0xca5d55f6Opens an empty simple policy. Anyone can call it. createPolicyWithAccounts0xa2d3044fOpens a simple policy with accounts already in it. Anyone can call it. createCompositePolicy0x6fdd1491Opens a composite that joins existing simple policies under a logic rule. updateAllowlist0x3388fb5bSets each of accounts to allowed on an ALLOWLIST policy, in one batch. updateBlocklist0x5c4e51b8Sets each of accounts to blocked on a BLOCKLIST policy, in one batch. updateComposite0xbfe142c0Swaps a composite’s children out wholesale for childPolicyIds. stageUpdateAdmin0x1d7ae695Nominates a replacement admin for policyId. The sitting admin keeps control until the handover completes. finalizeUpdateAdmin0x33031a9cCloses out the two-step handover. The caller becomes admin and the pending slot empties. renounceAdmin0xefdb7fa3Gives up the policy permanently, which freezes its membership and children from then on. isAuthorized0x55a1179eWhether account clears policyId. It never reverts, and an unknown ID behaves as an empty set. policyExists0x330f5637Whether policyId is a built-in sentinel or an ID that was actually handed out. It never reverts. policyAdmin0x09dd0a47The sitting admin for policyId, or the zero address for built-ins. pendingPolicyAdmin0x017548b7The nominated admin for policyId, or the zero address when nothing is staged. compositePolicyChildIds0x7c40df74The children a composite policyId is built from. MIN_COMPOSITE_CHILD_POLICIES0xb3ae29f7The fewest children a composite can reference, inclusive. It never reverts. MAX_COMPOSITE_CHILD_POLICIES0x54309870The most children a composite can reference, inclusive. It never reverts.
The gate that decides which features are live on the chain.
Function Selector Behavior isActivated0xba87af80Whether feature is on right now. It never reverts. checkActivated0xde5bfd9bThe asserting form — it reverts with FeatureNotActivated(feature) when the feature is off. admin0xf851a440The only address cleared to call activate and deactivate. activate0x59db6e85Switches feature on and emits FeatureActivated. deactivate0x22eee84cSwitches feature off and emits FeatureDeactivated.
Look these up by topic0 when you’re decoding logs.
Event Interface Topic0 TransferIB200xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efApprovalIB200x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925MemoIB200x6989f5818dcfd11f8cd53b27c94cec33dae1589735f03e639cba54553a1825e8SeizedIB200xa9aec5d8b86e2fa2fd6ac3af62f2622e3dfdab1967d4cbbb56a5df7d74cb887cBurnedBlockedIB200x0b552e96653fd6842da37c477005d3b5c08a8c7d3631b1f43787b2dc9a1006a3RoleGrantedIB200x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0dRoleRevokedIB200xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171bRoleAdminChangedIB200xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ffLastAdminRenouncedIB200xe8d3a9872e7ca325571ff1e4c51ddd69090a0345240cc605ccde365ec867cc67PausedIB200x43e072977b8112813d7c2aa0b63d3c121c1fe3e714e6d2eacb5735fe4027e976UnpausedIB200xdc6281474ea3dea2a856e225c7ba3edf427de9164495817c4d49a595e683fed4PolicyUpdatedIB200x8b4790f7ff717fc8f60f07ae099e47ef318dc04b37ae98056b50a22b79056626SupplyCapUpdatedIB200x6d14f44808ce024f263432bc38d019a9951fbe674e9898b54844dbc8dc09c23aContractURIUpdatedIB200xa5d4097edda6d87cb9329af83fb3712ef77eeb13738ffe43cc35a4ce305ad962NameUpdatedIB200x74321da206c1b9fa34367f7ece59ca49371dcd13820b9a5c3767ae1ecceed51aSymbolUpdatedIB200x64e8b5c6dcea43dd79766bb3b8af7c45968d12b68c960cf2da23856f34d598d4EIP712DomainChangedIB200x0a6387c9ea3628b88a633bb4f3b151770f70085117a15f9bf3787cda53f13d31UIMultiplierUpdatedIB20Asset0x2205df4534432b2f60654a3fdb48737ffdaf3e9edb1a498bd985bc026b15b055MultiplierUpdatedIB20Asset0x4dbe4840d7465bd162f67814cea0b519567a2e0e578bcde61e7f4ced361e5a3dUIMultiplierUpdateCancelledIB20Asset0x883856335ba5f60c18b9817c4505d3c7d3f6223dcf39516b30c508c46a5e1cadExtraMetadataUpdatedIB20Asset0xd7bb345be29e78d635203d40fe0567e7ef19d5cd5cc5fcd25f768b8063e82aa1AnnouncementIB20Asset0xccebf8218a62875909564adef86a6f4df81503cb617221e793357d62f8e813f7EndAnnouncementIB20Asset0x96d64dafe2c790596430196b982ad1da3221cb3b0f4e6e2df77f2e4f71a90037B20CreatedIB20Factory0xfd9bf2730513a1709722ff379a0844dfd8f997d600693c2bcc659e188bbdba0dPolicyCreatedIPolicyRegistry0xdc870ce85be577234b8548f42b93f84bbff6d0c1f38ee725c809c49932a13885PolicyAdminStagedIPolicyRegistry0xdbf3b34a4c956c56ca05cd4b8f9293a4347ad61994445a4b89817d4a19561136PolicyAdminUpdatedIPolicyRegistry0x98925cfb1bc09c5b43dd0dd56d3d95aa04fb3300927580cc588c3f5dd58c15e1AllowlistUpdatedIPolicyRegistry0x18c46532f90187ba11e436e21da087b684801d7f0787f2043f26f079c91e9ef0BlocklistUpdatedIPolicyRegistry0x2ff63c102b1b9fd7f5d39f83039c5d6aaf50a414a4f2def2704e41be2628f1e3CompositePolicyUpdatedIPolicyRegistry0x4ff6adaab31b0df87aa7b8b7320c52b8b3b5eede3bf28a6baaaa8b8b7e1d6363FeatureActivatedIActivationRegistry0x8c7a0ecdbb8d96e867e43ec1aef80027976ee493c18bef399fa799ed19752451FeatureDeactivatedIActivationRegistry0x15bf65a782c3258c63268ba9d7aed710cf9f9315d3687d9a4632ccdad7926c84
Reverse lookup for revert selectors. A few names appear more than once — NonPayable and Unauthorized are shared across surfaces, and Unauthorized does not even carry the same selector everywhere, so read the interface column.
Error Selector Cause NonPayable0x6fb1b0e9ETH rode along with a call to a nonpayable token selector. AccessControlUnauthorizedAccount0xe2517d3faccount is missing neededRole.AccessControlBadConfirmation0x6697b232renounceRole got a callerConfirmation that wasn’t msg.sender.Unauthorized0x82b42900A positional authorization check failed — one that doesn’t reduce to a missing role. ContractPaused0xf9df5ac9The PausableFeature this operation falls under is paused. InsufficientAllowance0x192b9e4eThe transferFrom asked for more than spender is allowed to move. InsufficientBalance0xdb42144dThe transfer or burn asked for more than sender holds. InvalidSender0x4c14f64cThe source address on the transfer is not valid, typically address(0). InvalidReceiver0x9cfea583The destination address on the transfer is not valid, typically address(0). InvalidApprover0x8bc146c4The owner on the approval is not valid, typically address(0). InvalidSpender0x4e15efdaThe spender on the approval is not valid, typically address(0). InvalidAmount0x2c5211c6An amount came through as zero where a nonzero one is required. ERC-20 amount arguments are exempt. EmptyFeatureSet0x4861ff45An empty array reached a function that needs at least one entry. InvalidSupplyCap0x0a3780ceThe proposed cap sits outside the permitted band — under current supply, or over the maximum. SupplyCapExceeded0x4b344b11The mint would carry totalSupply past the cap. PolicyForbids0xa43fec12One of the policy slots turned the operation down. PolicyNotFound0xcccad523The policy ID given isn’t in the registry. UnsupportedPolicyType0xcdd98a4aThis token, or this variant of it, has no such policyScope. AccountNotSeizable0x91dbbc8dseizeWithMemo targeted a from that SEIZE_HOLDER_POLICY still authorizes.AccountNotBlocked0x64a5cb46The deprecated burnBlocked targeted a from that is still authorized. ExpiredSignature0xbd2a913cThe permit arrived with a deadline already behind block.timestamp. InvalidSigner0x7ba5ffb5ECDSA recovery on the permit produced a signer that isn’t the owner claimed. LastAdminCannotRenounce0x361513e7The sole remaining admin tried to leave via renounceRole(DEFAULT_ADMIN_ROLE, ...). NotSoleAdmin0x2a98e73brenounceLastAdmin() was called while other accounts still hold DEFAULT_ADMIN_ROLE.
Error Selector Cause AnnouncementIdAlreadyUsed0xd10b3c9eThe id handed to announce was already spent. AnnouncementInProgress0x5c5f0829A call dispatched from inside announce tried to call announce again. InternalCallMalformed0x4e2f143eA call dispatched by announce came in under four bytes. InternalCallFailed0xb288a127A call dispatched by announce reverted normally, and its reason is wrapped here. InvalidMetadataKey0x86ea3abbupdateExtraMetadata got an empty key.InvalidMultiplier0x6f12f3dcA multiplier setter was handed a value it won’t accept. EffectiveAtInPast0x14119cf6The effectiveAt on updateUIMultiplier isn’t in the future. EffectiveAtTooFar0x1ce214faThe effectiveAt on updateUIMultiplier overran type(uint64).max. UIMultiplierUpdateExists0x4481a68eupdateUIMultiplier was called while another update is still pending.UIMultiplierUpdateDoesNotExist0xa7d6a5cacancelUIMultiplierUpdate found nothing pending to cancel.LengthMismatch0xab8b67c6A batched call got parallel arrays of unequal length. EmptyBatch0xc2e5347dA batched call got empty arrays.
Error Selector Cause NonPayable0x6fb1b0e9ETH rode along with a call to a nonpayable factory selector. TokenAlreadyExists0x15ef3a57Something already occupies the deterministic address this creation derives. InvalidVariant0xf10e8e43The variant given isn’t a recognized B20Variant. UnsupportedVersion0xc0d8b4e0The leading version byte in params matches no known encoding for that variant. MissingRequiredField0x4a43ae87A required string argument came through empty. InvalidCurrency0x997c1de8The stablecoin currency had content, but carried a byte outside A-Z. InvalidDecimals0xca950391The asset decimals fell outside the permitted inclusive range. InitCallFailed0x4eae0860One of the initCalls reverted, and the factory passes the underlying reason back up.
Error Selector Cause NonPayable0x6fb1b0e9ETH rode along with a call to a nonpayable registry selector. Unauthorized0x82b42900The caller isn’t the admin the operation demands. PolicyNotFound0x720caa4fThe policy ID referenced doesn’t exist. IncompatiblePolicyType0xf1011ef5The operation doesn’t fit this policy’s type. ZeroAddress0xd92e233dAn address argument that has to be set came through as the zero address. BatchSizeTooLarge0x083e2f67A membership batch ran past the registry’s limit. NoPendingAdmin0xb4539afafinalizeUpdateAdmin ran with nobody staged.ChildPoliciesOutsideOfRange0x697ec868A composite’s child count fell outside the permitted range. InvalidChildPolicy0x46508ef6A composite’s children must be existing simple policies. Composites can’t nest.
Error Selector Cause Unauthorized0x8e4a23d6The caller isn’t the activation admin. AlreadyActivated0x866b0041The feature is already on. FeatureNotActivated0xb9b2a425The feature is off. DelegateCallNotAllowed0x0d89438eThe precompile was reached through DELEGATECALL or CALLCODE. StaticCallNotAllowed0xbeaba5b7A state-changing entry point was reached from inside a STATICCALL frame.
Role Solidity constant Default admin bytes32(0)Mint keccak256("MINT_ROLE")Burn keccak256("BURN_ROLE")Deprecated blocked burn keccak256("BURN_BLOCKED_ROLE")Seize keccak256("SEIZE_ROLE")Pause keccak256("PAUSE_ROLE")Unpause keccak256("UNPAUSE_ROLE")Metadata keccak256("METADATA_ROLE")Asset operator keccak256("OPERATOR_ROLE")
Scope Solidity constant Transfer sender keccak256("TRANSFER_SENDER_POLICY")Transfer receiver keccak256("TRANSFER_RECEIVER_POLICY")Transfer executor keccak256("TRANSFER_EXECUTOR_POLICY")Mint receiver keccak256("MINT_RECEIVER_POLICY")Seize holder keccak256("SEIZE_HOLDER_POLICY")Seize receiver keccak256("SEIZE_RECEIVER_POLICY")
Two counters are reserved for the built-ins; anything you create starts at 2.
Name Value ALWAYS_ALLOW0ALWAYS_BLOCK(uint64(uint8(IPolicyRegistry.PolicyType.ALLOWLIST)) << 56) | 1
Custom IDs pack a type byte over a counter:
[8-bit PolicyType][56-bit counter]
PolicyType Byte BLOCKLIST0x00ALLOWLIST0x01UNION0x02INTERSECT0x03
The variant is encoded into the address, so you can read it off the prefix.
Variant Byte Address shape ASSET0x000xB200...STABLECOIN0x010xB201...
Constant Value Minimum Asset decimals 6Maximum Asset decimals 18Maximum supply cap, and the no-cap sentinel type(uint128).maxBitmask with every feature paused 15 — TRANSFER, MINT, BURN, SEIZE together