Bitcoin Runes Token Development
Before Runes came confusion: BRC-20 worked atop Ordinals, created inscription for each transfer, clogged mempool with "junk" transactions. Casey Rodarmor, Ordinals creator, developed Runes as clean-room solution for fungible tokens on Bitcoin—no extra data, using existing bitcoin primitives efficiently. Runes launched at block 840,000 (2024 halving).
How Runes Protocol Works
Runes needs no Bitcoin consensus changes. Protocol lives in OP_RETURN outputs—data in them not stored in UTXO set, blockchain not clogged (unlike BRC-20 storing state in satoshi).
Key concepts:
Runestone—protocol message, encoded in OP_RETURN output of transaction. Contains instructions: etching (creation), mint, transfer, edict (move balances between outputs).
UTXO as balance holder—Runes balances not in global mapping (like ERC-20) but in specific UTXO. If you hold 1000 RUNE, you have UTXO with attached 1000 RUNE balance. Fundamental difference from EVM tokens.
Rune ID—{block_height}:{tx_index} of etching transaction. E.g., first Rune etched at block 840,000 has ID 840000:3.
Spacers—visual separators in name (dots), UNCOMMON•GOODS—standard first Rune from Casey.
Creating Rune (Etching)
Etching—transaction with Runestone in OP_RETURN, announcing new Rune:
Etching parameters:
- divisibility: 0–38 (analog of decimals in ERC-20, but for satoshi sub-units)
- symbol: Unicode symbol for display
- premine: tokens for etcher immediately
- terms: conditions for open mint (if allowed)
- amount: per single mint transaction
- cap: max mint times
- height: [start_block, end_block] for open mint
- offset: [start_offset, end_offset] relative to etching block
- turbo: compatibility flag with future protocol versions
Runestone data encoded via varint encoding (LEB128)—compact variable-length number representation. Each tag—(tag, value) pair.
Practical Implementation via ord CLI
# Install ord (official client)
cargo install ord
# Sync with Bitcoin node (or RPC to external)
ord --bitcoin-rpc-url http://user:pass@localhost:8332 index
# Create wallet
ord wallet create
# Etch new Rune
ord wallet etch \
--rune "MYTOKEN•NAME" \
--divisibility 8 \
--symbol "M" \
--supply 21000000 \
--premine 21000000 \
--fee-rate 20
# Mint (if open mint enabled)
ord wallet mint \
--rune "MYTOKEN•NAME" \
--fee-rate 20
Via Library (JavaScript/TypeScript)
For app integration use runestone npm package or direct bitcoinjs-lib work:
import { Runestone, Etching, Terms, RuneId } from "runestone-lib";
import * as bitcoin from "bitcoinjs-lib";
function buildEtchingTransaction(
utxo: UTXO,
runeName: string,
divisibility: number,
supply: bigint,
feeRate: number
): bitcoin.Transaction {
const runestone = new Runestone({
etching: new Etching({
rune: Rune.fromString(runeName),
divisibility,
symbol: "T",
premine: supply,
turbo: true,
}),
// Edicts define premine distribution to outputs
edicts: [{
id: new RuneId(0n, 0n), // 0:0 = self on etching
amount: supply,
output: 1n, // output index for premine
}],
});
const psbt = new bitcoin.Psbt({ network: bitcoin.networks.bitcoin });
// Input: funded UTXO for fee payment
psbt.addInput({
hash: utxo.txid,
index: utxo.vout,
witnessUtxo: { script: utxo.scriptPubKey, value: utxo.value },
});
// Output 0: OP_RETURN with Runestone
psbt.addOutput({
script: bitcoin.script.compile([
bitcoin.opcodes.OP_RETURN,
Buffer.from("52554e45", "hex"), // RUNE magic bytes
runestone.encipher(),
]),
value: 0,
});
// Output 1: premine recipient (must not be dust)
psbt.addOutput({
address: recipientAddress,
value: 546, // dust limit for P2WPKH
});
// Output 2: change
psbt.addOutput({ address: changeAddress, value: changeAmount });
return psbt;
}
Transfer Logic: Edicts
Transferring Runes—transaction with Runestone containing edicts. Each edict: which Rune, how much, to which output.
Important protocol rule: if Rune balance of input UTXO not fully covered by edicts—remainder automatically goes to first non-OP_RETURN output (called "pointer"). No explicit instruction = first output. This differs from EVM where unspecified funds stay with sender.
Example: you have UTXO with 1000 RUNE
Transaction with edict: send 300 RUNE → output 2
Automatically: 700 RUNE → output 1 (default pointer)
If output 1 = burn address—you accidentally burned 700 RUNE.
This—main reason tokens lost—not understanding pointer logic. Wallet development for Runes must explicitly configure pointer output to user's change address.
Indexing Runes Balances
Runes don't have standard RPC API in Bitcoin Core—need separate indexer. Options:
ord indexer—official, written in Rust. Requires full Bitcoin node + SSD for index (~50–100GB). Provides JSON API:
# Run with API server
ord --bitcoin-rpc-url http://localhost:8332 server --http-port 8080
# Address balance
curl http://localhost:8080/runes/balance/bc1q...
# Rune info
curl http://localhost:8080/rune/MYTOKEN•NAME
Hiro Ordinals API—hosted, paid, no need running own node. Suitable for MVP.
Unisat API—likewise, REST API with Runes support. Rate limited on free tier.
For production app with high load—own ord indexer mandatory. External API dependency creates single point of failure.
Typical Use Cases
Governance token for Bitcoin-native app—project wants governance token "native" on Bitcoin without custodial risks of ERC-20 wrapped BTC. Runes fits if governance mechanics simple (basically off-chain snapshot voting, Rune as weight).
In-game currency / reward token—game with Bitcoin-centric audience wants to reward users with Bitcoin tokens. Runes with open mint mechanics.
Meme tokens—most common use case 2024. Simple etching, open mint, listing on Magic Eden / UniSat.
Limitations to Understand
Runes—not smart contracts. No conditional transfers, no staking, no DEX without separate solution. All logic you're used to doing in Solidity impossible on-chain. Only possible: create, transfer, burn.
For DeFi logic atop Runes need either offchain matching (centralized orderbook) or separate L2/sidechain verifying Runes UTXO via Bitcoin SPV.
Timeline: etching and basic wallet—1–2 weeks. Full integration with indexer, marketplace functionality and custody solution—6–10 weeks.







