Stamps (Multisig)
The most durable way to inscribe data on Bitcoin. Unprunable, permanent, and resistant to network changes.
What are Stamps?
Stamps is a protocol that embeds data inside bare 1-of-3 multisig outputs. Unlike OP_RETURN, these outputs are part of the UTXO set — the database of unspent coins that every full node must maintain. This makes Stamps inscriptions truly unprunable: no node can discard them without breaking consensus.
The data is encrypted using ARC4 (a stream cipher) with the first input’s transaction ID as the key. This isn’t for security — anyone who knows the txid can decrypt it. It’s a convention of the Stamps protocol to make the data appear random in the blockchain, reducing the chance of false pattern matches.
Encoding Pipeline
The encoding process has several stages:
1. Your message (UTF-8)
"I love you forever"
2. Base64 encode
"SSBsb3ZlIHlvdSBmb3JldmVy"
3. Add "stamp:" prefix
"stamp:SSBsb3ZlIHlvdSBmb3JldmVy"
4. Add 2-byte big-endian length frame
[0x00, 0x22, ...payload_bytes]
5. ARC4 encrypt using first input txid as key
[encrypted_bytes...]
6. Split into 31-byte chunks
[chunk_0, chunk_1, ...]Multisig Output Structure
Each pair of data chunks becomes a 1-of-3 bare multisig output. The three public keys in each output serve different purposes:
OP_1 (0x51)
<data_pubkey_1> 33 bytes — chunk N embedded in bytes [1..31]
<data_pubkey_2> 33 bytes — chunk N+1 embedded in bytes [1..31]
<keyburn_key> 33 bytes — unspendable (all 0x02 bytes)
OP_3 (0x53)
OP_CHECKMULTISIG (0xae)
Total script size: 105 bytes per multisig outputThe “data pubkeys” are crafted by embedding 31 bytes of data into positions [1..31] of a 33-byte compressed public key. Position [0] is the prefix (0x02 or 0x03) and position [32] is a nonce. The code tries different prefix/nonce combinations until the result is a valid point on the secp256k1 curve.
The keyburn key (0x020202...02) is unspendable — no one knows its private key. This ensures the 7,800 sats sent to each multisig output are permanently locked.
Transaction Structure
Input:
- Server wallet UTXO (P2WPKH)
Outputs:
1. Recipient address (7,800 sats dust)
2. Bare multisig (chunks 0-1) (7,800 sats dust, burned)
3. Bare multisig (chunks 2-3) (7,800 sats dust, burned)
...N multisig outputs...
N+1. Change to server wallet (remaining balance)Decoding a Stamps Inscription
// 1. Fetch the transaction
const res = await fetch("https://mempool.space/api/tx/<txid>");
const tx = await res.json();
// 2. Find bare multisig outputs
// Script starts with 0x51 (OP_1) and ends with 0x53ae (OP_3 OP_CHECKMULTISIG)
const multisigOutputs = tx.vout.filter(
(o) => o.scriptpubkey.startsWith("51") && o.scriptpubkey.endsWith("53ae")
);
// 3. Extract data pubkeys (skip the keyburn key)
const chunks = [];
for (const out of multisigOutputs) {
const script = out.scriptpubkey;
let offset = 2; // skip OP_1
const keys = [];
while (offset < script.length - 4) {
if (script.slice(offset, offset + 2) === "21") { // OP_PUSHBYTES_33
offset += 2;
keys.push(script.slice(offset, offset + 66));
offset += 66;
} else break;
}
// First 2 keys are data keys, 3rd is keyburn
for (let i = 0; i < Math.min(keys.length - 1, 2); i++) {
const keyBytes = hexToBytes(keys[i]);
chunks.push(keyBytes.slice(1, 32)); // strip prefix byte
}
}
// 4. Concatenate and ARC4 decrypt
const encrypted = concatBytes(chunks);
const arc4Key = hexToBytes(tx.vin[0].txid);
const decrypted = arc4Decrypt(arc4Key, encrypted);
// 5. Parse length frame and stamp: prefix
const payloadLen = (decrypted[0] << 8) | decrypted[1];
const payload = new TextDecoder().decode(
decrypted.slice(2, 2 + payloadLen)
);
// 6. Remove "stamp:" prefix and base64 decode
const b64 = payload.slice(6); // remove "stamp:"
const message = atob(b64);
console.log(message); // "I love you forever"Cost Breakdown
Estimated vsize (80-char message, 2 multisig outputs):
Overhead: 11 vB
P2WPKH input: 68 vB
Recipient output: 31 vB
2x multisig outputs: 210 vB (105 each)
Change output: 31 vB
─────────────────────────────
Total: 351 vB
At 10 sat/vB fee rate:
Miner fee: 3,510 sats
Recipient dust: 7,800 sats
Multisig dust: 15,600 sats (7,800 x 2, permanently burned)
Total cost: 26,910 sats (~$17.49 at $65k BTC)Trade-offs
Why “Stamps”?
The name comes from the Bitcoin Stamps protocol, which was originally designed for embedding images (like NFTs) on Bitcoin in a way that cannot be pruned. EternalChain uses the same encoding technique for text inscriptions, giving your words the same level of permanence as Bitcoin Stamps art.