Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions docs/base-chain/network-information/ecosystem-bridges.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,31 @@ Move assets to and from Base using the routes below. Pick the one that matches w
If you hold funds in a Coinbase account, you can withdraw many assets straight onto the Base network — select **Base** as the network when withdrawing, no bridge required. This is often the fastest path for USDC and ETH.
</Note>

## USDC on Base

USDC on Base is issued natively by Circle. Use the native USDC contract for new integrations. Circle publishes the canonical address for every network on its [USDC contract addresses](https://developers.circle.com/stablecoins/usdc-contract-addresses) page.

| Token | Network | Address | Issuer |
|---|---|---|---|
| USDC | Base Mainnet | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | Circle (native) |
| USDC | Base Sepolia | `0x036CbD53842c5426634e7929541eC2318f3dCF7e` | Circle (native) |
| USDbC (USD Base Coin) | Base Mainnet | `0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA` | Bridged from Ethereum |

USDbC is the earlier bridged form of USDC. It is a Standard Bridge token: Ethereum USDC (`0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48`) is locked on L1 and USDbC is minted on Base by the `L2StandardBridge` predeploy (`0x4200000000000000000000000000000000000010`). Circle does not issue USDbC, and it is a separate token from native USDC. To move from USDbC to native USDC, swap it onchain, or withdraw it to Ethereum through the Standard Bridge. The [Superchain token list](https://github.com/ethereum-optimism/ethereum-optimism.github.io/blob/master/data/BridgedUSDC/data.json) records the USDbC mapping.

To move native USDC between Base and other networks, use Circle's [Cross-Chain Transfer Protocol (CCTP)](https://developers.circle.com/cctp). CCTP burns USDC on the source chain and mints native USDC on the destination chain, so no bridged representation is created. Base is CCTP domain `6`. For the list of supported chains, see [CCTP supported blockchains](https://developers.circle.com/cctp/cctp-supported-blockchains).

### Other Stablecoins

Circle also issues EURC, a euro stablecoin, natively on Base:

| Token | Network | Address | Issuer |
|---|---|---|---|
| EURC | Base Mainnet | `0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42` | Circle (native) |
| EURC | Base Sepolia | `0x808456652fdb597867f38412077A9182bf77359F` | Circle (native) |

Confirm addresses on the issuer's own page ([EURC contract addresses](https://developers.circle.com/stablecoins/eurc-contract-addresses)) before you integrate. Stablecoins created with the [B20 Stablecoin variant](/get-started/issue-stablecoins) carry a self-declared `currency()` code; B20 checks the format of that code, not the issuer or its reserves.

## From Ethereum

Bridge ETH and supported ERC-20s between Ethereum mainnet (L1) and Base. Both providers support mainnet and Sepolia testnet.
Expand Down
2 changes: 2 additions & 0 deletions docs/build-on-base/issue-rwa/create-an-asset-token.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ New to B20? See the [B20 Token Standard](/build-on-base/issue-rwa/create-an-asse

Choose the **Asset** variant when you need configurable decimals, announcements, a scheduled UI multiplier, extra metadata, or `batchMint`. Asset decimals must be in the range `[6, 18]`; values outside that range revert `InvalidDecimals`. The type is sealed into the token address at creation and cannot change.

[`createB20`](/specifications/b20/reference/interfaces/ib20-factory/create-b20) requires no role or allowlist at the protocol level. Any account can create an Asset token once the B20 Asset variant is active in the [Activation Registry](/specifications/b20/reference/interfaces/i-activation-registry/is-activated), and the variant is active on Base Mainnet. The Factory keeps no access to the token after creation; the issuer configures and controls it.

<CodeGroup>
{/* sample: stock-create-ts */}
```typescript TypeScript lines wrap expandable highlight={15}
Expand Down
17 changes: 17 additions & 0 deletions docs/get-started/issue-stablecoins.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,23 @@ Issue a fiat-backed stablecoin on Base with [B20](/specifications/b20), Base's n
The demo uses a local browser-generated account to submit real transactions on **Base Vibenet**. If Vibenet or its B20 features are unavailable, it automatically switches to an illustrative offline version.
</Tip>

## Network Status

B20 shipped in the [Beryl upgrade](/upgrades/beryl/overview). The B20 Stablecoin and Asset variants are active on Base Mainnet. Before you deploy on any network, confirm the variant is active with [`isActivated`](/specifications/b20/reference/interfaces/i-activation-registry/is-activated) on the Activation Registry.

[`createB20`](/specifications/b20/reference/interfaces/ib20-factory/create-b20) requires no role or allowlist. Any account can create a stablecoin once the variant is active, and the Factory keeps no access to the token after creation.

## Integrate Stablecoins From Other Issuers

Wallets, apps, and payment providers that hold or move stablecoins issued by others work with the same interface for every issuer:

- **One integration.** Every B20 stablecoin exposes the same ERC-20 surface and `IB20Stablecoin` interface, so code written for one issuer's token works for any other.
- **Per-token rules.** Each token enforces its own issuer's [policies](/specifications/b20/concepts/policies) on the sender, receiver, and transfer executor. A transfer of one issuer's token succeeds only if the accounts pass that token's policies. Call `isAuthorized` on the Policy Registry to check an account before you send.
- **Shared compliance lists.** Policies live in the Policy Registry, not on the token, so one policy can back many tokens. Issuers that agree on a list can attach the same policy ID.
- **Conversion and redemption.** B20 has no protocol-level conversion between stablecoins from different issuers. Each token is a separate asset: convert through an onchain swap or a venue that supports both tokens. Fiat redemption happens offchain with each issuer, which then [burns the redeemed supply](/build-on-base/issue-stablecoins/burn-supply).

KYC and sanctions screening happen offchain. An issuer or its compliance operator applies the result onchain by adding accounts to an allowlist or blocklist policy. See [Restrict Who Can Hold It](/build-on-base/issue-stablecoins/restrict-who-can-hold) and [Block an Account](/build-on-base/issue-stablecoins/block-an-account).

## Guides

<CardGroup cols={2}>
Expand Down
2 changes: 2 additions & 0 deletions docs/sdks/tokenized-stocks/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Use the public Tokenized Stocks API to retrieve reference data for [Coinbase-iss
Coinbase tokenized stocks are available only to persons in eligible jurisdictions outside of the United States. This API provides read-only reference data only; it does not provide trading, execution, issuance, minting, redemption, or custody functionality. The availability of any token or related product may be subject to jurisdictional, eligibility, and compliance requirements. Integrators are responsible for applying applicable restrictions.
</Note>

The API returns token-level data only. It does not return a wallet's positions, cost basis, trade history, or profit and loss. To show a holder's position, read the token balance onchain and convert it with the current `multiplier` (see [List Tokenized Stocks](/build-on-base/integrate-defi/list-tokenized-stocks)). Cost basis and profit and loss across trading venues must come from your own records or an onchain indexer.

```text Base URL
https://api.coinbase.com/v1/tokenized-stocks
```
Expand Down
77 changes: 76 additions & 1 deletion docs/specifications/build-transaction/predicates-and-safety.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

## Balance Example

```json Balance predicate lines wrap expandable

Check warning on line 27 in docs/specifications/build-transaction/predicates-and-safety.mdx

View workflow job for this annotation

GitHub Actions / Docs Style / Conformance

Docs style

[codeblock/highlight] Consider `highlight={}` to draw attention to key lines
{
"type": "balance",
"params": {
Expand All @@ -37,7 +37,7 @@

## Storage Example

```json Storage predicate lines wrap expandable

Check warning on line 40 in docs/specifications/build-transaction/predicates-and-safety.mdx

View workflow job for this annotation

GitHub Actions / Docs Style / Conformance

Docs style

[codeblock/highlight] Consider `highlight={}` to draw attention to key lines
{
"type": "storage",
"params": {
Expand Down Expand Up @@ -66,4 +66,79 @@

Do not use transaction placement as a source of randomness. Do not assume a zero-valued slot makes a CREATE2 address, proxy, or uninitialized contract safe to call.

Validity criteria are not recorded onchain. Do not include secrets in calldata or predicate values.
Validity criteria are not recorded onchain. Do not include secrets in calldata or predicate values.

## Read B20 and Policy Registry State

[B20](/specifications/b20) tokens and the Policy Registry run as precompiles, but they keep their state in ordinary account storage. Each B20 token stores its state at the token address. The Policy Registry stores its state at `0x8453000000000000000000000000000000000002`. A `storage` predicate reads these slots the same way it reads any contract's storage.

The `balance` predicate reads an account's native ETH balance. To gate on a B20 token balance, use a `storage` predicate on the token's balance slot.

### Slot Derivation

B20 storage uses [ERC-7201](https://eips.ethereum.org/EIPS/eip-7201) namespaces. A top-level field lives at `root + offset`. A mapping value lives at `keccak256(abi.encode(key, baseSlot))`, where `baseSlot` is `root + offset`. For a nested mapping, hash the outer key first, then hash the inner key against the result. Packed fields follow Solidity packing: offset `0` is the lowest-order byte of the word.

| Namespace | Root slot | Used by |
|---|---|---|
| `base.b20` | `0xc78b71fee795ddd74aff64ea9b2474194c938c3196430e10bb5f01ed48434000` | Every B20 token |
| `base.b20.asset` | `0xfdc6d4552d1286ade4d9facdbf0fb50d2ec9b89a90e104f26fd277585e374b00` | Asset variant fields |
| `base.b20.stablecoin` | `0x35827975a06ca0e9367ea3129b19441d45d0ca58e30b7693f09e73d0943d6200` | Stablecoin variant fields |
| `base.policy_registry` | `0x00503aeb06982fa1fe3151dc68f90b3946c55c449dfd447e49dcaece71ba4a00` | Policy Registry |

### B20 Token Fields

Offsets from the `base.b20` root, read at the token address:

| Offset | Field | Encoding |
|---|---|---|
| `3` | `totalSupply` | `uint256` |
| `4` | `balances[account]` | `mapping(address => uint256)` |
| `5` | `allowances[owner][spender]` | `mapping(address => mapping(address => uint256))` |
| `6` | `roles[role][account]` | `mapping(bytes32 => mapping(address => bool))` |
| `9` | Transfer policy IDs | Packed `uint64` values: sender at bytes 0–7, receiver at bytes 8–15, executor at bytes 16–23 |
| `10` | Mint receiver policy ID | `uint64` at bytes 0–7 |
| `11` | Paused features | Bitmask by `PausableFeature` ordinal: `TRANSFER` bit 0, `MINT` bit 1, `BURN` bit 2, `SEIZE` bit 3 |
| `12` | Supply cap | `uint256` |
| `14` | Seize policy IDs | Packed `uint64` values: seize-exempt at bytes 0–7, seize-receiver at bytes 8–15 |

### Policy Registry Fields

Offsets from the `base.policy_registry` root, read at the Policy Registry address. Policy IDs are `uint64` keys.

| Offset | Field | Encoding |
|---|---|---|
| `0` | `policies[policyId]` | Packed word: admin address in bits 0–159, exists flag in bit 255 |
| `1` | `members[policyId][account]` | `mapping(uint64 => mapping(address => bool))` |
| `2` | `pendingAdmins[policyId]` | `address` |
| `4` | `children[policyId]` | Dynamic `uint64[]` of composite child policy IDs |

A `members` flag records list membership, not the `isAuthorized` result. On an allowlist, `true` means the account is authorized. On a blocklist, `true` means the account is blocked. Composite (`UNION` and `INTERSECT`) policies have no member set of their own, and a predicate cannot evaluate them. To watch a composite policy, read the member slots of its child policies. An [inverted policy ID](/specifications/b20/concepts/policies#23-inverting-a-policy) has no record of its own; clear bit 63, read the base policy's slots, and invert the condition you check.

### Example: Wait for an Allowlist Addition

Compute the `members[policyId][account]` slot with `cast index`, then wait for it to become `1`:

```bash Compute a member slot lines wrap highlight={4}
# members base slot = base.policy_registry root + 1
MEMBERS=0x00503aeb06982fa1fe3151dc68f90b3946c55c449dfd447e49dcaece71ba4a01
PER_POLICY=$(cast index uint64 <POLICY_ID> $MEMBERS)
cast index address <ACCOUNT> $PER_POLICY
```

```json Allowlist predicate lines wrap expandable highlight={5,7}
{
"type": "storage",
"params": {
"address": "0x8453000000000000000000000000000000000002",
"slot": "<MEMBER_SLOT>",
"op": "=",
"value": "0x1"
}
}
```

To wait until a token's transfers are unpaused, read offset `11` (`0xc78b71fee795ddd74aff64ea9b2474194c938c3196430e10bb5f01ed4843400b`) at the token address with `mask` `0x1` and `value` `0x0`. When you mask a packed field, shift the expected `value` into the same byte position as the mask.

<Warning>
The storage layout is part of the precompile implementation. Network upgrades append fields without reordering existing ones (for example, Cobalt added offset `14` to `base.b20`), but check the [B20 changelog](/specifications/b20/changelog) and the [base-std changelog](https://github.com/base/base-std/tree/main/changelog), which records each storage layout change, before you rely on a slot. The [base-std storage layout tests](https://github.com/base/base-std/tree/main/test/unit/storage) are the reference that the node implementation must match.
</Warning>
6 changes: 6 additions & 0 deletions docs/specifications/reference/smart-contracts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ description: "How smart contracts work on Base, why Base needs its own Foundry b

Base is an EVM-equivalent Ethereum L2. Smart contracts are written in Solidity or Vyper and deploy exactly as they do on Ethereum — the same bytecode, ABIs, and tooling apply.

## Token Standards

Any token standard implemented as a smart contract deploys and behaves on Base as it does on Ethereum. That includes [ERC-20](https://eips.ethereum.org/EIPS/eip-20), [ERC-721](https://eips.ethereum.org/EIPS/eip-721), [ERC-1155](https://eips.ethereum.org/EIPS/eip-1155), [ERC-4626](https://eips.ethereum.org/EIPS/eip-4626) vaults, and permissioned-token standards such as [ERC-3643](https://eips.ethereum.org/EIPS/eip-3643). These are contracts that you or your provider deploy, upgrade, and maintain; the Base node does not implement them.

[B20](/specifications/b20) is Base's native token standard. It runs as precompiles in the node and keeps full selector and behavior parity with ERC-20, so ERC-20 tooling works with B20 tokens unchanged. B20 logic changes only through [network upgrades](/upgrades/overview), and each change is listed in the [B20 changelog](/specifications/b20/changelog).

## How Base Differs

Base adds **native precompiles** at fixed addresses that extend the EVM with functionality built into the node itself, such as the [B20 token standard](/build-on-base/issue-rwa/create-an-asset-token), the PolicyRegistry, and the B20 Factory. These addresses hold no contract bytecode, so standard Foundry (`forge`, `cast`, `anvil`) cannot simulate calls to them and aborts with `call to non-contract address`.
Expand Down
Loading