---
name: b420
version: 1.0.1
description: B420 (b420.io) is a DEX and launchpad on Base. As an AI agent, trade B420, B69, tokenized stocks, tokens launched on B420, holder-rewards tokens and index tokens (onchain ETFs such as $OG); launch a classic B20 or a holder-rewards token; stake B420 or B69; claim every reward; read markets and any wallet's portfolio with no key. Every action is one raw transaction your own wallet signs and pays gas for; b420.io prepares calldata and never holds keys. The canonical, checked guides are the GitHub skills repo github.com/cryptomfer/b420-skills; this file is the flat summary that links to them.
homepage: https://b420.io
metadata: {"category":"defi","chain":"base","chain_id":8453,"api_base":"https://b420.io/api","skills_repo":"https://github.com/cryptomfer/b420-skills"}
---

# B420: reference for AI agents

> **Source of truth: the skills repo.** Every capability below has a full,
> checked guide in **[github.com/cryptomfer/b420-skills](https://github.com/cryptomfer/b420-skills)**,
> with runnable examples that simulate on Base mainnet before anything is sent.
> Install it and your runtime finds the skills on its own:
>
> ```bash
> git clone https://github.com/cryptomfer/b420-skills.git ~/.claude/skills/b420
> cd ~/.claude/skills/b420 && npm ci     # the examples' only dependency is viem
> ```
>
> This file is the flat HTTP summary: endpoints, contracts and the one-paragraph
> version of each flow, linking to the skill that has the rest. When the two
> disagree, the repo wins.

## What you can do

| Capability | Skill |
|---|---|
| Read markets, tokens, holders, candles, index NAV, staking APR and any wallet's portfolio (GET, no key) | [`market-data/`](https://github.com/cryptomfer/b420-skills/tree/main/market-data) |
| Buy or sell any token (the path picker: index, rewards token, classic launch, aggregator) | [`trade/`](https://github.com/cryptomfer/b420-skills/tree/main/trade) |
| Trade B420, B69, tokenized stocks and other Base tokens in one call (`POST /api/swap`) | [`trade/aggregator/`](https://github.com/cryptomfer/b420-skills/tree/main/trade/aggregator) |
| Trade a token launched on B420 | [`trade/classic/`](https://github.com/cryptomfer/b420-skills/tree/main/trade/classic) |
| Trade a holder-rewards token | [`trade/rewards-token/`](https://github.com/cryptomfer/b420-skills/tree/main/trade/rewards-token) |
| Trade an index token (onchain ETF: MEOW, COIN5, OG) | [`trade/index/`](https://github.com/cryptomfer/b420-skills/tree/main/trade/index) |
| Launch a classic B20 or a holder-rewards token | [`launch/`](https://github.com/cryptomfer/b420-skills/tree/main/launch) · [`launch/classic/`](https://github.com/cryptomfer/b420-skills/tree/main/launch/classic) · [`launch/rewards/`](https://github.com/cryptomfer/b420-skills/tree/main/launch/rewards) |
| Stake B420 (earn tokenized stocks) or B69 (earn B420) | [`staking/`](https://github.com/cryptomfer/b420-skills/tree/main/staking) |
| Claim everything a wallet is owed | [`claim/`](https://github.com/cryptomfer/b420-skills/tree/main/claim) |
| Run the permissionless maintenance calls (fund staking, flush hooks, pay holders) | [`keeper/`](https://github.com/cryptomfer/b420-skills/tree/main/keeper) |
| Signer modes, money rules, fee policy, the full address book | [root `SKILL.md`](https://github.com/cryptomfer/b420-skills/blob/main/SKILL.md) |

## Prerequisites

### 1. A wallet

An agent is a wallet: no account, no API key, no registration. Every write is a raw
transaction `{ to, data, value, gas }` on chainId 8453 that your wallet signs and pays gas for.

| Signer | How you send |
|---|---|
| viem / a private key | `walletClient.sendTransaction({ to, data, value, gas })` |
| Bankr (HTTP-only wallet) | `POST https://api.bankr.bot/wallet/submit` with header `X-API-Key` |
| Coinbase CDP, a Safe, a relayer, a hardware wallet | The raw transaction, as is |

Bankr body (`value` and `gas` as **decimal strings**, never hex):

```json
{ "transaction": { "to": "0x...", "data": "0x...", "value": "1000000000000000", "gas": "1556403", "type": 2, "chainId": 8453 },
  "waitForConfirmation": true }
```

Each of these Bankr settings answers `403` when wrong: "Disable arbitrary contract calls" must be
**off**, `walletApiEnabled` must be **true**, the key must not be `readOnly`, and
`allowedRecipients` must not exclude the B420 contracts (Bankr cannot read recipients out of
calldata).

**Always send the exact raw transaction.** Never use a wallet SDK's convenience helper (a "swap"
or "deploy token" helper) or a natural-language prompt ("buy 0.01 ETH of ..."): they rebuild the
call and drop the fee entries, the gas margin and the exact `value`. Keys come from the
environment only; never print, log or commit them.

### 2. Chain

| Chain | chainId | Gas | RPC |
|---|---|---|---|
| Base | 8453 | ETH | `https://mainnet.base.org` (rate-limited; use your own node for more than a few calls) |

B420 exists only on Base. Explorer: [basescan.org](https://basescan.org).

## Base URL

```
https://b420.io/api
```

Every endpoint in this file answers without a key or a login. A handful of routes back the
website's logged-in UI (Privy) and answer `401 {"error":"missing token"}`: they are not for
agents (list at the end). Send nothing secret to any host.

## Money rules

1. **Quote right before sending.** Prices, pool state and launch prepares go stale in a block.
2. **Simulate every step from the sending address** (approve then act, as one bundle) before
   broadcasting. A revert means nothing is sent.
3. **Send exactly the `value` a prepare or quote returns.** Router buys on ETH pairs send
   `value == amountIn`; anything else reverts.
4. **Approve exact amounts**, and nothing when the allowance already covers the step.
5. **Never resend after a timeout before checking** the receipt, your nonce and, for a launch,
   the predicted token address. A slow answer is not a failed transaction.
6. **No blind retries.** A revert names its cause; fix it, re-quote, re-simulate.
7. **APR and dividends are variable.** They follow volume and the amount staked; never present a
   live rate as fixed.
8. **Keep ETH for gas** on Base.

## Trading

Detect the market onchain, then use its path
([`trade/`](https://github.com/cryptomfer/b420-skills/tree/main/trade) has the multicall):

| Check (in this order) | True | Path |
|---|---|---|
| `isIndex(token)` on each index factory (v3, then v4) | index token | `B420IndexRouter` of that stack |
| `isRewardsToken(token)` on B420RewardsFactory | holder-rewards token | `B420RewardsRouter` |
| `tokenRewards(token).token != 0x0` on LP locker v2, then v1 | classic launch | WETH quote: Universal Router; any other quote: `B420HopRouter` |
| none of the above | anything else with a market | `POST /api/swap` |

**Aggregator** (B420, B69, tokenized stocks, other Base tokens): one call returns the router, the
calldata and an `amountOut` already net of the fee. Approve the returned router on sells; send
`amountIn` as `value` on ETH buys.

```bash
curl -s -X POST https://b420.io/api/swap -H 'content-type: application/json' -d '{
  "tokenIn": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
  "tokenOut": "0xB200000000000000000000231d6C1F1CE455ba32",
  "amountIn": "1000000000000000",
  "sender": "0xYourAgentWallet",
  "slippageBps": 100
}'
# -> {"success":true,"routerAddress":"0x0000000000001ff3684f28c67538d4d072c22734","data":"0x...","amountOut":"...","feeBps":100,"source":"0x"}
```

`sender` must be the address that sends the transaction. Aggregators cannot route hooked B420
pools: classic launches, rewards tokens and index tokens always take their own path.

**Classic launch** (a token launched on B420): WETH-paired pools through the Universal Router
`0x6fF5693b99212Da76ad316178A184AB56D299b43` (Permit2 on sells), quoted with the V4 Quoter on the
locker's exact pool key; pools on any other quote through `B420HopRouter`. New pools refuse
trades for **2 blocks** after the launch (`PoolLocked()`).
[`trade/classic/`](https://github.com/cryptomfer/b420-skills/tree/main/trade/classic).

**Holder-rewards token**: `B420RewardsRouter.buy(token, amountIn, minOut, recipient, fees,
deadline)` and `sell(...)`. ETH pairs send `value == amountIn` and **`gas = max(estimate +
400,000, 1,400,000)`** (the hook buys B420 inside every ETH-pair swap); ERC-20 pairs approve the
router first. [`trade/rewards-token/`](https://github.com/cryptomfer/b420-skills/tree/main/trade/rewards-token).

**Index token**: see the next section.

Default slippage: 100 bps on the aggregator; on classic pools 100 bps from $100,000 of liquidity,
300 from $20,000, 600 below. Never trade with a zero minimum.

## Index tokens (onchain ETFs)

An index token is an onchain basket: buying it with ETH makes the hook buy every constituent at
market **in the same transaction** and mint shares; selling burns shares and sells the basket
back to ETH. The index pool holds no liquidity by design, so **the only valid quote is the V4
Quoter's simulation** (`quoteExactInputSingle` on the index's pool key); never read `slot0`.

```solidity
// B420IndexRouter (one per stack). fees = [(Strategic Reserve, 100)]; approve the router on sells.
function buy(address index, uint256 minShares, address recipient, FeeTake[] fees, uint256 deadline) payable returns (uint256 shares)
function sell(address index, uint256 shares, uint256 minEthOut, address recipient, FeeTake[] fees, uint256 deadline) returns (uint256 ethOut)
```

| Stack | Factory | Router | Hook | Ledger |
|---|---|---|---|---|
| v3 | `0xD732F8c5854ae9E6de3046ad9ecA87577e5e93AF` | `0xbcD0329e229bc620704a2e86bF4D37DB68fA8ff4` | `0x5C654E637B6bC597A655DaB90867296d5Ae76888` | `0x934654A3FCa109A6ce70B2aADbC19d34f0080Fe6` |
| v4 | `0xD408a52ff4871097A89977Ca9fc48dF0D4243293` | `0x7B519742705e71313E982dA1cC89c05C076DA4AB` | `0x3A9721075D9f183648029058549A65C684D16888` | `0x1B66965006fbaa476fc22B8432cc232b6148E958` |

| Index | Address | Stack | Basket |
|---|---|---|---|
| OG Memes Index (`OG`) | `0x30261039E77Af71C7E69CDb571335E9e3214B420` | v4 | TOSHI, TYBG, CHAD, DICKBUTT, mfercoin, launched in equal parts |
| MEOW Index (`MEOW`) | `0xd29327FC1933bC6391d225A71bc1612A6Ed4b420` | v3 | TOSHI, Basecat, KEYCAT, MIGGLES |
| COIN5 (`COIN5`) | `0x7013546C860e527c1af0E6F809F95AAAe27cB420` | v3 | NVDAc, METAc, GOOGLc, AAPLc, AMZNc |

Every index trade pays a 1% index fee split onchain: the holders' share (`holdersBps`, 50% on the
three indexes above) is paid to holders **in ETH**, 30% buys back B420 for the Strategic Reserve,
20% goes to the protocol, and the creator gets the rest (0% on these three). Holders claim with
`claimDividend()` on the index token. The first buy on an empty index needs at least 0.0005 ETH
net. Creating an index is not open to agents.
[`trade/index/`](https://github.com/cryptomfer/b420-skills/tree/main/trade/index).

## Launching

```
GET  /api/launch                          status: { paused, live, factory, predictedSuffix: "b420" }
GET  /api/launch?name=&symbol=            name check (classic): { taken, existing? }
GET  /api/launch/rewards?name=&symbol=    name check (rewards)
GET  /api/launch?quote=0x...              preview a quote asset for a classic launch
POST /api/launch                          classic prepare: the deployToken call and its exact value
POST /api/launch/rewards                  rewards prepare: the launch call (salt bound to your address)
POST /api/launch/confirm  { "txHash" }    index your launch at once (else the worker does it in ~2 min)
```

Stop if `paused` is true, `live` is false or `factory` is not B420Factory v2. Every B420 token
address ends in `b420`. A prepare is single-use; prepare again rather than reusing one.

| | Classic B20 | Holder-rewards token |
|---|---|---|
| Call | B420Factory v2 `deployToken` (`0x0B5E30E8D5fdD6257F4Eb293dFc3121b0207F575`) | B420RewardsFactory `launch` (`0x4f924EDB313efB9E90CAf1f768dE54E5fFcD5e31`) |
| Pool | Uniswap v4, 1% LP fee, liquidity locked forever | Uniswap v4, the hook takes 1% of the paired leg, liquidity locked forever |
| Fee split (enforced onchain) | 50% to you, 50% to FeeCollector v2 | 50% creator half (the `holdersBps` part of it to holders), 20% Strategic Reserve, 15% B420 leg, 15% stock leg |
| Paired asset | WETH by default, or any token with a price | Native ETH by default, or any ERC-20 with a price |
| Options | Dev buy, share your fees with holders (distributor), airdrop to a list | Dev buy, `holdersBps` (raise-only later) |
| Your fees | ClankerFeeLocker `claim` | B420RewardsLedger creator slot |

A launch carries no fee of its own: `value` only funds an optional dev buy. Full guides:
[`launch/classic/`](https://github.com/cryptomfer/b420-skills/tree/main/launch/classic) ·
[`launch/rewards/`](https://github.com/cryptomfer/b420-skills/tree/main/launch/rewards).
Token page after launch: `https://b420.io/terminal/<token>`.

## Staking

| Pool | Address | Stake | Earn |
|---|---|---|---|
| StakingB420 | `0xC411bA66d1819054f67cDE26424cd876DB703E79` | B420 | the 10 registry tokenized stocks |
| StakingB69 | `0x82E6b3CEE079432F31D64855ed3DD5faCA71d309` | B69 | B420 |

`approve` the exact amount on the staking token, then `stake(amount)`. `getReward()` claims every
reward token. Leaving takes 48 hours: `requestUnstake(amount)` (stops earning), then `withdraw()`
once unlocked; `cancelUnstake()` re-stakes, `exit()` claims and requests the whole stake.
Rewards come mostly from FeeCollector v2, which splits the protocol's fee income 40% to the Strategic
Reserve, 30% into B420 for B69 stakers and 30% into registry stocks for B420 stakers. Current APR:
`GET /api/rewards/stats` (`pools[].apr`, a trailing 30-day figure).
[`staking/`](https://github.com/cryptomfer/b420-skills/tree/main/staking).

## Claiming

| Reward | Call |
|---|---|
| Staking rewards | `getReward()` on each pool |
| Holder-rewards token dividend (paired asset) | `claimDividend()` on the token |
| Index holder dividend (ETH) | `claimDividend()` on the index token |
| Rewards ledger creator balance | `claimFor(token, 0)` or `claimAllFor(wallet, tokens)` on B420RewardsLedger |
| Index ledger (creator, ops) | `claim`, `claimFor` or `claimAllFor` on the stack's ledger |
| Classic launch creator fees | `claim(wallet, currency)` on ClankerFeeLocker |
| Classic holder distributor (merkle) | `claim(currency, wallet, cumulativeAmount, proof)`; proofs from `GET /api/distributor/<token>/claim/<wallet>` |
| Airdrop allocation | `claim(token, wallet, allocatedAmount, proof)`; proofs from `GET /api/airdrop/<token>?account=<wallet>` |

One read finds everything a wallet can claim: `GET /api/portfolio/<wallet>` (`claimable[]`).
[`claim/`](https://github.com/cryptomfer/b420-skills/tree/main/claim) has a `scan` command that
checks every source onchain.

## Keeper calls (permissionless)

Anyone may run these; the caller pays gas and **receives nothing**. Send one only when a status
read shows work pending.

| Call | Effect |
|---|---|
| `swapAndFund` on FeeCollector v1 / v2 | Converts collected fees into B420 and registry stocks and funds both staking pools |
| `forward(token)` on a FeeCollector | Claims a fee token from the fee locker and routes it to staking or the reserve, no swap |
| `flush` on the rewards hook or an index hook | Releases parked holder fees and fee shares |
| `collectRewards(token)` on an LP locker | Pulls a classic pool's fees into the fee locker |
| `claimDividendFor(wallets)` on a rewards or index token | Pays a batch of holders their dividends |
| `claimFor` / `claimAllFor` on a ledger | Pays a fixed recipient (treasury, collector, staking) |

[`keeper/`](https://github.com/cryptomfer/b420-skills/tree/main/keeper) has a `status` command
that lists what needs doing.

## Fees

Trades made by agents carry the same 1% frontend fee as trades on b420.io, to the Strategic
Reserve `0xA3320DCaFAa124173fdf7BD18EcD85abBA325590`, on top of each pool's own fee. `POST
/api/swap` applies it server-side (its `amountOut` is already net); on the Universal Router it is
a `PAY_PORTION` of 100 bps before the final `SWEEP`; the rewards and index routers take it as the
`fees` argument `[(Strategic Reserve, 100)]`. Launches, staking, claims and keeper calls carry no
frontend fee.

## Never do

| Never | Why |
|---|---|
| Launch on B420Factory v1 | Deprecated: `deployToken` reverts `Deprecated()` |
| Curve launches or curve trades | Closed onchain (`launchEnabled()` is false) |
| Stake into a legacy dividend vault | Paused; existing stakers can only exit |
| Create an index | Not open to agents |
| Call distributor factory `create()` or any owner or admin function | Owner-only; they revert |
| Route rewards or index tokens through the Universal Router or an aggregator | No fee entries and no gas margin; use their routers |
| Reuse a launch prepare | Single-use, and a rewards salt is bound to the sender |
| Call a login-only endpoint | Not for agents (see below) |

## Contracts (Base mainnet, chainId 8453)

| Role | Address |
|---|---|
| B420 (token, B20, 18 decimals) | `0xB200000000000000000000231d6C1F1CE455ba32` |
| B69 (token, B20, 18 decimals) | `0xB2000000000000000000007594Fe5aCD56DF3937` |
| B420Factory v2 (classic launches) | `0x0B5E30E8D5fdD6257F4Eb293dFc3121b0207F575` |
| B420RewardsFactory | `0x4f924EDB313efB9E90CAf1f768dE54E5fFcD5e31` |
| B420RewardsRouter | `0x383156D66BdA2369c6eE061C66aa3D32E38cec72` |
| B420RewardsLedger | `0x8e95B431B70094B66836074B01c380A4935B7d49` |
| B420HopRouter | `0x82C2B0c34f843A5724497ce809ae96a6eeb03721` |
| Index v4 factory / router | `0xD408a52ff4871097A89977Ca9fc48dF0D4243293` / `0x7B519742705e71313E982dA1cC89c05C076DA4AB` |
| Index v3 factory / router | `0xD732F8c5854ae9E6de3046ad9ecA87577e5e93AF` / `0xbcD0329e229bc620704a2e86bF4D37DB68fA8ff4` |
| LP locker v2 / v1 | `0x351C934d698eB3c0683066D2fbD6CE7215573Bc4` / `0x0c0B04d8Bd761dA1899b1a13CD3353d0974F99D3` |
| ClankerFeeLocker | `0x20835181fD6F4e62AA8d630A89b0e5c8676808C6` |
| FeeCollector v2 / v1 | `0x22F005aa2b90E06C642C7462388b9d212D6344d8` / `0xB9366B662b610F730a50408Db17E550d64F06F44` |
| StakingB420 / StakingB69 | `0xC411bA66d1819054f67cDE26424cd876DB703E79` / `0x82E6b3CEE079432F31D64855ed3DD5faCA71d309` |
| B420StockRegistry | `0x5E4643c2F48c14e09f209CAe5A51455211e10c1E` |
| Uniswap v4 PoolManager | `0x498581fF718922c3f8e6A244956aF099B2652b2b` |
| Universal Router / V4 Quoter | `0x6fF5693b99212Da76ad316178A184AB56D299b43` / `0x0d5e0F971ED27FBfF6c2837bf31316121532048D` |
| Strategic Reserve | `0xA3320DCaFAa124173fdf7BD18EcD85abBA325590` |
| WETH / USDC | `0x4200000000000000000000000000000000000006` / `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |

The complete address book (hooks, extensions, distributors, the ten stocks, past versions) is in
the [root `SKILL.md`](https://github.com/cryptomfer/b420-skills/blob/main/SKILL.md). At run time,
prefer what the API and the factories return (`GET /api/launch` gives the live factory).

## Complete API reference

All `GET`, no auth, JSON, edge-cached: poll no faster than the cache window, and take any amount a
transaction depends on from the chain, not from these snapshots. Field lists:
[`market-data/`](https://github.com/cryptomfer/b420-skills/tree/main/market-data).

| Area | Endpoints |
|---|---|
| Markets | `/api/tokens` · `/api/b20s?q=&limit=` · `/api/stocks` · `/api/cbstocks` · `/api/st0x` · `/api/launch-stocks` · `/api/launches?limit=&creator=` · `/api/stats` · `/api/verified` |
| A token | `/api/token/<address>` · `…/holders` · `…/links` · `…/social-trades` · `…/theses` · `/api/security/<address>` · `/api/position/<wallet>/<token>` |
| Prices and candles | `/api/ohlc/<pool>?tf=&limit=&before=` · `/api/trades/<pool>` · `/api/prices?items=` · `/api/quote-price?address=` |
| Index tokens | `/api/indexes` · `/api/index/<address>` · `…/holders` · `…/ohlc?tf=` · `…/trades` · `/api/index/routes?token=&stack=` |
| Holder-rewards tokens | `/api/rewards-launch/<address>` · `…/holders` · `…/ohlc?tf=` · `…/trades` |
| Staking, fees, reserve | `/api/rewards` · `/api/rewards/stats` · `/api/buybacks` · `/api/reserve` |
| Distributors and airdrops | `/api/distributors` · `/api/distributor/<token>` · `…/claim/<wallet>` · `…/claim/all` · `/api/airdrop/<token>?account=` |
| Wallets and people | `/api/portfolio/<wallet>` · `…/activity` · `/api/profile/<username or 0x>` · `/api/profile/search?q=` · `/api/leaderboard?window=` |
| DEX adapter (GeckoTerminal, DEX Screener) | `/api/dex/latest-block` · `/api/dex/asset?id=` · `/api/dex/pair?id=` · `/api/dex/events?fromBlock=&toBlock=` |
| Write prepares | `POST /api/swap` · `POST /api/launch` · `POST /api/launch/rewards` · `POST /api/launch/confirm` |

**Login-only (Privy), not for agents:** `POST /api/swap-event`, profile writes and
`GET /api/profile/me`, `/api/thesis` writes, `/api/referral`, `/api/distributor/<token>/enable`,
`/api/push/subscribe`, `/api/terms/accept`, `/api/notifications`, `/api/airdrop/snapshot`, token
link edits, `POST /api/verified`.

## Error responses

| Code | Meaning | Do |
|---|---|---|
| 400 | Bad input (not an address, unknown token, a missing field) | Fix the request |
| 401 | A login-only endpoint | Not for agents; use the public endpoints |
| 404 | Unknown username or record; on `/api/launch/confirm`, "receipt not found yet" | Check the identifier; for a confirm, retry every 6 s, up to 10 times |
| 429 | Too many requests (the prepare and confirm routes are rate-limited) | Wait for the window, retry once |
| 502 | An upstream (the chain, GeckoTerminal) did not answer | Retry later; read the chain for amounts |
| 503 | A backing service is unavailable | Retry later |

Onchain reverts name their cause (`TooLittleReceived`, `PoolLocked`, `BadValue`,
`InsufficientAllowance`, ...): decode it, fix the cause, re-quote and re-simulate.

## Discovery and support

- This file: https://b420.io/skill.md · for LLM crawlers: https://b420.io/llms.txt
- Skills repo (source of truth): https://github.com/cryptomfer/b420-skills
- Website: https://b420.io · token pages: `https://b420.io/terminal/<token>`
- X: https://x.com/b420coin

License of the skills: CC0.
