From 591986404e46cce119e38875eb9d36facd19fd35 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 23 Aug 2025 08:57:04 +0000 Subject: [PATCH] Add example scripts demonstrating Ordinals inscription techniques Co-authored-by: brian --- examples/basic-json.ts | 50 +++++++++++++++++++++ examples/compressed-brotli.ts | 23 ++++++++++ examples/html-referencing-js.ts | 31 +++++++++++++ examples/metadata-followup.ts | 42 +++++++++++++++++ examples/multi-inscription-assets.ts | 67 ++++++++++++++++++++++++++++ examples/parse-witness.ts | 11 +++++ examples/pointer-delegate.ts | 30 +++++++++++++ 7 files changed, 254 insertions(+) create mode 100644 examples/basic-json.ts create mode 100644 examples/compressed-brotli.ts create mode 100644 examples/html-referencing-js.ts create mode 100644 examples/metadata-followup.ts create mode 100644 examples/multi-inscription-assets.ts create mode 100644 examples/parse-witness.ts create mode 100644 examples/pointer-delegate.ts diff --git a/examples/basic-json.ts b/examples/basic-json.ts new file mode 100644 index 0000000..b6daf63 --- /dev/null +++ b/examples/basic-json.ts @@ -0,0 +1,50 @@ +import * as btc from '@scure/btc-signer'; +import { hex, utf8 } from '@scure/base'; +import * as ordinals from '../src/index.ts'; + +// Single JSON inscription +(async () => { + const TESTNET = btc.utils.TEST_NETWORK; + const privKey = hex.decode('0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a'); + const pubKey = btc.utils.pubSchnorr(privKey); + const customScripts = [ordinals.OutOrdinalReveal]; + + const inscription: ordinals.Inscription = { + tags: { + contentType: 'application/json', + }, + body: utf8.decode(JSON.stringify({ type: 'example', format: 'json', version: 1 })), + }; + + const revealPayment = btc.p2tr( + undefined, + ordinals.p2tr_ord_reveal(pubKey, [inscription]), + TESTNET, + false, + customScripts + ); + + // Fund this address before revealing + console.log('Address', revealPayment.address); + + const changeAddr = revealPayment.address; + const revealAmount = 2000n; + const fee = 500n; + + const tx = new btc.Transaction({ customScripts }); + tx.addInput({ + ...revealPayment, + // Replace with real funding UTXO txid + txid: '75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858', + index: 0, + witnessUtxo: { script: revealPayment.script, amount: revealAmount }, + }); + tx.addOutputAddress(changeAddr, revealAmount - fee, TESTNET); + tx.sign(privKey, undefined, new Uint8Array(32)); + tx.finalize(); + + const txHex = hex.encode(tx.extract()); + console.log('txHex', txHex); + console.log('vsize', tx.vsize); + console.log('parsed', ordinals.parseWitness(tx.inputs[0].finalScriptWitness)); +})(); \ No newline at end of file diff --git a/examples/compressed-brotli.ts b/examples/compressed-brotli.ts new file mode 100644 index 0000000..0d3a123 --- /dev/null +++ b/examples/compressed-brotli.ts @@ -0,0 +1,23 @@ +import * as btc from '@scure/btc-signer'; +import { hex, utf8 } from '@scure/base'; +import * as ordinals from '../src/index.ts'; + +// Brotli-compressed body with contentEncoding: 'br' +(async () => { + const TESTNET = btc.utils.TEST_NETWORK; + const privKey = hex.decode('0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a'); + const pubKey = btc.utils.pubSchnorr(privKey); + const customScripts = [ordinals.OutOrdinalReveal]; + + // Pretend we have pre-compressed content (brotli) as bytes. + // In practice, compress the utf8 bytes externally and put result here. + const compressedBody = new Uint8Array([0x1b, 0x2a, 0x3c]); + + const inscription: ordinals.Inscription = { + tags: { contentType: 'application/json', contentEncoding: 'br' }, + body: compressedBody, + }; + + const revealPayment = btc.p2tr(undefined, ordinals.p2tr_ord_reveal(pubKey, [inscription]), TESTNET, false, customScripts); + console.log('Address', revealPayment.address); +})(); \ No newline at end of file diff --git a/examples/html-referencing-js.ts b/examples/html-referencing-js.ts new file mode 100644 index 0000000..a34fdad --- /dev/null +++ b/examples/html-referencing-js.ts @@ -0,0 +1,31 @@ +import * as btc from '@scure/btc-signer'; +import { hex, utf8 } from '@scure/base'; +import * as ordinals from '../src/index.ts'; + +// HTML referencing a JS inscription +(async () => { + const TESTNET = btc.utils.TEST_NETWORK; + const privKey = hex.decode('0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a'); + const pubKey = btc.utils.pubSchnorr(privKey); + const customScripts = [ordinals.OutOrdinalReveal]; + + // Step 1: JS inscription + const jsInscription: ordinals.Inscription = { + tags: { contentType: 'text/javascript' }, + body: utf8.decode(`console.log('hi from onchain script');`), + }; + const jsReveal = btc.p2tr(undefined, ordinals.p2tr_ord_reveal(pubKey, [jsInscription]), TESTNET, false, customScripts); + console.log('JS Address', jsReveal.address); + + // Suppose after reveal we get js inscription id + const jsInscriptionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaai0'; + + // Step 2: HTML inscription that references the JS + const html = `

Hello

`; + const htmlInscription: ordinals.Inscription = { + tags: { contentType: 'text/html;charset=utf-8' }, + body: utf8.decode(html), + }; + const htmlReveal = btc.p2tr(undefined, ordinals.p2tr_ord_reveal(pubKey, [htmlInscription]), TESTNET, false, customScripts); + console.log('HTML Address', htmlReveal.address); +})(); \ No newline at end of file diff --git a/examples/metadata-followup.ts b/examples/metadata-followup.ts new file mode 100644 index 0000000..5e949dc --- /dev/null +++ b/examples/metadata-followup.ts @@ -0,0 +1,42 @@ +import * as btc from '@scure/btc-signer'; +import { hex, utf8 } from '@scure/base'; +import * as ordinals from '../src/index.ts'; + +// Metadata flow: first inscribe base asset, then inscribe metadata referencing parent +(async () => { + const TESTNET = btc.utils.TEST_NETWORK; + const privKey = hex.decode('0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a'); + const pubKey = btc.utils.pubSchnorr(privKey); + const customScripts = [ordinals.OutOrdinalReveal]; + + // Step 1: Base asset inscription (e.g., image) + const basePng = new Uint8Array([0x89, 0x50, 0x4e, 0x47]); + const baseInscription: ordinals.Inscription = { + tags: { contentType: 'image/png' }, + body: basePng, + }; + + const baseReveal = btc.p2tr(undefined, ordinals.p2tr_ord_reveal(pubKey, [baseInscription]), TESTNET, false, customScripts); + console.log('Base Address', baseReveal.address); + + // Fund and reveal base inscription, then obtain txid/index externally. + // For demo purposes, we reference an example id here. + const baseInscriptionId = '75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858i0'; + + // Step 2: Metadata inscription that references the base via parent and includes metadata + const metadataInscription: ordinals.Inscription = { + tags: { + contentType: 'application/json', + parent: baseInscriptionId, + metadata: { name: 'Logo v1', attributes: [{ trait_type: 'theme', value: 'purple' }] }, + metaprotocol: 'com.example/meta-v1', + note: 'Metadata referencing a parent inscription', + }, + body: utf8.decode(JSON.stringify({ type: 'metadata', schema: 'example-v1' })), + }; + + const metaReveal = btc.p2tr(undefined, ordinals.p2tr_ord_reveal(pubKey, [metadataInscription]), TESTNET, false, customScripts); + console.log('Metadata Address', metaReveal.address); + + // Fund and reveal metaReveal, then broadcast. +})(); \ No newline at end of file diff --git a/examples/multi-inscription-assets.ts b/examples/multi-inscription-assets.ts new file mode 100644 index 0000000..42b303f --- /dev/null +++ b/examples/multi-inscription-assets.ts @@ -0,0 +1,67 @@ +import * as btc from '@scure/btc-signer'; +import { hex, utf8 } from '@scure/base'; +import * as ordinals from '../src/index.ts'; + +// Multi-inscription in a single reveal: manifest + js + binary asset +(async () => { + const TESTNET = btc.utils.TEST_NETWORK; + const privKey = hex.decode('0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a'); + const pubKey = btc.utils.pubSchnorr(privKey); + const customScripts = [ordinals.OutOrdinalReveal]; + + const manifest = { + tags: { contentType: 'application/json' }, + body: utf8.decode(JSON.stringify({ + name: 'multi-asset-demo', + version: 1, + files: [ + { name: 'app.js', type: 'text/javascript' }, + { name: 'logo.png', type: 'image/png' }, + ], + })), + } satisfies ordinals.Inscription; + + const appJs = { + tags: { contentType: 'text/javascript' }, + body: utf8.decode(`console.log('hello from onchain js');`), + } satisfies ordinals.Inscription; + + // Example binary content (not a real PNG; replace in real use) + const logoPngBytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47]); + const logoPng = { + tags: { contentType: 'image/png' }, + body: logoPngBytes, + } satisfies ordinals.Inscription; + + const inscriptions: ordinals.Inscription[] = [manifest, appJs, logoPng]; + + const revealPayment = btc.p2tr( + undefined, + ordinals.p2tr_ord_reveal(pubKey, inscriptions), + TESTNET, + false, + customScripts + ); + + console.log('Address', revealPayment.address); + + const changeAddr = revealPayment.address; + const revealAmount = 4000n; + const fee = 800n; + + const tx = new btc.Transaction({ customScripts }); + tx.addInput({ + ...revealPayment, + txid: '75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858', + index: 0, + witnessUtxo: { script: revealPayment.script, amount: revealAmount }, + }); + tx.addOutputAddress(changeAddr, revealAmount - fee, TESTNET); + tx.sign(privKey, undefined, new Uint8Array(32)); + tx.finalize(); + + const txHex = hex.encode(tx.extract()); + console.log('txHex', txHex); + console.log('vsize', tx.vsize); + console.log('parsed', ordinals.parseWitness(tx.inputs[0].finalScriptWitness)); +})(); \ No newline at end of file diff --git a/examples/parse-witness.ts b/examples/parse-witness.ts new file mode 100644 index 0000000..06d3900 --- /dev/null +++ b/examples/parse-witness.ts @@ -0,0 +1,11 @@ +import * as btc from '@scure/btc-signer'; +import { hex } from '@scure/base'; +import * as ordinals from '../src/index.ts'; + +// Parse inscriptions from a reveal tx hex +(() => { + const txHex = '0200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff01dc05000000000000225120a6c9676014f93c456161b946a7251f680b5ce31b52cf16b687b78f40bc4013ad03400e0888a69181fb2745c81cb595bdc1966e8b974a1c06b944e5f2be655af01fe5e1cc9626d6a97041a4b18654e20f7bd88a6ab1d12f6518b03264a19493946a7e7020f76a39d05686e34a4420897e359371836145dd3973e3982568b60f8433adde6eac0063036f72640101106170706c69636174696f6e2f6a736f6e00327b22736f6d65223a312c2274657374223a322c22696e736372697074696f6e223a747275652c22696e223a226a736f6e227d6821c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac000000000'; + const tx = btc.Transaction.fromRaw(hex.decode(txHex)); + const inscriptions = ordinals.parseWitness(tx.inputs[0].finalScriptWitness); + console.log('inscriptions', inscriptions); +})(); \ No newline at end of file diff --git a/examples/pointer-delegate.ts b/examples/pointer-delegate.ts new file mode 100644 index 0000000..5f2fa58 --- /dev/null +++ b/examples/pointer-delegate.ts @@ -0,0 +1,30 @@ +import * as btc from '@scure/btc-signer'; +import { hex, utf8 } from '@scure/base'; +import * as ordinals from '../src/index.ts'; + +// Demonstrate pointer and delegate tags +(async () => { + const TESTNET = btc.utils.TEST_NETWORK; + const privKey = hex.decode('0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a'); + const pubKey = btc.utils.pubSchnorr(privKey); + const customScripts = [ordinals.OutOrdinalReveal]; + + // Pointer example (point to 0th output by specifying pointer=0) + const pointerInscription: ordinals.Inscription = { + tags: { contentType: 'text/plain;charset=utf-8', pointer: 0n }, + body: utf8.decode('This is pointed to output 0'), + }; + + const pointerReveal = btc.p2tr(undefined, ordinals.p2tr_ord_reveal(pubKey, [pointerInscription]), TESTNET, false, customScripts); + console.log('Pointer Address', pointerReveal.address); + + // Delegate example: referencing a previous inscription id + const someInscriptionId = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbi1'; + const delegateInscription: ordinals.Inscription = { + tags: { contentType: 'application/json', delegate: someInscriptionId }, + body: utf8.decode(JSON.stringify({ delegated: true })), + }; + + const delegateReveal = btc.p2tr(undefined, ordinals.p2tr_ord_reveal(pubKey, [delegateInscription]), TESTNET, false, customScripts); + console.log('Delegate Address', delegateReveal.address); +})(); \ No newline at end of file