diff --git a/docs/agents.md b/docs/agents.md
index 819611e3c..b7fd5f12f 100644
--- a/docs/agents.md
+++ b/docs/agents.md
@@ -49,7 +49,7 @@ npx skills add base/base-skills
|apps:index
|apps/builder-codes:agent-developers,app-developers,builder-codes,wallet-developers
|apps/growth:rewards
-|apps/guides:migrate-to-standard-web-app
+|apps/guides:accept-b20-payments,migrate-to-standard-web-app
|apps/quickstart:build-app
|apps/resources:design-resources,templates
|apps/technical-guides:base-notifications
@@ -101,6 +101,6 @@ npx skills add base/base-skills
|base-chain/specs/upgrades/holocene:derivation,exec-engine,overview,system-config
|base-chain/specs/upgrades/isthmus:derivation,exec-engine,l1-attributes,overview,predeploys,system-config
|base-chain/specs/upgrades/jovian:derivation,exec-engine,l1-attributes,overview,system-config
-|get-started:base-mentorship-program,base-services-hub,base,block-explorers,concepts,country-leads-and-ambassadors,data-indexers,deploy-smart-contracts,docs-llms,docs-mcp,get-funded,launch-token,learning-resources,prompt-library,resources-for-ai-agents
+|get-started:base-mentorship-program,base-services-hub,base,block-explorers,concepts,country-leads-and-ambassadors,data-indexers,deploy-smart-contracts,docs-llms,docs-mcp,get-funded,launch-b20-token,launch-token,learning-resources,prompt-library,resources-for-ai-agents
|ledgers:deposit-flow,how-it-works,overview,withdraw-flow
|root:agents,changes,cookie-policy,privacy-policy,terms-of-service,tone_of_voice
diff --git a/docs/apps/guides/accept-b20-payments.mdx b/docs/apps/guides/accept-b20-payments.mdx
new file mode 100644
index 000000000..d6dae4e18
--- /dev/null
+++ b/docs/apps/guides/accept-b20-payments.mdx
@@ -0,0 +1,60 @@
+---
+title: "Accept B20 payments"
+description: "Accept B20 token payments in your app and match each transaction to an order with onchain memos."
+---
+
+B20 is an ERC-20 superset. Standard `transfer`, `transferFrom`, `approve`, `balanceOf`, and ERC-2612 `permit` all work, so an app that accepts ERC-20 tokens accepts B20 with no code changes.
+
+B20's new features include transfer policies, pausing, supply caps, and memos. This guide uses the memo: `transferWithMemo` works like `transfer`, but also attaches a `bytes32` reference such as an order ID and emits a `Memo` event right after the standard `Transfer`. Read that event to tie each payment to an order.
+
+## Tag a payment with a memo
+
+This uses your configured viem `walletClient` and `publicClient`. It reads the token's decimals, sends a payment tagged with an order ID, then reads the memo back from the receipt:
+
+```js
+import { parseUnits, stringToHex, hexToString, parseEventLogs } from 'viem';
+
+const TOKEN = '0xB20f...'; // the B20 token you accept
+const MERCHANT = '0x...'; // where payments land
+
+const ABI = [
+ { type: 'function', name: 'decimals', stateMutability: 'view', inputs: [], outputs: [{ type: 'uint8' }] },
+ { type: 'function', name: 'transferWithMemo', stateMutability: 'nonpayable',
+ inputs: [{ name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }, { name: 'memo', type: 'bytes32' }],
+ outputs: [{ type: 'bool' }] },
+ { type: 'event', name: 'Memo', inputs: [
+ { name: 'caller', type: 'address', indexed: true },
+ { name: 'memo', type: 'bytes32', indexed: true },
+ ] },
+];
+
+// Read decimals because B20 tokens range from 6 to 18.
+const decimals = await publicClient.readContract({ address: TOKEN, abi: ABI, functionName: 'decimals' });
+
+// Pay 10 tokens, tagging the transfer with an order ID.
+const hash = await walletClient.writeContract({
+ address: TOKEN, abi: ABI, functionName: 'transferWithMemo',
+ args: [MERCHANT, parseUnits('10', decimals), stringToHex('order-42', { size: 32 })],
+});
+
+// The Memo event carries the order ID back. Read it from the receipt.
+const receipt = await publicClient.waitForTransactionReceipt({ hash });
+const [memo] = parseEventLogs({ abi: ABI, logs: receipt.logs, eventName: 'Memo' });
+console.log(hexToString(memo.args.memo, { size: 32 }).replace(/\0+$/, '')); // "order-42"
+```
+
+To collect with an allowance instead of a direct transfer, use `transferFromWithMemo`. It emits the same `Memo` event.
+
+## Handle B20-specific reverts
+
+A B20 transfer can revert where a standard ERC-20 would not. Surface these so a failed payment is visible, not silent:
+
+- `PolicyForbids`: the sender or recipient is not authorized by the token's transfer policy. Most tokens are open by default, but a regulated issuer can gate transfers with an allowlist or blocklist.
+- A paused transfer: the issuer paused the token's `TRANSFER` feature.
+
+Call `publicClient.simulateContract` with the same arguments before sending. It raises these as typed errors before the user signs, so you can show the reason instead of a failed transaction.
+
+## Related pages
+
+- [B20 token standard](/base-chain/specs/upgrades/beryl/b20): the full interface, including memos, policies, pausing, and roles.
+- [Launch a B20 Token](/get-started/launch-b20-token): create your own B20 token.
\ No newline at end of file
diff --git a/docs/docs.json b/docs/docs.json
index bc19b3cf8..582fd72da 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -40,6 +40,7 @@
"get-started/resources-for-ai-agents",
"get-started/build-app",
"get-started/launch-token",
+ "get-started/launch-b20-token",
"get-started/deploy-smart-contracts",
"get-started/learning-resources"
]
@@ -665,7 +666,8 @@
"group": "Guides",
"pages": [
"apps/technical-guides/base-notifications",
- "apps/guides/migrate-to-standard-web-app"
+ "apps/guides/migrate-to-standard-web-app",
+ "apps/guides/accept-b20-payments"
]
},
{
diff --git a/docs/get-started/base.mdx b/docs/get-started/base.mdx
index d672db9a9..95c53bf98 100644
--- a/docs/get-started/base.mdx
+++ b/docs/get-started/base.mdx
@@ -20,8 +20,8 @@ mode: "wide"
### Tokens
[Launch a Token](/get-started/launch-token)
+ [Launch a B20 Token](/get-started/launch-b20-token)
[Bridge from Solana](/base-chain/quickstart/base-solana-bridge)
- [Bridge from Ethereum](/base-chain/network-information/bridges)
diff --git a/docs/get-started/launch-b20-token.mdx b/docs/get-started/launch-b20-token.mdx
new file mode 100644
index 000000000..2de35e341
--- /dev/null
+++ b/docs/get-started/launch-b20-token.mdx
@@ -0,0 +1,286 @@
+---
+title: "Launch a B20 Token"
+description: "Launch a B20 token on Base by calling the B20 Factory precompile."
+---
+
+B20 is an ERC-20 superset that runs as a native precompile on Base, which makes transfers cheaper and higher-throughput than a standard contract token while keeping full ERC-20 compatibility. Roles, supply caps, pausing, policy gating, memos, and `permit` are built into the chain.
+
+A standard ERC-20 leaves that logic for you to build, audit, and maintain. With B20, you call the singleton **B20 Factory** to create a token, fully configured, in a single transaction.
+
+This guide creates an Asset token, mints its initial supply, and verifies the balance onchain. To accept the token as payment in an app, continue with [Accept B20 payments](/apps/guides/accept-b20-payments).
+
+## Before you begin
+
+You need **Base's Foundry build** (`base-forge`, `base-cast`, `base-anvil`). Install it via `base-foundryup`:
+
+ ```bash Terminal theme={null}
+ curl -L https://raw.githubusercontent.com/base/base-anvil/HEAD/foundryup/install | bash
+ base-foundryup --install v1.1.0
+ ```
+
+
+ Standard `forge` cannot simulate calls to B20 precompile addresses (they hold no contract bytecode) and aborts with `call to non-contract address`. Base's `base-forge` registers the precompiles into its EVM. It installs alongside your existing Foundry toolchain without overwriting it — use `base-forge`, `base-cast`, and `base-anvil` for all commands in this guide.
+
+
+## Set up your project
+
+```bash Terminal theme={null}
+base-forge init b20-quickstart && cd b20-quickstart
+base-forge install base/base-std --no-git
+```
+
+Add the remappings and the `base = true` flag to `foundry.toml` (under `[profile.default]`). `base = true` tells Base's `forge` build to run the B20 precompiles inside its EVM, so the deploy script's local simulation can call the factory:
+
+```toml foundry.toml theme={null}
+base = true
+remappings = [
+ "base-std/=lib/base-std/src/",
+ "base-std-test/=lib/base-std/test/",
+]
+```
+
+
+The interfaces compile with any Solidity `>=0.8.20 <0.9.0`.
+
+
+## Choose a network
+
+Pick a network with the B20 precompiles active, then create a `.env` **inside your `b20-quickstart` project directory**:
+
+
+
+ | Setting | Value |
+ |---|---|
+ | RPC URL | `https://sepolia.base.org` |
+ | Chain ID | `84532` |
+ | Faucet | [CDP Faucet](https://portal.cdp.coinbase.com/products/faucet) or [other providers](/base-chain/network-information/network-faucets) |
+ | Explorer | [sepolia.basescan.org](https://sepolia.basescan.org) |
+
+ ```bash .env theme={null}
+ export RPC_URL="https://sepolia.base.org"
+ export PRIVATE_KEY="0x..."
+ export ACCOUNT_ADDRESS="0x..."
+ export CHAIN_ID="84532"
+ ```
+
+
+ If you don't have an account, `base-cast wallet new` prints a fresh address and key.
+
+
+ Request testnet ETH from the faucet, then confirm it arrived:
+
+ ```bash Terminal theme={null}
+ source .env
+ base-cast balance $ACCOUNT_ADDRESS --rpc-url $RPC_URL
+ ```
+
+
+ The command prints a non-zero balance. This account signs the deploy and the mint, and receives the minted supply.
+
+
+
+ | Setting | Value |
+ |---|---|
+ | RPC URL | `https://rpc.vibes.base.org/` |
+ | Chain ID | `84538453` |
+ | Faucet | [faucet.vibes.base.org](https://faucet.vibes.base.org/) |
+ | Explorer | [explorer.vibes.base.org](https://explorer.vibes.base.org/) |
+
+ ```bash .env theme={null}
+ export RPC_URL="https://rpc.vibes.base.org/"
+ export PRIVATE_KEY="0x..."
+ export ACCOUNT_ADDRESS="0x..."
+ export CHAIN_ID="84538453"
+ ```
+
+
+ If you don't have an account, `base-cast wallet new` prints a fresh address and key.
+
+
+ Request testnet ETH from the faucet, then confirm it arrived:
+
+ ```bash Terminal theme={null}
+ source .env
+ base-cast balance $ACCOUNT_ADDRESS --rpc-url $RPC_URL
+ ```
+
+
+ The command prints a non-zero balance. This account signs the deploy and the mint, and receives the minted supply.
+
+
+
+ Start a local Base node in a separate terminal:
+
+ ```bash Terminal theme={null}
+ base-anvil
+ ```
+
+ Create a `.env` using anvil's pre-funded account #0:
+
+ ```bash .env theme={null}
+ export RPC_URL="http://127.0.0.1:8545"
+ export PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
+ export ACCOUNT_ADDRESS="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
+ export CHAIN_ID="31337"
+ ```
+
+ Confirm the node is running:
+
+ ```bash Terminal theme={null}
+ source .env
+ base-cast balance $ACCOUNT_ADDRESS --rpc-url $RPC_URL
+ ```
+
+
+ The command prints `10000000000000000000000` — anvil's default pre-funded balance of 10,000 ETH.
+
+
+
+
+## Create your token
+
+The factory's single entry point is `createB20(variant, salt, params, initCalls)`:
+
+* `variant`: `ASSET` or `STABLECOIN`. This guide uses `ASSET`.
+* `salt`: caller-chosen entropy that fixes the deterministic token address.
+* `params`: ABI-encoded name, symbol, initial admin, and decimals.
+* `initCalls`: optional batch of config calls applied at creation.
+
+
+
+ Use `B20FactoryLib` to encode `params` and `initCalls`. Create `script/CreateToken.s.sol`:
+
+ ```solidity script/CreateToken.s.sol highlight={26} theme={null}
+ // SPDX-License-Identifier: MIT
+ pragma solidity ^0.8.20;
+
+ import {Script, console} from "forge-std/Script.sol";
+
+ import {B20Constants} from "base-std/lib/B20Constants.sol";
+ import {B20FactoryLib} from "base-std/lib/B20FactoryLib.sol";
+ import {IB20Factory} from "base-std/interfaces/IB20Factory.sol";
+ import {StdPrecompiles} from "base-std/StdPrecompiles.sol";
+
+ contract CreateToken is Script {
+ function run() external returns (address token) {
+ // For the quickstart, one account is admin + minter.
+ address account = vm.envAddress("ACCOUNT_ADDRESS");
+ bytes32 salt = keccak256("my-first-b20");
+
+ // Name, symbol, initial DEFAULT_ADMIN_ROLE holder, decimals (6-18).
+ bytes memory params = B20FactoryLib.encodeAssetCreateParams("My Token", "MYT", account, 18);
+
+ // Configuration applied atomically at creation.
+ bytes[] memory initCalls = new bytes[](2);
+ initCalls[0] = B20FactoryLib.encodeGrantRole(B20Constants.MINT_ROLE, account);
+ initCalls[1] = B20FactoryLib.encodeUpdateSupplyCap(1_000_000e18);
+
+ vm.startBroadcast();
+ token = StdPrecompiles.B20_FACTORY.createB20(IB20Factory.B20Variant.ASSET, salt, params, initCalls);
+ vm.stopBroadcast();
+
+ console.log("B20 token created at:", token);
+ }
+ }
+ ```
+
+
+ **Encode with `B20FactoryLib`.** The native implementation rejects non-canonical calldata with `AbiDecodeFailed`; the helpers produce canonical encoding.
+
+
+
+ Asset decimals are fixed at creation and must be in `[6, 18]`. The supply cap is optional; the no-cap sentinel is `type(uint128).max` (the cap can never exceed `uint128.max`).
+
+
+
+ Use the `STABLECOIN` variant and its params encoder. A stablecoin fixes decimals at 6 and carries an immutable ISO currency code (uppercase `A`–`Z`) instead of a configurable decimals value:
+
+ ```solidity theme={null}
+ bytes memory params = B20FactoryLib.encodeStablecoinCreateParams("My USD", "MUSD", account, "USD");
+
+ token = StdPrecompiles.B20_FACTORY.createB20(IB20Factory.B20Variant.STABLECOIN, salt, params, initCalls);
+ ```
+
+ Everything else in this guide — roles, supply cap, minting, and verification — works identically.
+
+
+
+
+ ```bash Terminal theme={null}
+ source .env
+ base-forge script script/CreateToken.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast
+ ```
+
+ On success the script logs the new token's address. The factory's address starts `0xB20f...`. The tokens it creates start `0xB200...`:
+
+
+ If you see `TokenAlreadyExists`, the salt `keccak256("my-first-b20")` is already registered on this network or anvil instance. Either restart `base-anvil` for a fresh state, or change the salt in the script to a unique value.
+
+
+ ```text Output theme={null}
+ == Logs ==
+ B20 token created at: 0xB200...
+ ```
+
+
+
+ Save the address to an environment variable so the next step needs no copy-paste. The broadcast artifact holds the return value:
+
+ ```bash Terminal theme={null}
+ TOKEN_ADDRESS=$(jq -er '.returns.token.value' \
+ broadcast/CreateToken.s.sol/$CHAIN_ID/run-latest.json) \
+ && echo "export TOKEN_ADDRESS=$TOKEN_ADDRESS" >> .env \
+ && source .env \
+ && echo "TOKEN_ADDRESS=$TOKEN_ADDRESS"
+ ```
+
+ Appending to `.env` keeps `TOKEN_ADDRESS` available in later steps, even in a new terminal session.
+
+
+ The broadcast path includes the chain ID, which the `CHAIN_ID` value in your `.env` supplies: `84532` for Sepolia, `84538453` for Vibenet, `31337` for local base-anvil.
+
+
+
+
+## Mint and verify
+
+Minting requires `MINT_ROLE`, which `initCalls` granted to your account.
+
+
+
+ ```bash Terminal theme={null}
+ base-cast send $TOKEN_ADDRESS "mint(address,uint256)" $ACCOUNT_ADDRESS 1000000000000000000000 \
+ --rpc-url $RPC_URL --private-key $PRIVATE_KEY
+ ```
+
+ `base-cast send` prints a receipt with `status 1 (success)`.
+
+
+
+ ```bash Terminal theme={null}
+ base-cast call $TOKEN_ADDRESS "balanceOf(address)(uint256)" $ACCOUNT_ADDRESS --rpc-url $RPC_URL
+ # 1000000000000000000000 [1e21]
+ ```
+
+
+ The token now holds minted supply onchain. Search `$TOKEN_ADDRESS` in the explorer to view it.
+
+
+
+
+## What you built
+
+In this guide you:
+
+* Created a B20 Asset token with one `createB20` call
+* Configured its admin, minter, and supply cap atomically via `initCalls`
+* Minted supply
+* Verified the balance onchain
+
+You did all of this without writing, deploying, or auditing a token contract.
+
+## Next steps
+
+* [Accept B20 payments in an app](/apps/guides/accept-b20-payments): wire this token into a checkout flow that tags each payment with an order ID and reconciles it from onchain events.
+* Gate transfers or mints with PolicyRegistry policies, add granular pause, or manage roles. See the [B20 token standard](/base-chain/specs/upgrades/beryl/b20).
+* Issue a stablecoin variant (fixed 6 decimals, immutable currency code).
\ No newline at end of file
diff --git a/docs/llms-full.txt b/docs/llms-full.txt
index 7258a832b..80e300d21 100644
--- a/docs/llms-full.txt
+++ b/docs/llms-full.txt
@@ -102,6 +102,7 @@ const client = createPublicClient({ chain: base, transport: http() })
- [Static Docs Files](https://docs.base.org/get-started/docs-llms): Use llms.txt and llms-full.txt to give AI assistants access to Base documentation.
- [MCP Server](https://docs.base.org/get-started/docs-mcp): Connect your AI coding assistant to Base documentation using Model Context Protocol for real-time access.
- [Get Funded](https://docs.base.org/get-started/get-funded): The Base ecosystem offers multiple funding pathways designed specifically for builders at every stage—from weekend experiments to full-scale ventures.
+- [Launch a B20 Token](https://docs.base.org/get-started/launch-b20-token): Launch a B20 token on Base by calling the B20 Factory precompile.
- [Launch a Token](https://docs.base.org/get-started/launch-token)
- [Learning Resources](https://docs.base.org/get-started/learning-resources): Find educational content for learning Solidity, Ethereum, and blockchain development
- [Developer's Guide to Effective AI Prompting](https://docs.base.org/get-started/prompt-library): Learn practical AI prompting techniques to enhance your coding workflow and get better results from AI coding assistants.
@@ -403,6 +404,7 @@ const client = createPublicClient({ chain: base, transport: http() })
- [Base Builder Codes](https://docs.base.org/apps/builder-codes/builder-codes): Attribute onchain activity to your app, wallet or agent with Builder Codes.
- [Builder Codes for Wallet Developers](https://docs.base.org/apps/builder-codes/wallet-developers): Implement the dataSuffix capability in your wallet to enable Builder Code attribution.
- [Rewards](https://docs.base.org/apps/growth/rewards): Earn financial incentives for building high-quality Apps that drive user engagement and onchain transactions
+- [Accept B20 payments](https://docs.base.org/apps/guides/accept-b20-payments): Accept B20 token payments in your app and match each transaction to an order with onchain memos.
- [Migrate to a Standard Web App](https://docs.base.org/apps/guides/migrate-to-standard-web-app): Migrate your Farcaster mini-app to work in the Base App. Covers replacing deprecated SDK methods, and registering on Base.dev.
- [Build an app on Base](https://docs.base.org/apps/quickstart/build-app): A step-by-step guide to building a Next.js tally app on Base using wagmi and viem, with wallet connection, contract reads and writes, and batch transaction support.
- [Add notifications](https://docs.base.org/apps/technical-guides/base-notifications): Send in-app notifications to your app's users through the Base Dashboard REST API.
diff --git a/docs/llms.txt b/docs/llms.txt
index 175ca7613..ad93af27d 100644
--- a/docs/llms.txt
+++ b/docs/llms.txt
@@ -14,6 +14,7 @@
- [Static Docs Files](https://docs.base.org/get-started/docs-llms): Use llms.txt and llms-full.txt to give AI assistants access to Base documentation.
- [MCP Server](https://docs.base.org/get-started/docs-mcp): Connect your AI coding assistant to Base documentation using Model Context Protocol for real-time access.
- [Get Funded](https://docs.base.org/get-started/get-funded): The Base ecosystem offers multiple funding pathways designed specifically for builders at every stage—from weekend experiments to full-scale ventures.
+- [Launch a B20 Token](https://docs.base.org/get-started/launch-b20-token): Launch a B20 token on Base by calling the B20 Factory precompile.
- [Launch a Token](https://docs.base.org/get-started/launch-token)
- [Learning Resources](https://docs.base.org/get-started/learning-resources): Find educational content for learning Solidity, Ethereum, and blockchain development
- [Developer's Guide to Effective AI Prompting](https://docs.base.org/get-started/prompt-library): Learn practical AI prompting techniques to enhance your coding workflow and get better results from AI coding assistants.
@@ -315,6 +316,7 @@
- [Base Builder Codes](https://docs.base.org/apps/builder-codes/builder-codes): Attribute onchain activity to your app, wallet or agent with Builder Codes.
- [Builder Codes for Wallet Developers](https://docs.base.org/apps/builder-codes/wallet-developers): Implement the dataSuffix capability in your wallet to enable Builder Code attribution.
- [Rewards](https://docs.base.org/apps/growth/rewards): Earn financial incentives for building high-quality Apps that drive user engagement and onchain transactions
+- [Accept B20 payments](https://docs.base.org/apps/guides/accept-b20-payments): Accept B20 token payments in your app and match each transaction to an order with onchain memos.
- [Migrate to a Standard Web App](https://docs.base.org/apps/guides/migrate-to-standard-web-app): Migrate your Farcaster mini-app to work in the Base App. Covers replacing deprecated SDK methods, and registering on Base.dev.
- [Build an app on Base](https://docs.base.org/apps/quickstart/build-app): A step-by-step guide to building a Next.js tally app on Base using wagmi and viem, with wallet connection, contract reads and writes, and batch transaction support.
- [Add notifications](https://docs.base.org/apps/technical-guides/base-notifications): Send in-app notifications to your app's users through the Base Dashboard REST API.