From 71c570691d133205156fdedb4321ef294a58248c Mon Sep 17 00:00:00 2001 From: Youssef Date: Fri, 25 Sep 2026 15:15:53 +0100 Subject: [PATCH] Consolidate B20 overview at specification root Co-authored-by: Toshi --- docs/AGENTS.md | 2 +- .../integrate-defi/list-tokenized-stocks.mdx | 2 +- .../issue-rwa/create-an-asset-token.mdx | 2 +- .../issue-rwa/restrict-eligible-holders.mdx | 2 +- .../issue-stablecoins/block-an-account.mdx | 4 +- .../issue-stablecoins/burn-supply.mdx | 6 +- .../issue-your-stablecoin.mdx | 6 +- .../issue-stablecoins/mint-supply.mdx | 6 +- .../issue-stablecoins/pause-activity.mdx | 6 +- .../reconcile-with-memos.mdx | 4 +- .../issue-stablecoins/recover-funds.mdx | 4 +- .../restrict-who-can-hold.mdx | 8 +- docs/docs.json | 11 +- docs/get-started/issue-rwa.mdx | 2 +- docs/get-started/issue-stablecoins.mdx | 2 +- docs/llms-full.txt | 4 +- docs/llms.txt | 4 +- docs/snippets/RwaDisclaimer.mdx | 2 +- docs/specifications/b20/architecture.mdx | 2 +- docs/specifications/b20/changelog.mdx | 26 +- docs/specifications/b20/index.mdx | 258 +++++++++++++----- .../b20/specification-overview.mdx | 199 -------------- docs/specifications/overview.mdx | 2 +- .../__tests__/base-std-routing.test.mjs | 16 +- scripts/sync-from-base-std/route-table.json | 36 +-- 25 files changed, 267 insertions(+), 349 deletions(-) delete mode 100644 docs/specifications/b20/specification-overview.mdx diff --git a/docs/AGENTS.md b/docs/AGENTS.md index 4dd492f89..7c237aed7 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -58,7 +58,7 @@ npx skills add base/base-skills |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 |Specifications/Specifications/Base Protocol/Bridging:specifications/base-protocol/bridging/standard-bridges,specifications/base-protocol/bridging/deposits,specifications/base-protocol/bridging/withdrawals,specifications/base-protocol/bridging/cross-domain-messengers,specifications/base-protocol/bridging/base-solana-bridge |Specifications/Specifications/Base Protocol/Proofs:specifications/base-protocol/proofs/overview,specifications/base-protocol/proofs/challenger,specifications/base-protocol/proofs/proposer,specifications/base-protocol/proofs/registrar,specifications/base-protocol/proofs/tee-prover,specifications/base-protocol/proofs/zk-prover,specifications/base-protocol/proofs/proof-contracts -|Specifications/Specifications/B20:specifications/b20/index,specifications/b20/specification-overview,specifications/b20/architecture,specifications/b20/changelog +|Specifications/Specifications/B20:specifications/b20/index,specifications/b20/architecture,specifications/b20/changelog |Specifications/Specifications/B20/Concepts:specifications/b20/concepts/token-types,specifications/b20/concepts/policies,specifications/b20/concepts/roles-and-pause,specifications/b20/concepts/multipliers |Specifications/Specifications/B20/Reference:specifications/b20/reference/interfaces,specifications/b20/reference/constants,specifications/b20/reference/errors,specifications/b20/reference/events |Specifications/Specifications/Transactions:specifications/transactions/transaction-ordering,specifications/transactions/transaction-finality,specifications/transactions/network-fees,specifications/transactions/throughput-and-limits,specifications/transactions/troubleshooting-transactions diff --git a/docs/build-on-base/integrate-defi/list-tokenized-stocks.mdx b/docs/build-on-base/integrate-defi/list-tokenized-stocks.mdx index bf8635c67..b8913f925 100644 --- a/docs/build-on-base/integrate-defi/list-tokenized-stocks.mdx +++ b/docs/build-on-base/integrate-defi/list-tokenized-stocks.mdx @@ -187,7 +187,7 @@ To fetch these addresses programmatically, use the [List Tokenized Stocks](/sdks ## Additional Resources - [Tokenized Stocks API](/sdks/tokenized-stocks/overview) -- [B20 Standard](/specifications/b20/specification-overview) +- [B20 Standard](/specifications/b20) - [Base Standard Library](https://github.com/base/base-std/tree/main) ## Disclaimer diff --git a/docs/build-on-base/issue-rwa/create-an-asset-token.mdx b/docs/build-on-base/issue-rwa/create-an-asset-token.mdx index d7054f39a..6045e4f8c 100644 --- a/docs/build-on-base/issue-rwa/create-an-asset-token.mdx +++ b/docs/build-on-base/issue-rwa/create-an-asset-token.mdx @@ -7,7 +7,7 @@ keywords: ["create asset token Base", "B20 Asset token", "tokenized asset Base", import { AssetDemo } from "/snippets/AssetDemo.jsx" import RwaDisclaimer from "/snippets/RwaDisclaimer.mdx" -Use the [B20 Asset variant](/specifications/b20/specification-overview#asset) to represent a class of tokenized shares with configurable precision, issuer roles, supply controls, and metadata. This example creates Example Corp Class A (`EXM`) with six decimals. +Use the [B20 Asset variant](/specifications/b20/concepts/token-types#4-asset) to represent a class of tokenized shares with configurable precision, issuer roles, supply controls, and metadata. This example creates Example Corp Class A (`EXM`) with six decimals. diff --git a/docs/build-on-base/issue-rwa/restrict-eligible-holders.mdx b/docs/build-on-base/issue-rwa/restrict-eligible-holders.mdx index 07f73ef89..ea2b40511 100644 --- a/docs/build-on-base/issue-rwa/restrict-eligible-holders.mdx +++ b/docs/build-on-base/issue-rwa/restrict-eligible-holders.mdx @@ -7,7 +7,7 @@ keywords: ["asset token allowlist", "B20 Policy Registry", "eligible holders", " import { AssetDemo } from "/snippets/AssetDemo.jsx" import RwaDisclaimer from "/snippets/RwaDisclaimer.mdx" -Create an allowlist in the [Policy Registry](/specifications/b20/specification-overview#policy-registry), then apply it when shares are issued, sent, or received. Accounts remain ineligible until the policy admin adds them. +Create an allowlist in the [Policy Registry](/specifications/b20#integrating-compliance-checks), then apply it when shares are issued, sent, or received. Accounts remain ineligible until the policy admin adds them. diff --git a/docs/build-on-base/issue-stablecoins/block-an-account.mdx b/docs/build-on-base/issue-stablecoins/block-an-account.mdx index 3d8fca74e..1992141d0 100644 --- a/docs/build-on-base/issue-stablecoins/block-an-account.mdx +++ b/docs/build-on-base/issue-stablecoins/block-an-account.mdx @@ -17,7 +17,7 @@ The demo uses a local browser-generated account to submit real transactions on * -New to B20? See the [B20 Token Standard](/specifications/b20/specification-overview) for the concepts and a full launch walkthrough. These samples target `base-std@be6d045`, `viem@2.55.11`, and Base Foundry `v1.1.1`. +New to B20? See the [B20 Token Standard](/specifications/b20) for the concepts and a full launch walkthrough. These samples target `base-std@be6d045`, `viem@2.55.11`, and Base Foundry `v1.1.1`. ## Block and Verify an Account @@ -64,7 +64,7 @@ base-cast call "$POLICY_REGISTRY" "isAuthorized(uint64,address)(bool)" \ ``` -See the [B20 token standard](/specifications/b20/specification-overview) for the complete interface, roles, and policies. +See the [B20 token standard](/specifications/b20) for the complete interface, roles, and policies. `isAuthorized(policyId, account)` returns `false` while the account is blocked. diff --git a/docs/build-on-base/issue-stablecoins/burn-supply.mdx b/docs/build-on-base/issue-stablecoins/burn-supply.mdx index 95194a78e..7c28ae348 100644 --- a/docs/build-on-base/issue-stablecoins/burn-supply.mdx +++ b/docs/build-on-base/issue-stablecoins/burn-supply.mdx @@ -17,7 +17,7 @@ The demo uses a local browser-generated account to submit real transactions on * -New to B20? See the [B20 Token Standard](/specifications/b20/specification-overview) for the concepts and a full launch walkthrough. These samples target `base-std@be6d045`, `viem@2.55.11`, and Base Foundry `v1.1.1`. +New to B20? See the [B20 Token Standard](/specifications/b20) for the concepts and a full launch walkthrough. These samples target `base-std@be6d045`, `viem@2.55.11`, and Base Foundry `v1.1.1`. ## Burn and Verify Supply @@ -54,7 +54,7 @@ base-cast call "$TOKEN_ADDRESS" "totalSupply()(uint256)" --rpc-url "$RPC_URL" ``` -See the [B20 token standard](/specifications/b20/specification-overview) for the complete interface, roles, and policies. +See the [B20 token standard](/specifications/b20) for the complete interface, roles, and policies. `totalSupply()` decreases by exactly 400 MUSD. @@ -70,7 +70,7 @@ See the [B20 token standard](/specifications/b20/specification-overview) for the Increase circulating supply. - + The burn operation in the B20 standard. diff --git a/docs/build-on-base/issue-stablecoins/issue-your-stablecoin.mdx b/docs/build-on-base/issue-stablecoins/issue-your-stablecoin.mdx index c48852706..a794b39f5 100644 --- a/docs/build-on-base/issue-stablecoins/issue-your-stablecoin.mdx +++ b/docs/build-on-base/issue-stablecoins/issue-your-stablecoin.mdx @@ -6,7 +6,7 @@ description: "Create a fiat-backed stablecoin on Base with one B20 factory call. import { StablecoinDemo } from "/snippets/StablecoinDemo.jsx" -Create a fiat-backed token with one call to the [B20 Factory](/specifications/b20/specification-overview#factory), using the `STABLECOIN` variant. Decimals are fixed at `6`, and the token carries an immutable, self-declared currency code such as `USD`. The type is sealed into the token address at creation and cannot change. +Create a fiat-backed token with one call to the [B20 Factory](/specifications/b20#creating-a-b20-asset), using the `STABLECOIN` variant. Decimals are fixed at `6`, and the token carries an immutable, self-declared currency code such as `USD`. The type is sealed into the token address at creation and cannot change. ## Demo @@ -17,7 +17,7 @@ The demo uses a local browser-generated account to submit real transactions on * -New to B20? See the [B20 Token Standard](/specifications/b20/specification-overview) for the concepts and a full launch walkthrough. These samples target `base-std@be6d045`, `viem@2.55.11`, and Base Foundry `v1.1.1`. +New to B20? See the [B20 Token Standard](/specifications/b20) 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 the Stablecoin Variant Works @@ -97,7 +97,7 @@ export async function createStablecoin() { The `params` blob is ABI-encoded with a leading `version` byte (currently `1`) as `B20StablecoinCreateParams`: `version`, `name`, `symbol`, `initialAdmin`, and `currency`. Optional `initCalls` run on the new token in the same transaction; the factory drops access after they complete. -See the [B20 token standard](/specifications/b20/specification-overview) for the complete interface, roles, and policies. +See the [B20 token standard](/specifications/b20) for the complete interface, roles, and policies. The factory emits `B20Created`, and the returned token address is ready for minting. diff --git a/docs/build-on-base/issue-stablecoins/mint-supply.mdx b/docs/build-on-base/issue-stablecoins/mint-supply.mdx index 42ae0a9a1..fddc5cd00 100644 --- a/docs/build-on-base/issue-stablecoins/mint-supply.mdx +++ b/docs/build-on-base/issue-stablecoins/mint-supply.mdx @@ -17,7 +17,7 @@ The demo uses a local browser-generated account to submit real transactions on * -New to B20? See the [B20 Token Standard](/specifications/b20/specification-overview) for the concepts and a full launch walkthrough. These samples target `base-std@be6d045`, `viem@2.55.11`, and Base Foundry `v1.1.1`. +New to B20? See the [B20 Token Standard](/specifications/b20) for the concepts and a full launch walkthrough. These samples target `base-std@be6d045`, `viem@2.55.11`, and Base Foundry `v1.1.1`. ## Mint and Verify Supply @@ -53,7 +53,7 @@ base-cast call "$TOKEN_ADDRESS" "balanceOf(address)(uint256)" "$HOLDER" --rpc-ur ``` -See the [B20 token standard](/specifications/b20/specification-overview) for the complete interface, roles, and policies. +See the [B20 token standard](/specifications/b20) for the complete interface, roles, and policies. The holder balance increases by `1,000,000,000` base units, or 1,000 MUSD. @@ -69,7 +69,7 @@ The caller needs `MINT_ROLE`, the recipient must pass `MINT_RECEIVER_POLICY`, an Remove tokens from supply. - + Supply cap in the B20 standard. diff --git a/docs/build-on-base/issue-stablecoins/pause-activity.mdx b/docs/build-on-base/issue-stablecoins/pause-activity.mdx index 0695effab..6585a9ad8 100644 --- a/docs/build-on-base/issue-stablecoins/pause-activity.mdx +++ b/docs/build-on-base/issue-stablecoins/pause-activity.mdx @@ -19,7 +19,7 @@ The demo uses a local browser-generated account to submit real transactions on * -New to B20? See the [B20 Token Standard](/specifications/b20/specification-overview) for the concepts and a full launch walkthrough. These samples target `base-std@be6d045`, `viem@2.55.11`, and Base Foundry `v1.1.1`. +New to B20? See the [B20 Token Standard](/specifications/b20) for the concepts and a full launch walkthrough. These samples target `base-std@be6d045`, `viem@2.55.11`, and Base Foundry `v1.1.1`. ## Pause and Resume Transfers @@ -53,7 +53,7 @@ base-cast send "$TOKEN_ADDRESS" "unpause(uint8[])" "[0]" \ ``` -See the [B20 token standard](/specifications/b20/specification-overview) for the complete interface, roles, and policies. +See the [B20 token standard](/specifications/b20) for the complete interface, roles, and policies. `isPaused(0)` returns `true` after pausing and `false` after resuming. @@ -80,7 +80,7 @@ When a paused feature blocks a call, the transaction reverts `ContractPaused(fea Deny a specific address. - + Pause controls in the B20 standard. diff --git a/docs/build-on-base/issue-stablecoins/reconcile-with-memos.mdx b/docs/build-on-base/issue-stablecoins/reconcile-with-memos.mdx index 5b7103b75..a9b567e27 100644 --- a/docs/build-on-base/issue-stablecoins/reconcile-with-memos.mdx +++ b/docs/build-on-base/issue-stablecoins/reconcile-with-memos.mdx @@ -19,7 +19,7 @@ The demo uses a local browser-generated account to submit real transactions on * -New to B20? See the [B20 Token Standard](/specifications/b20/specification-overview) for the concepts and a full launch walkthrough. These samples target `base-std@be6d045`, `viem@2.55.11`, and Base Foundry `v1.1.1`. +New to B20? See the [B20 Token Standard](/specifications/b20) for the concepts and a full launch walkthrough. These samples target `base-std@be6d045`, `viem@2.55.11`, and Base Foundry `v1.1.1`. ## Attach and Read an Invoice Memo @@ -52,7 +52,7 @@ base-cast receipt "$TX" --rpc-url "$RPC_URL" ``` -See the [B20 token standard](/specifications/b20/specification-overview) for the complete interface, roles, and policies. +See the [B20 token standard](/specifications/b20) for the complete interface, roles, and policies. The receipt contains `Transfer` followed immediately by `Memo`, with `invoice-8842` encoded as `bytes32`. diff --git a/docs/build-on-base/issue-stablecoins/recover-funds.mdx b/docs/build-on-base/issue-stablecoins/recover-funds.mdx index 1a1e662d1..6a3c09076 100644 --- a/docs/build-on-base/issue-stablecoins/recover-funds.mdx +++ b/docs/build-on-base/issue-stablecoins/recover-funds.mdx @@ -17,7 +17,7 @@ The demo uses a local browser-generated account to submit real transactions on * -New to B20? See the [B20 Token Standard](/specifications/b20/specification-overview) for the concepts and a full launch walkthrough. These samples target `base-std@be6d045`, `viem@2.55.11`, and Base Foundry `v1.1.1`. +New to B20? See the [B20 Token Standard](/specifications/b20) for the concepts and a full launch walkthrough. These samples target `base-std@be6d045`, `viem@2.55.11`, and Base Foundry `v1.1.1`. ## Burn and Reissue the Blocked Balance @@ -56,7 +56,7 @@ base-cast call "$TOKEN_ADDRESS" "balanceOf(address)(uint256)" "$REPLACEMENT" --r ``` -See the [B20 token standard](/specifications/b20/specification-overview) for the complete interface, roles, and policies. +See the [B20 token standard](/specifications/b20) for the complete interface, roles, and policies. The blocked balance falls and the replacement address receives the same amount, leaving circulating supply unchanged. diff --git a/docs/build-on-base/issue-stablecoins/restrict-who-can-hold.mdx b/docs/build-on-base/issue-stablecoins/restrict-who-can-hold.mdx index 9e1a67a3a..0bfe4bbc7 100644 --- a/docs/build-on-base/issue-stablecoins/restrict-who-can-hold.mdx +++ b/docs/build-on-base/issue-stablecoins/restrict-who-can-hold.mdx @@ -6,7 +6,7 @@ description: "Limit transfers of your stablecoin to accounts your KYC program ha import { StablecoinDemo } from "/snippets/StablecoinDemo.jsx" -Keep your stablecoin within a known set of holders with an **allowlist**: transfers only settle between accounts your KYC program has approved. You manage the list in the [Policy Registry](/specifications/b20/specification-overview#policy-registry) and bind it to the token's transfer scopes. +Keep your stablecoin within a known set of holders with an **allowlist**: transfers only settle between accounts your KYC program has approved. You manage the list in the [Policy Registry](/specifications/b20#integrating-compliance-checks) and bind it to the token's transfer scopes. ## Demo @@ -17,7 +17,7 @@ The demo uses a local browser-generated account to submit real transactions on * -New to B20? See the [B20 Token Standard](/specifications/b20/specification-overview) for the concepts and a full launch walkthrough. These samples target `base-std@be6d045`, `viem@2.55.11`, and Base Foundry `v1.1.1`. +New to B20? See the [B20 Token Standard](/specifications/b20) 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 @@ -93,7 +93,7 @@ An allowlist denies every account not in the policy. Seed intended holders befor After creation, only the policy admin can add or remove accounts. Call `updateAllowlist(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. See the [B20 token standard](/specifications/b20/specification-overview) for the full policy type reference. +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. See the [B20 token standard](/specifications/b20) for the full policy type reference. ## See Also @@ -101,7 +101,7 @@ To combine a KYC allowlist with a sanctions blocklist, create an `INTERSECT` com Deny a specific address. - + Policy hooks in the B20 standard. diff --git a/docs/docs.json b/docs/docs.json index 7b7ceccc2..4898df077 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -245,7 +245,6 @@ "group": "B20", "pages": [ "specifications/b20/index", - "specifications/b20/specification-overview", "specifications/b20/architecture", { "group": "Concepts", @@ -629,6 +628,10 @@ ] }, "redirects": [ + { + "source": "/specifications/b20/specification-overview", + "destination": "/specifications/b20" + }, { "source": "/build-on-base/accept-payments/capture-a-partial-amount", "destination": "/build-on-base/accept-payments/capture-an-authorization#capture-a-partial-amount" @@ -2691,7 +2694,7 @@ }, { "source": "/base-chain/specs/b20/overview", - "destination": "/specifications/b20/specification-overview" + "destination": "/specifications/b20" }, { "source": "/base-chain/specs/b20/changelog", @@ -2931,7 +2934,7 @@ }, { "source": "/base-chain/specs/reference/b20/index", - "destination": "/specifications/b20/specification-overview" + "destination": "/specifications/b20" }, { "source": "/base-chain/network-information/b20-token-standard", @@ -3483,7 +3486,7 @@ }, { "source": "/base-chain/specs/reference/b20", - "destination": "/specifications/b20/specification-overview" + "destination": "/specifications/b20" }, { "source": "/base-chain/specs/protocol/consensus", diff --git a/docs/get-started/issue-rwa.mdx b/docs/get-started/issue-rwa.mdx index 878e7cc96..c64b68676 100644 --- a/docs/get-started/issue-rwa.mdx +++ b/docs/get-started/issue-rwa.mdx @@ -7,7 +7,7 @@ keywords: ["issue RWA Base", "real-world asset tokenization", "B20 Asset", "toke import { AssetDemo } from "/snippets/AssetDemo.jsx" import RwaDisclaimer from "/snippets/RwaDisclaimer.mdx" -Represent a real-world asset with the [B20 Asset standard](/specifications/b20/specification-overview#asset). Configure precision and issuer roles, distribute units, restrict eligible holders, and run distributions through one ERC-20-compatible surface built into Base. The guides below use a stock token as the worked example; the same flows apply to other asset types. +Represent a real-world asset with the [B20 Asset standard](/specifications/b20/concepts/token-types#4-asset). Configure precision and issuer roles, distribute units, restrict eligible holders, and run distributions through one ERC-20-compatible surface built into Base. The guides below use a stock token as the worked example; the same flows apply to other asset types. diff --git a/docs/get-started/issue-stablecoins.mdx b/docs/get-started/issue-stablecoins.mdx index 3a34f60a7..fd72c3088 100644 --- a/docs/get-started/issue-stablecoins.mdx +++ b/docs/get-started/issue-stablecoins.mdx @@ -6,7 +6,7 @@ description: "Run a fiat-backed stablecoin on Base with minting, compliance, and import { StablecoinDemo } from "/snippets/StablecoinDemo.jsx" -Issue a fiat-backed stablecoin on Base with [B20](/specifications/b20/specification-overview), Base's native token standard. Minting, redemption, compliance controls, and reconciliation ship with the chain, so there's no custom contract to build or audit, and it's fully ERC-20 compatible. +Issue a fiat-backed stablecoin on Base with [B20](/specifications/b20), Base's native token standard. Minting, redemption, compliance controls, and reconciliation ship with the chain, so there's no custom contract to build or audit, and it's fully ERC-20 compatible. ## Demo diff --git a/docs/llms-full.txt b/docs/llms-full.txt index 8bf70c8c7..9e169959a 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -292,9 +292,7 @@ const client = createPublicClient({ chain: base, transport: http() }) #### B20 -- [B20](https://docs.base.org/specifications/b20/index): Native token standard for issuing programmable assets and stablecoins on Base, with roles, policies, supply controls, and ERC-20 compatibility. - -- [B20 Overview](https://docs.base.org/specifications/b20/specification-overview): Introduction to B20, Base’s native token standard for programmable assets and stablecoins. +- [Overview](https://docs.base.org/specifications/b20): Introduction to B20, Base’s native token standard for programmable assets and stablecoins. - [B20 Execution Architecture](https://docs.base.org/specifications/b20/architecture): How B20 precompiles execute, how B20 tokens are created and recognized, and how protocol versions preserve execution consensus. diff --git a/docs/llms.txt b/docs/llms.txt index 9c78beeb1..a57b722c5 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -226,9 +226,7 @@ #### B20 -- [B20](https://docs.base.org/specifications/b20/index): Native token standard for issuing programmable assets and stablecoins on Base, with roles, policies, supply controls, and ERC-20 compatibility. - -- [B20 Overview](https://docs.base.org/specifications/b20/specification-overview): Introduction to B20, Base’s native token standard for programmable assets and stablecoins. +- [Overview](https://docs.base.org/specifications/b20): Introduction to B20, Base’s native token standard for programmable assets and stablecoins. - [B20 Execution Architecture](https://docs.base.org/specifications/b20/architecture): How B20 precompiles execute, how B20 tokens are created and recognized, and how protocol versions preserve execution consensus. diff --git a/docs/snippets/RwaDisclaimer.mdx b/docs/snippets/RwaDisclaimer.mdx index 39881b753..6b675298a 100644 --- a/docs/snippets/RwaDisclaimer.mdx +++ b/docs/snippets/RwaDisclaimer.mdx @@ -1,5 +1,5 @@ -Real-world asset (RWA) tokenization is one of many use cases for the [B20 Asset standard](/specifications/b20/specification-overview#asset). The examples on this page use a stock token for illustration; the same flows apply to other asset types. +Real-world asset (RWA) tokenization is one of many use cases for the [B20 Asset standard](/specifications/b20/concepts/token-types#4-asset). The examples on this page use a stock token for illustration; the same flows apply to other asset types. Tokenized securities examples shown for illustration. Base is a general-purpose blockchain; issuance and compliance are the responsibility of the issuer under applicable law. diff --git a/docs/specifications/b20/architecture.mdx b/docs/specifications/b20/architecture.mdx index 09ff5fa51..f7719ff81 100644 --- a/docs/specifications/b20/architecture.mdx +++ b/docs/specifications/b20/architecture.mdx @@ -3,7 +3,7 @@ title: "B20 Execution Architecture" description: "How B20 precompiles execute, how B20 tokens are created and recognized, and how protocol versions preserve execution consensus." --- -*How B20 actually executes: how its precompiles differ from ordinary contracts, how a token gets created and recognized as one, and how the protocol evolves without breaking history. For what each primitive means and how to use it (assets, roles, policies), see [B20 concepts](/specifications/b20/concepts/token-types). For the "B20 in 10 minutes" tour, see [B20 Overview](/specifications/b20/specification-overview).* +*How B20 actually executes: how its precompiles differ from ordinary contracts, how a token gets created and recognized as one, and how the protocol evolves without breaking history. For what each primitive means and how to use it (assets, roles, policies), see [B20 concepts](/specifications/b20/concepts/token-types). For the "B20 in 10 minutes" tour, see [B20 Overview](/specifications/b20).* ## 1. How B20 Uses Precompiles diff --git a/docs/specifications/b20/changelog.mdx b/docs/specifications/b20/changelog.mdx index 5ccef23f1..992c4840f 100644 --- a/docs/specifications/b20/changelog.mdx +++ b/docs/specifications/b20/changelog.mdx @@ -3,7 +3,7 @@ title: "Changelog" description: "Per-hardfork, per-feature migration notes for the B20 token standard, newest first, including new methods, deprecations, and activation dates." --- -Per-hardfork, per-feature migration notes for the [B20 token standard](/specifications/b20/specification-overview). Each Cobalt entry is a focused, code-forward changelog for one scoped feature change that crosses a hardfork boundary. Newest first. +Per-hardfork, per-feature migration notes for the [B20 token standard](/specifications/b20). Each Cobalt entry is a focused, code-forward changelog for one scoped feature change that crosses a hardfork boundary. Newest first. B20 method and event signatures are part of the chain's consensus surface. Existing selectors and behavior do not change; the standard grows by addition. @@ -36,19 +36,19 @@ Shipped with the [Beryl upgrade](/upgrades/beryl/overview). **Added** - The B20 standard as a native precompile: a superset of ERC-20 with full selector and behavior parity for `transfer`, `transferFrom`, `approve`, `allowance`, `balanceOf`, `totalSupply`, `name`, `symbol`, `decimals`, `Transfer`, and `Approval` -- [Role-based access control](/specifications/b20/specification-overview#roles-model) with built-in roles, user-defined roles in the role graph, and `renounceLastAdmin` for permanent admin-less operation -- [PolicyRegistry](/specifications/b20/specification-overview#policy-registry) singleton precompile with `BLOCKLIST`, `ALLOWLIST`, `UNION`, and `INTERSECT` policy types, two-step admin transfer, and the `ALWAYS_ALLOW` and `ALWAYS_BLOCK` built-ins -- [Six policy scopes](/specifications/b20/specification-overview#policy-integration) on every token, covering transfer sender, receiver, and executor, mint receiver, and seize holder and receiver -- [`seizeWithMemo`](/specifications/b20/specification-overview#seize) for compliance-driven balance transfers -- [Memo variants](/specifications/b20/specification-overview#memos) of transfer, transferFrom, mint, burn, and seize, emitting `Memo` immediately after the primary event -- [Granular pausing](/specifications/b20/specification-overview#pause) by `PausableFeature`: `TRANSFER`, `MINT`, `BURN`, and `SEIZE` -- [Optional supply caps](/specifications/b20/specification-overview#supply-cap), with `type(uint128).max` as the uncapped sentinel and maximum supply -- [ERC-2612 `permit`](/specifications/b20/specification-overview) with an EIP-712 domain at version `"1"` -- [ERC-7572 `contractURI`](/specifications/b20/specification-overview#contract-uri-erc-7572) and `METADATA_ROLE`-gated name, symbol, and URI updates -- [B20Factory](/specifications/b20/specification-overview#factory) singleton precompile with deterministic, variant-encoding addresses and `initCalls` bootstrap semantics +- [Role-based access control](/specifications/b20/concepts/roles-and-pause#2-roles) with built-in roles, user-defined roles in the role graph, and `renounceLastAdmin` for permanent admin-less operation +- [PolicyRegistry](/specifications/b20/concepts/policies#2-how-policies-work) singleton precompile with `BLOCKLIST`, `ALLOWLIST`, `UNION`, and `INTERSECT` policy types, two-step admin transfer, and the `ALWAYS_ALLOW` and `ALWAYS_BLOCK` built-ins +- [Six policy scopes](/specifications/b20/concepts/policies#32-policy-scopes) on every token, covering transfer sender, receiver, and executor, mint receiver, and seize holder and receiver +- [`seizeWithMemo`](/specifications/b20/reference/interfaces/ib20/seize-with-memo) for compliance-driven balance transfers +- [Memo variants](/specifications/b20/reference/interfaces/ib20) of transfer, transferFrom, mint, burn, and seize, emitting `Memo` immediately after the primary event +- [Granular pausing](/specifications/b20#pause-vectors) by `PausableFeature`: `TRANSFER`, `MINT`, `BURN`, and `SEIZE` +- [Optional supply caps](/specifications/b20/reference/interfaces/ib20/supply-cap), with `type(uint128).max` as the uncapped sentinel and maximum supply +- [ERC-2612 `permit`](/specifications/b20) with an EIP-712 domain at version "1" +- [ERC-7572 `contractURI`](/specifications/b20/reference/interfaces/ib20/contract-uri) and `METADATA_ROLE`-gated name, symbol, and URI updates +- [B20Factory](/specifications/b20#creating-a-b20-asset) singleton precompile with deterministic, variant-encoding addresses and `initCalls` bootstrap semantics - ActivationRegistry gating for state-changing PolicyRegistry calls -- The [`ASSET` variant](/specifications/b20/specification-overview#asset): `OPERATOR_ROLE`, WAD-precision UI multipliers with scheduled and instant updates, announcements, `batchMint`, and issuer-defined extra metadata -- The [`STABLECOIN` variant](/specifications/b20/specification-overview#stablecoin): fixed 6 decimals and a `currency()` code set once at creation +- The [`ASSET` variant](/specifications/b20/concepts/token-types#4-asset): `OPERATOR_ROLE`, WAD-precision UI multipliers with scheduled and instant updates, announcements, `batchMint`, and issuer-defined extra metadata +- The [`STABLECOIN` variant](/specifications/b20/concepts/token-types#5-stablecoin): fixed 6 decimals and a `currency()` code set once at creation **Deprecated** diff --git a/docs/specifications/b20/index.mdx b/docs/specifications/b20/index.mdx index 4f323a599..1a10beb8f 100644 --- a/docs/specifications/b20/index.mdx +++ b/docs/specifications/b20/index.mdx @@ -1,73 +1,191 @@ --- -title: "B20" -sidebarTitle: "Overview" -description: "Native token standard for issuing programmable assets and stablecoins on Base, with roles, policies, supply controls, and ERC-20 compatibility." +title: "Overview" +description: "Introduction to B20, Base’s native token standard for programmable assets and stablecoins." --- -B20 is Base's native token standard for issuing and managing programmable assets onchain. It is an ERC-20 superset with shared protocol logic for roles, policies, supply controls, and variant-specific Asset and Stablecoin capabilities. - -## Learn B20 - - - - Start with B20's purpose, core primitives, and token-creation flow. - - - Learn how B20 precompiles execute, route, and evolve across hardforks. - - - -## Concepts - - - - Compare the Asset and Stablecoin variants and their capabilities. - - - Configure allowlists, blocklists, composite policies, and policy scopes. - - - Assign operational permissions and pause individual token features. - - - Understand Asset multiplier behavior and ERC-8056 UI balances. - - - -## Build on Base - -Use these Build on Base demos to create and operate B20 tokens. - - - - Create a B20 Asset token with deterministic factory deployment. - - - Create a B20 Stablecoin with an immutable currency code. - - - Apply a policy that limits asset ownership to eligible accounts. - - - Schedule or apply an Asset UI multiplier for a corporate action. - - - -## Reference - -For technical details, see the [B20 interfaces](/specifications/b20/reference/interfaces) and [B20 constants](/specifications/b20/reference/constants). - - - - Browse B20 interfaces, canonical Solidity sources, and copy-paste imports. - - - Find precompile addresses, role identifiers, policy scopes, and validation bounds. - - - Review custom errors, selectors, and the conditions that trigger them. - - - Review B20 events and when each interface emits them. - - +## 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} +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} +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} +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} +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} +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. + +--- + +## Where to Go Next + +If you want to understand how B20 works internally: + +→ [B20 Architecture](/specifications/b20/architecture) + +If you are integrating B20: + +→ [B20 interfaces](/specifications/b20/reference/interfaces) +→ [B20 concepts](/specifications/b20/concepts/token-types) + +For exact interfaces and protocol definitions: + +→ [B20 reference](/specifications/b20/reference/interfaces) +→ [B20 architecture](/specifications/b20/architecture) diff --git a/docs/specifications/b20/specification-overview.mdx b/docs/specifications/b20/specification-overview.mdx deleted file mode 100644 index 21c6aedea..000000000 --- a/docs/specifications/b20/specification-overview.mdx +++ /dev/null @@ -1,199 +0,0 @@ ---- -title: "B20 Overview" -description: "Introduction to B20, Base’s native token standard for programmable assets and stablecoins." ---- - -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. - -For a deeper technical explanation, see [How B20 Works](/specifications/b20/architecture). - ---- - -## 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} -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} -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} -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} -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} -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. - ---- - -## Where to Go Next - -If you want to understand how B20 works internally: - -→ [B20 Architecture](/specifications/b20/architecture) - -If you are integrating B20: - -→ [B20 interfaces](/specifications/b20/reference/interfaces) -→ [B20 concepts](/specifications/b20/concepts/token-types) - -For exact interfaces and protocol definitions: - -→ [B20 reference](/specifications/b20/reference/interfaces) -→ [B20 architecture](/specifications/b20/architecture) diff --git a/docs/specifications/overview.mdx b/docs/specifications/overview.mdx index 1b4595b9c..10e9f5433 100644 --- a/docs/specifications/overview.mdx +++ b/docs/specifications/overview.mdx @@ -9,7 +9,7 @@ Technical specifications for how Base works at the chain level, organized by top Design goals, rollup architecture, and core user flows. - + Native token standard with compliance, memos, and supply controls. diff --git a/scripts/sync-from-base-std/__tests__/base-std-routing.test.mjs b/scripts/sync-from-base-std/__tests__/base-std-routing.test.mjs index af7615ae8..61e5e766c 100644 --- a/scripts/sync-from-base-std/__tests__/base-std-routing.test.mjs +++ b/scripts/sync-from-base-std/__tests__/base-std-routing.test.mjs @@ -25,7 +25,7 @@ const REPO_ROOT = path.resolve( ); const B20_REFERENCE_ROOT = "docs/specifications/b20"; const B20_MANUAL_UPDATE_PAGES = [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/build-on-base/issue-rwa/create-an-asset-token.mdx", "docs/build-on-base/accept-payments/request-a-payment.mdx", ]; @@ -199,7 +199,7 @@ test("route rules carry a kind, and changelog index vs entry are routed differen ["src/interfaces/IB20.sol", "changelog/README.md"], { repoRoot: REPO_ROOT }, ); - const overview = mixed.find((w) => w.page === `${B20_REFERENCE_ROOT}/specification-overview.mdx`); + const overview = mixed.find((w) => w.page === `${B20_REFERENCE_ROOT}/index.mdx`); assert.deepEqual(overview.kinds, ["interface"]); assert.deepEqual(mixed.find((w) => w.page === summary).kinds, ["changelog-index"]); }); @@ -333,7 +333,7 @@ test("upstream docs tree routes to the pages the IA guidelines assign", async () const arch = await pagesFor("docs/architecture.md"); for (const expected of [ "docs/specifications/base-protocol/execution/precompiles.mdx", - `${B20_REFERENCE_ROOT}/specification-overview.mdx`, + `${B20_REFERENCE_ROOT}/index.mdx`, `${B20_REFERENCE_ROOT}/reference/interfaces.mdx`, ]) { assert.ok(arch.includes(expected), `docs/architecture.md should route to ${expected}`); @@ -342,7 +342,7 @@ test("upstream docs tree routes to the pages the IA guidelines assign", async () // Concepts feed the spec overview key-concept sections plus the owning reference pages. const multipliers = await pagesFor("docs/concepts/multipliers.md"); - assert.ok(multipliers.includes(`${B20_REFERENCE_ROOT}/specification-overview.mdx`)); + assert.ok(multipliers.includes(`${B20_REFERENCE_ROOT}/index.mdx`)); assert.ok(multipliers.includes(`${B20_REFERENCE_ROOT}/reference/interfaces.mdx`)); assert.ok(multipliers.includes("docs/build-on-base/issue-rwa/apply-a-multiplier.mdx")); @@ -369,7 +369,7 @@ test("upstream docs tree routes to the pages the IA guidelines assign", async () test("removed source files never route, even when a rule still matches them", async () => { const routeTable = { code_changes: [ - { source_prefix: "docs/B20/Asset.md", kind: "product-doc", pages: [`${B20_REFERENCE_ROOT}/specification-overview.mdx`], transformer: "claude" }, + { source_prefix: "docs/B20/Asset.md", kind: "product-doc", pages: [`${B20_REFERENCE_ROOT}/index.mdx`], transformer: "claude" }, { source_prefix: "src/interfaces/IB20Asset.sol", kind: "interface", pages: [`${B20_REFERENCE_ROOT}/reference/interfaces.mdx`], transformer: "claude" }, ], }; @@ -399,7 +399,7 @@ test("classifyChangedPaths separates routed, ignored, unrouted, and removed for test("filterPlacementProposals keeps only real sources and existing candidate pages", () => { const sources = ["docs/concepts/brand-new-topic.md"]; - const candidates = [`${B20_REFERENCE_ROOT}/specification-overview.mdx`]; + const candidates = [`${B20_REFERENCE_ROOT}/index.mdx`]; const kept = filterPlacementProposals( [ { source: "docs/concepts/brand-new-topic.md", page: candidates[0], guideline_rule: "Specifications → B20 | key concepts", rationale: "Concept page | fits the overview\nx" }, @@ -420,7 +420,7 @@ test("filterPlacementProposals keeps only real sources and existing candidate pa test("routingReportRows renders unrouted, proposal, and removed sections", () => { const rows = routingReportRows({ classification: { unrouted: ["docs/concepts/new.md"], removed: ["docs/B20/Asset.md"], ignored: [] }, - proposals: [{ source: "docs/concepts/new.md", page: "docs/specifications/b20/specification-overview.mdx", guideline_rule: "Key concepts", rationale: "Concept material" }], + proposals: [{ source: "docs/concepts/new.md", page: "docs/specifications/b20/index.mdx", guideline_rule: "Key concepts", rationale: "Concept material" }], source: "base/base-std", sha: "be6d0450890e20fc4a739aeaff5e839f234d12a6", }); @@ -428,7 +428,7 @@ test("routingReportRows renders unrouted, proposal, and removed sections", () => assert.match(md, /## Unrouted source files/); assert.match(md, /https:\/\/github\.com\/base\/base-std\/blob\/be6d0450890e20fc4a739aeaff5e839f234d12a6\/docs\/concepts\/new\.md/); assert.match(md, /### Proposed placement \(from IA guidelines\)/); - assert.match(md, /\| .*docs\/concepts\/new\.md.* \| `docs\/specifications\/b20\/specification-overview\.mdx` \| Key concepts \| Concept material \|/); + assert.match(md, /\| .*docs\/concepts\/new\.md.* \| `docs\/specifications\/b20\/index\.mdx` \| Key concepts \| Concept material \|/); assert.match(md, /## Removed source files/); assert.match(md, /`docs\/B20\/Asset\.md`/); assert.deepEqual(routingReportRows({ classification: { unrouted: [], removed: [], ignored: ["README.md"] }, source: "x", sha: "y" }), []); diff --git a/scripts/sync-from-base-std/route-table.json b/scripts/sync-from-base-std/route-table.json index 2ad3f22d3..23dbd80ba 100644 --- a/scripts/sync-from-base-std/route-table.json +++ b/scripts/sync-from-base-std/route-table.json @@ -5,7 +5,7 @@ "source_prefix": "src/interfaces/IActivationRegistry.sol", "kind": "interface", "pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/specifications/b20/reference/interfaces.mdx", "docs/specifications/b20/reference/errors.mdx", "docs/specifications/b20/reference/events.mdx" @@ -16,7 +16,7 @@ "source_prefix": "src/interfaces/IB20.sol", "kind": "interface", "pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/specifications/b20/reference/interfaces.mdx", "docs/specifications/b20/reference/errors.mdx", "docs/specifications/b20/reference/events.mdx", @@ -29,7 +29,7 @@ "source_prefix": "src/interfaces/IB20Asset.sol", "kind": "interface", "pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/specifications/b20/reference/interfaces.mdx", "docs/specifications/b20/reference/errors.mdx", "docs/specifications/b20/reference/events.mdx" @@ -48,7 +48,7 @@ "source_prefix": "src/interfaces/IB20Factory.sol", "kind": "interface", "pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/specifications/b20/reference/interfaces.mdx", "docs/build-on-base/issue-rwa/create-an-asset-token.mdx" ], @@ -58,7 +58,7 @@ "source_prefix": "src/interfaces/IB20Stablecoin.sol", "kind": "interface", "pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/specifications/b20/reference/interfaces.mdx" ], "transformer": "claude" @@ -67,7 +67,7 @@ "source_prefix": "src/interfaces/IPolicyRegistry.sol", "kind": "interface", "pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/specifications/b20/reference/interfaces.mdx", "docs/specifications/b20/reference/errors.mdx", "docs/specifications/b20/reference/events.mdx" @@ -78,7 +78,7 @@ "source_prefix": "src/StdPrecompiles.sol", "kind": "interface", "pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/specifications/b20/reference/constants.mdx" ], "transformer": "claude" @@ -87,7 +87,7 @@ "source_prefix": "src/lib/B20Constants.sol", "kind": "interface", "pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/specifications/b20/reference/constants.mdx", "docs/specifications/b20/reference/interfaces.mdx" ], @@ -106,7 +106,7 @@ "source_prefix": "test/lib/mocks/MockB20Asset.sol", "kind": "interface", "pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/specifications/b20/reference/interfaces.mdx", "docs/specifications/b20/reference/errors.mdx", "docs/specifications/b20/reference/events.mdx" @@ -134,7 +134,7 @@ "source_prefix": "test/lib/mocks/MockB20.sol", "kind": "interface", "pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/specifications/b20/reference/interfaces.mdx", "docs/specifications/b20/reference/errors.mdx", "docs/specifications/b20/reference/events.mdx" @@ -145,7 +145,7 @@ "source_prefix": "test/lib/mocks/MockPolicyRegistry.sol", "kind": "interface", "pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/specifications/b20/reference/interfaces.mdx" ], "transformer": "claude" @@ -164,7 +164,7 @@ "kind": "product-doc", "pages": [ "docs/build-on-base/issue-rwa/create-an-asset-token.mdx", - "docs/specifications/b20/specification-overview.mdx" + "docs/specifications/b20/index.mdx" ], "transformer": "claude" }, @@ -173,7 +173,7 @@ "kind": "product-doc", "pages": [ "docs/specifications/base-protocol/execution/precompiles.mdx", - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/specifications/b20/reference/interfaces.mdx" ], "transformer": "claude" @@ -182,7 +182,7 @@ "source_prefix": "docs/concepts/multipliers.md", "kind": "product-doc", "pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/build-on-base/issue-rwa/apply-a-multiplier.mdx", "docs/specifications/b20/reference/interfaces.mdx" ], @@ -192,7 +192,7 @@ "source_prefix": "docs/concepts/policies.md", "kind": "product-doc", "pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/specifications/b20/reference/constants.mdx", "docs/specifications/b20/reference/interfaces.mdx", "docs/build-on-base/issue-rwa/restrict-eligible-holders.mdx", @@ -204,7 +204,7 @@ "source_prefix": "docs/concepts/roles-and-pause.md", "kind": "product-doc", "pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/specifications/b20/reference/constants.mdx", "docs/specifications/b20/reference/interfaces.mdx", "docs/build-on-base/issue-rwa/pause-transfers.mdx", @@ -216,7 +216,7 @@ "source_prefix": "docs/concepts/token-types.md", "kind": "product-doc", "pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/specifications/b20/reference/interfaces.mdx", "docs/build-on-base/issue-rwa/create-an-asset-token.mdx", "docs/build-on-base/issue-stablecoins/issue-your-stablecoin.mdx" @@ -344,7 +344,7 @@ }, "manual_update": { "allowed_pages": [ - "docs/specifications/b20/specification-overview.mdx", + "docs/specifications/b20/index.mdx", "docs/build-on-base/issue-rwa/create-an-asset-token.mdx", "docs/build-on-base/accept-payments/request-a-payment.mdx" ],