Practical · Worked examples · Byte counts computed, not estimated

Working with SRC-101

This page walks through the five operations with complete payloads, shows exactly how many bytes each one occupies on chain, works out what a term costs, and explains how to tell whether your registration actually happened. Every example is constructed for illustration. The addresses and the public key are well formed and pass every checksum, because a broken example teaches the wrong lesson, but they are derived from fixed text rather than from any key anyone holds. No transaction hash here names a transaction that exists.


1. Before you spend anything

Four things decide whether an SRC-101 transaction does what you meant, and all four are settled before you sign.

The namespace

A 64-character transaction hash. It fixes the price list, the addresses that may collect payment, the term granularity, and the mint window. There is no global list of namespaces in the protocol; you have to be given the hash.

The exact key set

Four of the five operations reject a payload with a single extra or missing key, and reject it silently. Run your payload through the validator before you build the transaction.

The payment output

Output 0 must pay one of the namespace's recipient addresses, and must pay at least the computed amount. One satoshi short is a failure with status IRV, and the fee is still spent.

The name, decoded

Names are compared after Base64 decoding and lower-casing. Confirm what your Base64 actually decodes to before you pay for it.

Two ways to lose money quietly

Both are specific to SRC-101 and neither exists in SRC-20. First, a malformed key set is excluded: your transaction confirms, your fee is spent, and no record of an attempt exists anywhere. Second, a multi-name mint drops taken names and proceeds: you pay for what you asked for and receive only the names that were free. Neither produces a warning.

2. Opening a namespace

A deploy is the largest payload in the protocol and the only one with no hash. All sixteen keys are mandatory.

{
  "p": "src-101",
  "op": "DEPLOY",
  "root": "btc",
  "name": "bitnames",
  "tick": "bitname",
  "lim": "1",
  "owner": "14caKuhoxA2wCj7JYjVJhgsuxTsbu5rzch",
  "rec": ["bc1qs2x9wz22tasf8pfejlcc3e8yhnzhv9qs2kgwls"],
  "pri": { "0": 200000, "3": 2000000, "4": 800000 },
  "desc": "Bitcoin name registry",
  "mintstart": "1732924800",
  "mintend": "0",
  "wla": "02bb9e14cd42f6487e94071cb25fe5bd6f42753de771be11d21c6a80b5f08cf4a4",
  "imglp": "https://names.example/i/",
  "imgf": "png",
  "idua": "1"
}
What each deploy field actually does
FieldEffect
rootLabels the namespace. Shorter than 32 characters, no special characters. Not used in any check
nameHuman-readable namespace name. Shorter than 32, no special characters
tickShorter than 32. Lower-cased and hashed with SHA3-256 into a stored tick_hash
limNot a limit. Only a presence sentinel. Must be greater than zero or the namespace is unusable
ownerStored. Not consulted by any authorisation or payment check
recThe addresses that may collect mint and renew payments. Order is not preserved
priPrice in satoshis per decoded name length. The key 0 is the fallback for any other length
descFree text, stored, never validated
mintstartUnix seconds. Minting is allowed when the block timestamp is at least this
mintendUnix seconds. Zero means no end, stored as 18446744073709551615
wlaCompressed secp256k1 public key. Signs discount authorisations for mints
imglpImage URL prefix. Shorter than 255
imgfImage file extension. Shorter than 32
iduaTerm granularity in years. Every requested term is rounded up to a multiple of this
Two ways to deploy a namespace nobody can use

Setting lim to "0" makes every later operation fail with ND, because the namespace lookup treats a zero limit as "not found". Setting idua to "0" makes every mint and renew fail with ITID, because the term rounding divides by it. Neither is caught at deploy time. The deploy is recorded as valid and the namespace is dead.

Also worth knowing before you deploy: you cannot mint in the same block. The in-block namespace lookup cannot resolve a namespace deployed in that same block, so a mint in the deploy's own block fails with ND. Wait for a confirmation.

3. Claiming a name

A mint at block 872200 or above. The tokenid value c2F0b3NoaQ== is Base64 for satoshi.

{
  "p": "src-101",
  "op": "MINT",
  "hash": "3a7f1c8e0b45d29a6f13e874c05b9d2e6a8f04713bc9e25d8a06f1c34b7e9c19",
  "toaddress": "bc1qs2x9wz22tasf8pfejlcc3e8yhnzhv9qs2kgwls",
  "tokenid": ["c2F0b3NoaQ=="],
  "dua": "2",
  "prim": "true",
  "sig": "",
  "coef": "1000"
}

Five details in that payload are easy to get wrong:

  • tokenid is a list even for one name. The string form is rejected with ITT.
  • prim is the string "true". The JSON boolean true discards the whole payload.
  • sig is present but empty. It is mandatory below block 872200 and, if you want the payload to work at every height, worth keeping.
  • coef is "1000", meaning full price. Any value below 1000 is ignored unless sig proves it.
  • toaddress is where the name goes, and it need not be you. Output 0 is where the money goes, and that must be a namespace recipient.

Below block 872200 the same mint additionally required an img list, and the entries had to match the derived URLs exactly:

  "img": ["https://names.example/i/satoshi.png"],

At and above 872200 an img you supply is discarded and the derived value is written instead, so there is no reason to include it.

Minting several names at once

  "tokenid": ["c2F0b3NoaQ==", "Yml0Y29pbg==", "bGFwc2Vk"],

The three decode to satoshi, bitcoin and lapsed. If bitcoin is already held with a live expiry, it is dropped and the other two are minted. The required payment is computed over the full submitted list, before the drop, so you pay for three and receive two. If all three were taken you would get status DM and nothing at all.

4. What a term costs

The arithmetic is short and entirely deterministic. Using the deploy above, whose pri is {"0": 200000, "3": 2000000, "4": 800000} and whose idua is 1:

How a mint payment is computed The requested term is rounded up to a multiple of the namespace granularity, giving a number of rounds. Each name is priced by its decoded length, defaulting to the zero-length entry. The sum is multiplied by the rounds, then by the effective coefficient divided by one thousand. dua requested rounded up to a multiple of idua rounds rounded dua divided by idua sum of price by decoded name length pri entry for that length, or the pri entry keyed 0, else status IRL required sum times rounds times coefficient over 1000 1000 unless a valid sig proves a lower coef output 0 value must be at least this
The renew path is the same picture with the coefficient step removed, and with the price looked up once, for the length of the single name being renewed.
Worked amounts, all in satoshis, against the example deploy
OperationName and lengthduaRoundsUnit priceRequired at output 0
mintsatoshi, 722200000, the fallback400000
mintabc, 31120000002000000
mintabcd, 4338000002400000
mint, three namessatoshi, bitcoin, lapsed11200000 each, all use the fallback600000
renewsatoshi, 711200000200000

When the granularity bites

If the namespace had "idua": "2" instead, a request for "dua": "3" would round up to 4, giving 2 rounds, and you would pay for four years while asking for three. The rounded value is what is stored, so you do get the four years. It is not a penalty, but it is not what you asked for either, and it is the value that goes into the discount signature.

The discount, if you have one

A namespace operator can pre-authorise a discount by signing over your specific mint with the namespace's whitelist key. You then set coef to the agreed value in parts per thousand and put the signature in sig. A coef of 250 means you pay a quarter. If the signature does not verify against either of the two accepted message forms, the mint fails outright with IRS; it does not fall back to full price. The exact message forms are in rule 51. Two traps: the dua in the signed message is the value after rounding, and coef and dua are serialised as strings.

There is no discount on renewal. The renew key set has no coef and no sig.

5. Pointing a name somewhere

A name with no records is just a reservation. setrecord is what makes it resolve.

{
  "p": "src-101",
  "op": "SETRECORD",
  "hash": "3a7f1c8e0b45d29a6f13e874c05b9d2e6a8f04713bc9e25d8a06f1c34b7e9c19",
  "tokenid": "c2F0b3NoaQ==",
  "type": "address",
  "data": { "btc": "bc1qs2x9wz22tasf8pfejlcc3e8yhnzhv9qs2kgwls" },
  "prim": "true"
}

Note that tokenid here is the plain string form, not a list. Only mint takes a list.

Two record kinds exist. "type": "address" writes a resolved address, and "type": "txt" writes free text. Any other type value produces neither and the operation fails with ID.

The Ethereum record is a signature, not an address

This is the least obvious part of SRC-101. Inside data, the eth member is not an Ethereum address. It is the hexadecimal signature of an Ethereum personal-sign message, and the message text is the hexadecimal, byte-reversed hash of the transaction your input 0 is spending. The indexer recovers the signer from that signature and stores the recovered address, without its 0x prefix.

  "data": { "eth": "<hex signature over the reversed previous txid>" },

The effect is that you cannot claim an Ethereum address you do not control, and the proof is bound to this specific Bitcoin transaction. The cost is that the signature has to be produced after you have chosen your inputs and before you sign the Bitcoin transaction.

The primary flag

Setting "prim": "true" marks this name as the primary one for its holder within this namespace. Two constraints follow. The payload must also carry an address record whose btc member equals the address funding input 0, else status IDB. And the flag is cleared from every other name that address holds in the namespace: one primary per address per namespace.

Records merge rather than replace. A setrecord that sets only txt leaves an existing address record in place. The only thing that clears records is a transfer.

6. Handing a name on

{
  "p": "src-101",
  "op": "TRANSFER",
  "hash": "3a7f1c8e0b45d29a6f13e874c05b9d2e6a8f04713bc9e25d8a06f1c34b7e9c19",
  "toaddress": "bc1qs2x9wz22tasf8pfejlcc3e8yhnzhv9qs2kgwls",
  "tokenid": "c2F0b3NoaQ=="
}

Five keys, no payment, no term change. The address funding input 0 must be the current owner, and the term must still be running. What the new owner receives is the name and the remainder of the existing term, with every record cleared and the primary flag off.

If you are buying a name from somebody

Check the expiry before you pay. A transfer does not extend the term, so a name with three weeks left is a name with three weeks left, and a renewal is a separate transaction with a separate payment. Check the records too: they will be empty the moment the transfer lands, and re-establishing them is your job, including the Ethereum proof.

There is also nothing in the protocol that makes a sale atomic. The transfer moves the name; whatever you paid for it moved separately, in a transaction the protocol knows nothing about. SRC-101 has no escrow primitive and no on-chain trade path, which is the same reason no Bitcoin Universe product implements one.

7. Keeping a name

{
  "p": "src-101",
  "op": "RENEW",
  "hash": "3a7f1c8e0b45d29a6f13e874c05b9d2e6a8f04713bc9e25d8a06f1c34b7e9c19",
  "tokenid": "c2F0b3NoaQ==",
  "dua": "1"
}

Five keys, and a payment at output 0 to a namespace recipient. The new expiry is the existing expiry plus the term, so renewing early costs you nothing in time. That is the important asymmetry with the expiry rule:

Renewal timingTwo outcomes

renewed one block early

Accepted
condition
block time < expiry
new expiry
old expiry plus term
time lost
none
owner
unchanged

renewed one block late

Refused, OE
condition
block time >= expiry
result
no state change, fee spent
grace period
none
the name
claimable by anyone

Because there is no grace period, and because the comparison uses the block timestamp rather than the time you broadcast, renewing at the last minute is a race against block production and against anyone watching for the name to lapse. Renew with real margin.

8. Byte framing and outputs

The framed buffer is a two-byte big-endian length, then stamp:, then the JSON. The declared length covers the prefix and the JSON and excludes itself. The counts below are computed from the exact payloads on this page.

Framing computed from the payloads above
PayloadJSON bytesDeclared lengthFramed totalMultisig data outputsP2WSH data outputs
DEPLOY43101b5 (437)439814
MINT, 872200 and above23200ee (238)24048
MINT with img278011c (284)28659
TRANSFER18700c1 (193)19547
SETRECORD22200e4 (228)23048
RENEW137008f (143)14535

The two output counts come from the carrier geometry. A multisig data output is a 1-of-3 bare multisig whose third key is a burn key; the two data-bearing keys contribute 31 bytes each, so 62 bytes per output. A P2WSH data output carries a 32-byte witness program, so 32 bytes per output, and only outputs after the first count, since output 0 is the payment. Every transaction also needs output 0 and usually a change output on top of the data outputs above.

The practical consequence: a deploy is expensive and a renew is cheap. The deploy above needs roughly three times the data of a renew. If you are choosing a carrier, note that P2WSH needs about twice as many outputs for the same payload but each is a cheap 32-byte program, while multisig packs more per output but uses uncompressible keys.

If you use P2WSH before block 940000

The decoder reads output 0's value on the P2WSH branch only from block 940000. Below that height a P2WSH-carried mint or renew is seen as paying zero and fails with IRV, no matter what you actually paid. deploy, transfer and setrecord are unaffected because they never read that value. Use bare multisig for paid operations below that height.

9. Reading a result

After your transaction confirms, there are three things it might have become, and only one of them is what you wanted.

Valid
The register changed. Your name appears with your owner address and an expiry.
Invalid, with a status code
The operation was understood and refused. There is a record with a code such as IRV or OE, and the status table explains it. Nothing in the register changed.
Excluded
The payload was never an SRC-101 operation. No record, no status code, nothing to look up. If you cannot find your transaction at all in an SRC-101 view, this is almost certainly what happened, and the cause is almost certainly the key set or a field type.

The most common causes of exclusion, in the order worth checking:

  1. An extra or missing key. Compare against the key set table character by character.
  2. prim written as a JSON boolean rather than a string.
  3. A numeric field written as a decimal string, such as "dua": "1.5".
  4. A tokenid whose Base64 is not a multiple of four characters long, or which uses the URL-safe alphabet.
  5. Below block 872200, a mint missing img.

10. What Bitcoin Universe supports

Taken from the ecosystem capability registry, which is generated from the Core protocol registry and records what the product code implements.

SRC-101 support by surface
SurfaceActions
Coreview, discover, view-collection, view-activity, view-transaction
Walletview, send, receive
Inscribemint, transfer
MarketplaceNone recorded. The registry has no marketplace entry for SRC-101 at all

Three consequences follow directly from that table, and it is worth being blunt about all three.

  • No product implements a trade path. There is no list, no buy, no offer, no settle. This is not a narrowed marketplace with unsupported actions recorded against it, as SRC-20 has; there is no marketplace entry for the protocol at all.
  • Inscribe cannot deploy, renew or write records. Its two recorded actions are mint and transfer. Opening a namespace, renewing a term and setting a record are operations you would need to construct and broadcast yourself.
  • No confirmation, freshness or reorg policy is recorded. The registry entry for SRC-101 carries a null source of truth, a null indexer, and null policies for all three. Where a protocol like SRC-20 can point at a stated policy, SRC-101 cannot.

This site states what the registry records and nothing beyond it. A capability that is not in the registry is not claimed here, and support recorded in the registry is a statement about product code, not a promise that a feature is enabled in production.

11. Safety

Names can look identical

Names are decoded from Base64 and lower-cased, and the character rules reject spaces and format characters but not homoglyphs. Two names can render the same and be different registrations. Compare the decoded bytes, not the rendering.

The namespace is the whole context

The same name in a different namespace is a different registration with a different owner. A name shown without its namespace hash tells you nothing.

Expiry is unforgiving

No grace period, no redemption. Note your expiry, renew early, and remember that a transfer does not extend it.

Records are claims, one of them proved

A txt record is whatever the owner wrote. A btc record is a well-formed address, not a proved one. Only the eth record carries a signature the indexer verifies. Do not treat the others as attestations.

Nobody can undo a broadcast

A confirmed transaction cannot be reversed, a mint cannot be un-minted, and no operator can move a name back. Verify the namespace, the decoded name, the recipient and the payment amount before you sign.

There is no consensus safety net

SRC-101 contributes nothing to the block ledger hash and has no cross-implementation check. Two explorers can disagree about who owns a name and nothing will flag it. Where the answer matters, read more than one.