BRC-20 Token Development (Bitcoin Ordinals)
BRC-20—experimental token standard on Bitcoin built atop Ordinals protocol. No smart contract, no virtual machine—just JSON data in witness part of Bitcoin transactions and off-chain indexers tracking all balances.
First thing to understand: BRC-20 is not ERC-20 on Bitcoin. Fundamentally different system, no on-chain logic execution. "Contract" of BRC-20 token—JSON object inscribed in satoshi via Ordinals inscription.
How Ordinals Works
Satoshi Nakamoto gave each satoshi unique ordinal number (ordinal number) according to mining order. Ordinals protocol (Casey Rodarmor, 2023) uses these numbers to attach arbitrary data to specific satoshis via inscriptions—data recorded in witness field of SegWit transaction.
Inscription size not strictly limited, but Bitcoin nodes accept witness data up to 4MB (block limit in SegWit). Inscription cost depends on fee rate (sat/vbyte) and data size. Average cost at normal network load: $5–50 per inscription.
JSON Format for BRC-20
BRC-20 uses three operation types, each—separate Ordinals inscription:
Deploy—create token:
{
"p": "brc-20",
"op": "deploy",
"tick": "MYtk",
"max": "21000000",
"lim": "1000",
"dec": "18"
}
tick—4-character ticker (case-insensitive, stored as-is). max—maximum supply. lim—max per single mint transaction. dec—decimals (optional, default 18).
Mint—mint tokens (anyone can execute until max filled):
{
"p": "brc-20",
"op": "mint",
"tick": "MYKT",
"amt": "1000"
}
Transfer—transfer tokens (two-step process):
{
"p": "brc-20",
"op": "transfer",
"tick": "MYKT",
"amt": "500"
}
BRC-20 transfer—not one step. First create transfer inscription on sender address. Then this inscription transferred to recipient via standard Bitcoin transaction. Indexer records both steps and counts transfer only when exact UTXO containing transfer inscription is sent.
Working with Ordinals API and Indexers
For creating inscriptions—ord library (official client) or third-party APIs:
import axios from 'axios';
// Using Ordinalsbot API to create inscription
async function deployBRC20Token(config: {
tick: string;
max: string;
lim: string;
receiverAddress: string;
}) {
const inscriptionData = JSON.stringify({
p: "brc-20",
op: "deploy",
tick: config.tick,
max: config.max,
lim: config.lim,
});
const response = await axios.post('https://api.ordinalsbot.com/inscribe', {
files: [{
name: "deploy.json",
size: Buffer.byteLength(inscriptionData),
type: "text/plain;charset=utf-8",
dataURL: `data:text/plain;charset=utf-8,${inscriptionData}`,
}],
receiveAddress: config.receiverAddress,
fee: 15, // sat/vbyte — check current fee
lowPostage: false,
});
return response.data; // contains payment address to send BTC to
}
Check balance via indexers. No official indexers—BRC-20 completely depends on third-party services:
// Hiro API — one of most reliable indexers
async function getBRC20Balance(address: string, ticker: string): Promise<string> {
const response = await axios.get(
`https://api.hiro.so/ordinals/v1/brc-20/balances/${address}`,
{
params: { ticker: ticker.toUpperCase() },
headers: { 'x-api-key': process.env.HIRO_API_KEY },
}
);
const token = response.data.results.find(
(t: any) => t.ticker.toLowerCase() === ticker.toLowerCase()
);
return token?.overall_balance ?? "0";
}
// Check transferable (ready to transfer) balance
async function getTransferableBalance(address: string, ticker: string): Promise<string> {
const response = await axios.get(
`https://api.hiro.so/ordinals/v1/brc-20/balances/${address}`,
{ params: { ticker, balance_type: "transferable" } }
);
return response.data.results[0]?.transferable_balance ?? "0";
}
Quirks That Often Break Integrations
Two-step transfer. Developers used to ERC-20 expect transfer—one transaction. No. If UTXO with transfer inscription spent not to recipient but arbitrary address—indexer counts this "burn". Irreversible.
UTXO dependency. BRC-20 balance tied to Bitcoin UTXO containing inscriptions. If wallet not "aware" of inscriptions and accidentally spends UTXO with inscription as regular bitcoin (e.g., fee bumping)—tokens lost.
Different indexers—different results. Early BRC-20 different indexers interpreted edge cases differently. Situation stabilized now, but when developing verify results across multiple indexers (Hiro, UniSat, OKX Ordinals).
Ticker capture. First deploy with ticker "MYKT" becomes canonical. Second deploy with same ticker valid technically, but indexers ignore it—first gets priority. Before deploy always check ticker not taken.
async function isTickerAvailable(ticker: string): Promise<boolean> {
const response = await axios.get(
`https://api.hiro.so/ordinals/v1/brc-20/tokens/${ticker.toLowerCase()}`
);
return response.status === 404; // 404 = ticker free
}
Mint Mechanics and Fair Launch
Standard BRC-20 presumes open mint: anyone can mint up to lim per transaction while max not reached. This fair launch—first miners get tokens for gas cost.
Projects wanting controlled distribution—BRC-20 doesn't fit natively. Options:
- Set
lim = max, then one address mints entire supply - Use ORC-20 or other experimental extensions with richer logic
- Apply external whitelist and sell mint rights to addresses
Infrastructure for BRC-20 Project
Beyond token deployment itself, real project needs:
Indexer node. For reliable service—own or trusted provider. Running full Bitcoin node + ord daemon: requires 600+ GB SSD, sync 3–7 days.
Inscription service. Service creating mint/transfer inscriptions on behalf of users (if part of your UX). UTXO management, fee estimation, retry on failed transactions.
Explorer. Most BRC-20 projects build own explorer—standard Bitcoin explorers don't show BRC-20.
BRC-20 Limitations
BRC-20 has no:
- Programmable logic (no smart contracts)
- Atomic swaps between other BRC-20 tokens (DEX on BRC-20—complex off-chain construct)
- Instant confirmation (Bitcoin blocks ~10 minutes)
- Cheap transactions at load (at BRC-20 activity peaks fee reached $30–100 per transfer)
For projects needing programmability—Stacks (Clarity smart contracts with Bitcoin finality) or Bitcoin L2 (Merlin, BEVM).
Timeline: BRC-20 deploy itself—few hours. Full infrastructure (indexer, mint service, explorer, wallet)—4–8 weeks.







