§ 06 · Script
OP_RETURN
An output for data that can never be spent
On this page 7 sections
An OP_RETURNOP_RETURN: An output that starts with OP_RETURN. It can carry data but can never be spent, so it never joins the set of unspent outputs. output is a note pinned to a transaction. It carries bytes, usually a small amount of data, and no one can ever spend it.
The trick is in the first byte. 0x6a is the opcode OP_RETURN, which ends a script as failed. A locking script that starts with it can never be satisfied, so the output is provably dead from the moment it’s created. Nodes know that, and never bother remembering it.
Try it
Pick the OP_RETURN example and hover its middle output.
What does an OP_RETURN output look like?
Here is a real one, in a transaction from block 130,809:
| Field | Bytes | Value |
|---|---|---|
| Version version | 4 @0 | 2 int32 little-endian. Pearl wallets and pearld coinbases use version 1; relay policy accepts 1-2. |
| Marker marker | 1 @4 | 0x00 Segwit marker (BIP-144): says witness data follows the outputs. |
| Flag flag | 1 @5 | 0x01 Witness flag; 0x01 is the only value Pearl accepts. |
| Input count vin.length | 1 @6 | 1 CompactSize number of inputs. |
| Previous txid vin[0].txid | 32 @7 | ad4e340fe4d41a0842ab0a102222fc07a8052afe171ba7e7cbc3dfacb83fe845 The transaction being spent, stored byte-reversed (internal order); shown here in normal display order. |
| Output index vin[0].vout | 4 @39 | 2 Which output of that transaction is spent (uint32 LE). |
| scriptSig length vin[0].scriptSig.length | 1 @43 | 0 Always 0 for non-coinbase inputs in Pearl: every spend uses the witness instead. |
| Sequence vin[0].sequence | 4 @44 | 0xfffffffd signals opt-in replace-by-fee (BIP-125). |
| Output count vout.length | 1 @48 | 3 CompactSize number of outputs. |
| Amount vout[0].value | 8 @49 | 48,047,555,000 grains (480.47555000 PRL) int64 little-endian, in grains (1 PRL = 100,000,000 grains). |
| Script length vout[0].scriptPubKey.length | 1 @57 | 34 CompactSize length of the locking script. |
| Locking script vout[0].scriptPubKey | 34 @58 | prl1p3995s5kmdgjrldc9yd450kjuevvv8w5fd3dssw9e6hpwjw3wln6qstfvct OP_1 OP_PUSHBYTES_32 <output key Q>: a Taproot output. |
| Amount vout[1].value | 8 @92 | 0 grains (0.00000000 PRL) int64 little-endian, in grains (1 PRL = 100,000,000 grains). |
| Script length vout[1].scriptPubKey.length | 1 @100 | 39 CompactSize length of the locking script. |
| Locking script vout[1].scriptPubKey | 39 @101 | OP_RETURN data (unspendable) OP_RETURN data carrier: provably unspendable, never enters the UTXO set. |
| Amount vout[2].value | 8 @140 | 6,422,205,178,247 grains (64222.05178247 PRL) int64 little-endian, in grains (1 PRL = 100,000,000 grains). |
| Script length vout[2].scriptPubKey.length | 1 @148 | 34 CompactSize length of the locking script. |
| Locking script vout[2].scriptPubKey | 34 @149 | prl1p5f450a5540efskxv050tgscelscuztut6zfaqssq8vnlnw53wvdsmw4yvs OP_1 OP_PUSHBYTES_32 <output key Q>: a Taproot output. |
| Witness items witness[0].length | 1 @183 | 1 Number of witness stack items for input 0. |
| Schnorr signature length witness[0][0].length | 1 @184 | 64 CompactSize length of this witness item. |
| Schnorr signature witness[0][0] | 64 @185 | 834fa0d9eb91ac59fcf21e911b43eca33608b6b8393c6a304a959cfaeebbb505ff38d095905be416e78c70ae3fe6d018d823d6ff36467f3199bea33f91976c71 64-byte BIP-340 signature (R.x ‖ s) with SIGHASH_DEFAULT: a Taproot key-path spend. |
| Locktime locktime | 4 @249 | 0 0: no time lock. |
| Field | Size | Description |
|---|---|---|
| amount | 8 | 0000000000000000: 0 grains. |
| script length | 1 | 27 = 39 bytes. |
| OP_RETURN | 1 | 6a: this output can never be spent. |
| push 37 | 1 | 25: the next 37 bytes are data. |
| tag | 4 | 50524c55, ASCII "PRLU". |
| data | 33 | 01 then 32 bytes, be1688fc…33d9ae87. |
What does it mean? Nobody has said. Twelve outputs with this “PRLU” tag appeared in 200 recent blocks, all from one large address, along with one tagged “PRLB” whose payload looks like a 20-byte Ethereum-style address padded with zeros. They may be bridge or custody messages; neither tag appears in Pearl’s own software. Pearl doesn’t care: to the protocol it’s 39 bytes nobody can spend.
What are the rules?
- Size
- 1 byte or more
- Type
- Locking script
- Format
- 6a ‖ anything
Pearl’s consensus rule is short:
func isNullDataScript(script []byte) bool {
return len(script) >= 1 && script[0] == OP_RETURN
} Anything after 0x6a is allowed: pushes, other opcodes, bytes that don’t even parse. Pearl’s own comment notes this is looser than Bitcoin Core, which requires the rest to be data pushes.
| Consensus (blocks) | Policy (relay) | |
|---|---|---|
| Size | No limit beyond the transaction’s (1,000,000 vB) | No byte cap; a standard transaction is at most 100,000 vB |
| Outputs per transaction | Any number | At most one |
Contents after 6a |
Anything | Anything |
| Amount | Any (a non-zero amount is burned) | Any; exempt from the dust limit |
These were tested against Pearl’s own code: a bare 6a, an 81-byte push, a 1,000-byte push, 6a ac (OP_RETURN then OP_CHECKSIG) and a 1-PRL burn all pass both consensus and policy. Two OP_RETURN outputs in one transaction pass consensus but fail policy.
How is more data written?
The push opcode after 6a grows with the data, exactly as in any script:
| Data size | Script starts | Example |
|---|---|---|
| 1 to 75 bytes | one length byte, 01–4b |
6a 25 … for the 37-byte PRLU payload |
| 76 to 255 bytes | 4c (OP_PUSHDATA1) + 1 length byte |
6a 4c 51 …: 81 bytes, an 84-byte script |
| 256 to 65,535 bytes | 4d (OP_PUSHDATA2) + 2 length bytes, little-endian |
6a 4d e803 …: 1,000 bytes, a 1,004-byte script |
Both of those larger examples pass Pearl’s consensus and relay checks. Since the data is never executed, the 520-byte limit on stack items doesn’t apply to it.
What does it cost?
Output bytes are base bytes, counted in full. The PRLU output is 8 (amount) + 1 (length) + 39 (script) = 48 bytes, so it adds 48 vbytes to its transaction: 48 grains at the minimum relay fee, or about 2,400 at the 50 grains per vbyte most transactions paid that day.
Why do nodes forget them?
Because they can’t be spent. Every other output joins the UTXO setUTXO: An output that has not been spent yet: a chunk of PRL waiting to be used. Your balance is the total of the UTXOs your keys can unlock. and stays until someone spends it, which may be never. An OP_RETURN output is skipped when the set is updated, so data stored this way costs nodes disk space in the block history, but not in the set of live coins that every node must keep fast.
Data still isn’t free. Output bytes are base bytes, counted in full toward vsize, so each byte of OP_RETURN data costs at least a grain at the minimum relay fee.
Where else does OP_RETURN appear?
- The witness commitment. Every coinbase in a block with witness data has an output
6a 24 aa21a9edfollowed by 32 bytes, committing to all the block’s witnesses. In 200 recent blocks there were 192 of them. See coinbase. - The genesis block. Pearl’s very first coinbase pays 0 grains to a bare
6a, its only output. There was no premine to spend. - Merged mining. Some coinbases carry a tag, such as
OP_RETURN "NAC*"plus 32 bytes from one merged-mining pool. Coinbases skip relay policy, so they can carry this alongside the witness commitment. - Message signing. Oyster’s
signmessagesigns a small virtual transaction whose output isOP_RETURN <your message>. It never goes on chain.
Make a guess
Could you send 1 PRL to an OP_RETURN output by mistake and get it back later?
Think of your answer first, then check it.
Answer No. It's gone for good.
Consensus allows a non-zero amount on an OP_RETURN output, and relay policy accepts it (it’s exempt from dust). But the output can never be spent, so those grains are destroyed. Wallets pay to addresses, and an OP_RETURN output has no address, so this only happens with hand-built transactions.