OP_RETURN
Complete examples for embedding arbitrary data on the BSV blockchain using OP_RETURN outputs.
Overview
OP_RETURN is a Bitcoin script opcode that marks transaction outputs as provably unspendable, making it ideal for storing arbitrary data on the blockchain. This feature is widely used for timestamping, document verification, application protocols, and on-chain data storage. BSV's unbounded block size makes it practical to store substantial amounts of data using OP_RETURN.
Related SDK Components:
Simple OP_RETURN Data Storage
import { Transaction, PrivateKey, P2PKH, Script, OP } from '@bsv/sdk'
/**
* Simple OP_RETURN Data Storage
*
* Store arbitrary data in OP_RETURN outputs
*/
class OpReturnDataStore {
/**
* Create an OP_RETURN script with data
*/
createOpReturnScript(data: Buffer | Buffer[]): Script {
const script = new Script()
// Add OP_FALSE (OP_0) and OP_RETURN
script.writeOpCode(OP.OP_FALSE)
script.writeOpCode(OP.OP_RETURN)
// Add data chunks
const dataArray = Array.isArray(data) ? data : [data]
for (const chunk of dataArray) {
script.writeBin(chunk)
}
return script
}
/**
* Store text data on blockchain
*/
async storeText(
senderKey: PrivateKey,
text: string,
utxo: {
txid: string
vout: number
satoshis: number
script: Script
}
): Promise<Transaction> {
try {
const tx = new Transaction()
// Add input
tx.addInput({
sourceTXID: utxo.txid,
sourceOutputIndex: utxo.vout,
unlockingScriptTemplate: new P2PKH().unlock(senderKey),
sequence: 0xffffffff
})
// Add OP_RETURN output with text data
const textBuffer = Buffer.from(text, 'utf8')
tx.addOutput({
satoshis: 0, // OP_RETURN outputs are provably unspendable
lockingScript: this.createOpReturnScript(textBuffer)
})
// Add change output
const fee = 500
const change = utxo.satoshis - fee
if (change > 546) {
tx.addOutput({
satoshis: change,
lockingScript: new P2PKH().lock(senderKey.toPublicKey().toHash())
})
}
await tx.sign()
console.log('Text stored on blockchain')
console.log('Transaction ID:', tx.id('hex'))
console.log('Data size:', textBuffer.length, 'bytes')
return tx
} catch (error) {
throw new Error(`Failed to store text: ${error.message}`)
}
}
/**
* Store multiple data fields
*/
async storeMultipleFields(
senderKey: PrivateKey,
fields: string[],
utxo: {
txid: string
vout: number
satoshis: number
script: Script
}
): Promise<Transaction> {
try {
const tx = new Transaction()
tx.addInput({
sourceTXID: utxo.txid,
sourceOutputIndex: utxo.vout,
unlockingScriptTemplate: new P2PKH().unlock(senderKey),
sequence: 0xffffffff
})
// Convert fields to buffers
const dataBuffers = fields.map(field => Buffer.from(field, 'utf8'))
// Add OP_RETURN output
tx.addOutput({
satoshis: 0,
lockingScript: this.createOpReturnScript(dataBuffers)
})
// Add change
const fee = 500
const change = utxo.satoshis - fee
if (change > 546) {
tx.addOutput({
satoshis: change,
lockingScript: new P2PKH().lock(senderKey.toPublicKey().toHash())
})
}
await tx.sign()
console.log('Multiple fields stored')
console.log('Fields:', fields.length)
console.log('Transaction ID:', tx.id('hex'))
return tx
} catch (error) {
throw new Error(`Failed to store fields: ${error.message}`)
}
}
/**
* Parse OP_RETURN data from script
*/
parseOpReturnData(script: Script): Buffer[] {
const chunks = script.chunks
const data: Buffer[] = []
// Skip OP_FALSE (index 0) and OP_RETURN (index 1)
for (let i = 2; i < chunks.length; i++) {
if (chunks[i].buf) {
data.push(chunks[i].buf)
}
}
return data
}
/**
* Extract OP_RETURN data from transaction
*/
extractData(tx: Transaction): Buffer[] | null {
for (const output of tx.outputs) {
const chunks = output.lockingScript.chunks
// Check if this is an OP_RETURN output
if (chunks.length >= 2 && chunks[1].opCodeNum === OP.OP_RETURN) {
return this.parseOpReturnData(output.lockingScript)
}
}
return null
}
}
/**
* Usage Example
*/
async function simpleOpReturnExample() {
const dataStore = new OpReturnDataStore()
const privateKey = PrivateKey.fromRandom()
const utxo = {
txid: 'funding-tx-id...',
vout: 0,
satoshis: 50000,
script: new P2PKH().lock(privateKey.toPublicKey().toHash())
}
// Store simple text
const tx1 = await dataStore.storeText(
privateKey,
'Hello, BSV!',
utxo
)
// Store multiple fields
const tx2 = await dataStore.storeMultipleFields(
privateKey,
['app_name', 'my_app', 'version', '1.0.0'],
utxo
)
// Extract data
const extractedData = dataStore.extractData(tx1)
if (extractedData) {
console.log('Extracted text:', extractedData[0].toString('utf8'))
}
}Document Timestamping
JSON Data Storage
File Metadata Registry
Related Examples
See Also
SDK Components:
Script - Script creation and manipulation
Transaction - Transaction building
Transaction Output - Output management
Learning Paths:
Last updated
