> ## Documentation Index
> Fetch the complete documentation index at: https://base-a060aa97-roethke-b20-upgrade-pages.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> Native token standard for issuing programmable assets and stablecoins on Base, with roles, policies, supply controls, and ERC-20 compatibility.

B20 is Base's native token standard for issuing and managing programmable assets onchain.

This document provides a high-level introduction to B20: what it is, why it exists, the core primitives it exposes, and how those pieces fit together.

## Concepts

<CardGroup cols={2}>
  <Card title="Token Types" icon="coins" href="/specifications/b20/concepts/token-types">
    Compare the Asset and Stablecoin variants and their capabilities.
  </Card>

  <Card title="Policies" icon="shield-check" href="/specifications/b20/concepts/policies">
    Configure allowlists, blocklists, composite policies, and policy scopes.
  </Card>

  <Card title="Roles and Pause" icon="user-lock" href="/specifications/b20/concepts/roles-and-pause">
    Assign operational permissions and pause individual token features.
  </Card>

  <Card title="UI Multipliers" icon="chart-column" href="/specifications/b20/concepts/multipliers">
    Understand Asset multiplier behavior and ERC-8056 UI balances.
  </Card>
</CardGroup>

## Build on Base

Use these Build on Base demos to create and operate B20 tokens.

<CardGroup cols={2}>
  <Card title="Create an Asset Token" icon="building-columns" href="/build-on-base/issue-rwa/create-an-asset-token">
    Create a B20 Asset token with deterministic factory deployment.
  </Card>

  <Card title="Issue a Stablecoin" icon="coins" href="/build-on-base/issue-stablecoins/issue-your-stablecoin">
    Create a B20 Stablecoin with an immutable currency code.
  </Card>

  <Card title="Restrict Eligible Holders" icon="user-check" href="/build-on-base/issue-rwa/restrict-eligible-holders">
    Apply a policy that limits asset ownership to eligible accounts.
  </Card>

  <Card title="Apply a Multiplier" icon="chart-column" href="/build-on-base/issue-rwa/apply-a-multiplier">
    Schedule or apply an Asset UI multiplier for a corporate action.
  </Card>
</CardGroup>

## Reference

For technical details, see the [B20 interfaces](/specifications/b20/reference/interfaces) and [B20 constants](/specifications/b20/reference/constants).

<CardGroup cols={2}>
  <Card title="Interfaces" icon="file-contract" href="/specifications/b20/reference/interfaces">
    Browse B20 interfaces, canonical Solidity sources, and copy-paste imports.
  </Card>

  <Card title="Constants" icon="table" href="/specifications/b20/reference/constants">
    Find precompile addresses, role identifiers, policy scopes, and validation bounds.
  </Card>

  <Card title="Errors" icon="bug" href="/specifications/b20/reference/errors">
    Review custom errors, selectors, and the conditions that trigger them.
  </Card>

  <Card title="Events" icon="bolt" href="/specifications/b20/reference/events">
    Review B20 events and when each interface emits them.
  </Card>
</CardGroup>

## Changelog

<Card title="Changelog" href="/specifications/b20/changelog">
  Review B20 updates and protocol changes over time.
</Card>

***

## What is B20?

B20 is Base's native token standard for issuing and managing programmable assets onchain. Base created it to standardize real-world asset (RWA) and stablecoin issuance. B20 is an ERC-20 superset: balances, transfers, and approvals work like ERC-20, and every B20 asset shares the same additional interfaces and protocol logic rather than each issuer deploying a custom token implementation.

The standard also includes compliance and administrative controls. Issuers can configure roles and permissions, attach policies, mint and burn supply, pause operations, and perform other administrative actions that regulated-asset workflows typically require.

B20 runs as precompiles in the Base node, not as per-token Solidity. Wallets, issuers, and apps call ERC-20-style interfaces; the node runs the shared B20 logic natively. Base upgrades that logic through hardforks, so every caller gets consistent behavior and native execution across all B20 assets.

### At a Glance

```mermaid At a Glance lines wrap expandable highlight={1} theme={null}
flowchart TD
    W[Wallets]
    I[Issuers]
    A[Apps]
    B[B20 interface]
    N[Node]
    P[Precompile]
    L[Shared logic]
    W --> B
    I --> B
    A --> B
    B -->|call| N
    N --> P
    P --> L
```

You call a B20 asset the same way you call any other contract: through its interface at the asset address. Every B20 asset uses that same interface and the same precompile logic, so integrators have one source of truth.

***

## Why B20?

Real-world asset (RWA) issuance onchain needs a shared token standard with compliance built into the asset. ERC-20 covers balances, transfers, and approvals. Regulated assets also need eligibility checks, roles, mint and burn, pausing, and other administrative controls. Issuers rebuild those primitives for almost every tokenized asset.

Issuers who implement that stack themselves repeat the same logic, diverge in behavior, and force every wallet and app to integrate a custom token. B20 is the alternative: you create a B20 asset and configure its roles and policies instead of writing and maintaining a one-off token. Compliance is a first-class primitive, not an add-on each issuer designs around transfers.

A single standard also helps integrators and issuers. Wallets and apps integrate against one interface. Issuers can use shared services, such as oracles, without designing a new integration for each asset.

***

## Creating a B20 Asset

Every B20 token is created through the Factory, a singleton precompile. You submit `createB20` to a Base node the same way you submit any other contract call.

```mermaid Creating a B20 Asset Diagram lines wrap expandable highlight={1} theme={null}
sequenceDiagram
    participant Issuer
    participant Factory
    participant Token as B20 token

    Issuer->>Factory: createB20(variant, salt, params, initCalls)
    Factory->>Token: seal identity
    Factory->>Token: initCalls (grantRole, updatePolicy, mint)
    Factory-->>Issuer: token address
```

1. The issuer calls `createB20` with a variant, a salt, and creation parameters (name, symbol, initial admin, and variant-specific fields).
2. The Factory assigns a deterministic address from `(variant, sender, salt)` and seals the token's identity.
3. Optional `initCalls` run on the new token so the issuer can grant roles, attach policies, or mint in the same transaction.
4. `createB20` returns. The Factory retains no ongoing access to the token.

Choose **Asset** for general-purpose issuance, including RWAs, or **Stablecoin** for a fiat-pegged token with a fixed currency code. Both variants share roles, policies, and the ERC-20 surface. See [Token Types](/specifications/b20/concepts/token-types).

The Activation Registry is a Base-operated safety switch that turns Factory and token features on. Issuers and apps do not operate it.

***

## Configuring Roles

Roles let an issuer assign each privileged operation to a specific account. An admin can grant minting to a minter, seizing to a compliance operator, and pausing of a single feature (`TRANSFER`, `MINT`, `BURN`, or `SEIZE`) without pausing the rest of the token.

B20 implements this with [OpenZeppelin AccessControl](https://docs.openzeppelin.com/contracts/5.x/access-control) on the token. Roles are not a separate registry. One `DEFAULT_ADMIN_ROLE` holder grants and revokes the operating roles. A privileged call checks the role first, then the matching pause vector. Holder `transfer` skips the role check; it still hits the `TRANSFER` pause vector and policy.

The full role list and what each role gates is in [Roles and Pause](/specifications/b20/concepts/roles-and-pause). A role-gated call looks like this:

```mermaid Configuring Roles Diagram lines wrap expandable highlight={1} theme={null}
sequenceDiagram
    participant Admin
    participant Token as B20 token
    participant Caller

    Caller->>Token: mint(to, amount)
    Token-->>Caller: revert AccessControlUnauthorizedAccount

    Admin->>Token: grantRole(MINT_ROLE, Caller)
    Caller->>Token: mint(to, amount)
    Token-->>Caller: allowed
```

1. At creation, `initialAdmin` holds `DEFAULT_ADMIN_ROLE`.
2. That admin grants operating roles such as `MINT_ROLE` and `PAUSE_ROLE`.
3. A caller without the required role is rejected with `AccessControlUnauthorizedAccount`.

***

## Pause Vectors

Pause vectors stop a class of operations on a token without pausing the rest of the asset. An issuer uses them when an off-chain workflow needs a feature frozen (for example a settlement window), or when a vulnerability is found and that path must stop immediately.

Pause is per feature, not global. The four vectors are `TRANSFER`, `MINT`, `BURN`, and `SEIZE`. Pausing `MINT` halts new issuance while transfers continue. `approve` is not pause-gated.

`pause` requires `PAUSE_ROLE`. `unpause` requires `UNPAUSE_ROLE`. Those roles are separate, so the account that pauses does not have to be the account that resumes.

A paused call looks like this:

```mermaid Pause Vectors Diagram lines wrap expandable highlight={1} theme={null}
sequenceDiagram
    participant Pauser
    participant Token as B20 token
    participant Caller
    participant Unpauser

    Caller->>Token: mint(to, amount)
    Token-->>Caller: allowed

    Pauser->>Token: pause([MINT])
    Caller->>Token: mint(to, amount)
    Token-->>Caller: revert ContractPaused(MINT)

    Unpauser->>Token: unpause([MINT])
    Caller->>Token: mint(to, amount)
    Token-->>Caller: allowed
```

1. A caller who holds `MINT_ROLE` can mint while `MINT` is unpaused.
2. An account with `PAUSE_ROLE` pauses `MINT`. Other features stay live.
3. The next `mint` reverts with `ContractPaused(MINT)`, even if the caller still holds `MINT_ROLE`.
4. An account with `UNPAUSE_ROLE` unpauses `MINT`. Minting works again.

***

## Integrating Compliance Checks

Most compliance checks reduce to a set of addresses and an allow-or-deny decision on a specific function. B20 uses that model instead of per-token hooks: you maintain an allowlist or blocklist, bind it to a function on the token, and the call proceeds or reverts.

Those lists live in the Policy Registry, a global singleton precompile, not on the token. Allowlists, blocklists, and composite policies (union or intersect) are stored there and referenced by policy ID. Because the registry is shared, one list can back many tokens: you maintain membership once, and every attached token sees the same result.

A token admin binds a policy ID to a policy scope with `updatePolicy`. A scope sits in a similar place to a hook: it runs on a specific function. When that function runs, the token asks the registry `isAuthorized(policyId, account)` and reverts with `PolicyForbids` if the check fails. Which scope runs on which function is in [Policies](/specifications/b20/concepts/policies).

A policy-gated transfer looks like this:

```mermaid Integrating Compliance Checks Diagram lines wrap expandable highlight={1} theme={null}
sequenceDiagram
    participant Admin
    participant Registry as Policy Registry
    participant Token as B20 token
    participant Alice

    Admin->>Registry: createPolicy(ALLOWLIST)
    Admin->>Token: updatePolicy(TRANSFER_RECEIVER_POLICY, id)
    Alice->>Token: transfer(Bob)
    Token->>Registry: isAuthorized(id, Bob)
    Registry-->>Token: false
    Token-->>Alice: revert PolicyForbids

    Admin->>Registry: updateAllowlist(Bob)
    Alice->>Token: transfer(Bob)
    Token->>Registry: isAuthorized(id, Bob)
    Registry-->>Token: true
    Token-->>Alice: allowed
```

1. Create an allowlist or blocklist on the registry.
2. The token admin binds that policy ID to a scope.
3. On `transfer`, the token asks the registry whether the receiver is authorized.
4. Authorized: the call continues. Denied: the call reverts with `PolicyForbids`.
5. Unset scopes default to always-allow. `approve` is not policy-gated.
