Skip to content

Instantly share code, notes, and snippets.

@msinkec
Created January 19, 2024 08:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save msinkec/886ded5bff9a6b5fc0bf1f40530531e6 to your computer and use it in GitHub Desktop.
Save msinkec/886ded5bff9a6b5fc0bf1f40530531e6 to your computer and use it in GitHub Desktop.
Ordinal w/ custom script
// @ts-ignore
import btc = require('bitcore-lib');
import axios from 'axios';
import { Address, Tap, Tx } from '@cmdcode/tapscript' // Requires node >= 19
async function fetchP2WPKHUtxos(address: btc.Address): Promise<any[]> {
const url = `https://blockstream.info/api/address/${address.toString()}/utxo`;
let res = []
try {
// Make a GET request to the URL using axios
const response = await axios.get(url);
// The response data is available in `response.data`
console.log(address.toString())
if (response.data) {
for (let i = 0; i < response.data.length; i++) {
const e = response.data[i]
const utxo = {
address: address.toString(),
txId: e.txid,
outputIndex: e.vout,
script: new btc.Script(address),
satoshis: e.value
};
res.push(utxo)
}
}
} catch (error) {
// Handle any errors that occurred during the request
console.error('Failed to fetch data:', error);
}
return res
}
async function fetchFeeRate(): Promise<number> {
const url = `https://blockstream.info/api/fee-estimates`;
let res = 20.0
try {
// Make a GET request to the URL using axios
const response = await axios.get(url);
// The response data is available in `response.data`
if (response.data) {
return response.data['20'] // ~ blocks inclusion
}
} catch (error) {
// Handle any errors that occurred during the request
console.error('Failed to fetch data:', error);
}
return res
}
async function main() {
// Sample secret / public key pair.
const seckey = 'TODO'
const pubkey = 'TODO'
const marker = new Uint8Array(Buffer.from('ord', 'utf-8'))
const mimetype = new Uint8Array(Buffer.from('text/plain', 'utf-8'))
const textData = new Uint8Array(Buffer.from('Hello, sCrypt!', 'utf-8'))
const scriptPrefix = new btc.Script('147526ffac76fab398971f3a1688908961ef0b69bd14437258615b9ee7d68daaa11d7a9618ac968c59be14a75e16b064a686048bb9bb98a8e0d48d5baf78fe14519d8dcc251a72763598de67b77a817653151fcf143a97630774dc650878fb11db2760a5b08a3d44121402b6a2d36b7f1c6672aef7cd0121f141310a178d557955795a79610079a90079547987640079537987675168690079537987517a75517a75517a75517a7561547954795a79610079a90079547987640079537987675168690079537987517a75517a75517a75517a7561537953795a79610079a90079547987640079537987675168690079537987517a75517a75517a75517a75615279527961517963007967006891517a75517a7561517987777777777777777777777777')
let script: any[] = []
scriptPrefix.chunks.forEach((chunk: { opcodenum: number; buf: Buffer; }) => {
if (chunk.buf) {
script.push(new Uint8Array(chunk.buf))
} else {
script.push(btc.Opcode.reverseMap[chunk.opcodenum])
}
});
script = [...script, 'OP_0', 'OP_IF', marker, '01', mimetype, 'OP_0', textData, 'OP_ENDIF']
// For tapscript spends, we need to convert this script into a 'tapleaf'.
const tapleaf = Tap.encodeScript(script)
// Generate a tapkey that includes our leaf script. Also, create a merlke proof
// (cblock) that targets our leaf and proves its inclusion in the tapkey.
const [tpubkey, cblock] = Tap.getPubKey(pubkey, { target: tapleaf })
// Regular P2WPKH stuff used for funding...
const alice = new btc.PrivateKey('TODO', btc.Networks.livenet)
const aliceAddrP2WPKH = alice.toAddress(null, btc.Address.PayToWitnessPublicKeyHash)
console.log(aliceAddrP2WPKH.toString())
const scripP2TR = new btc.Script(`OP_1 32 0x${tpubkey}}`)
console.log(scripP2TR.toString())
// Fetch UTXO's for address
const utxos = await fetchP2WPKHUtxos(aliceAddrP2WPKH)
const tx0 = new btc.Transaction()
.from(utxos)
.addOutput(new btc.Transaction.Output({
satoshis: 16000,
script: scripP2TR
}))
.change(aliceAddrP2WPKH)
.feePerByte(await fetchFeeRate())
.sign(alice)
console.log(tx0.serialize())
////// UNLOCKING TX //////
const prevTxId = 'TODO'
const utxoP2TR = {
txId: prevTxId,
outputIndex: 0,
script: scripP2TR,
satoshis: 16000
};
const p2trInput = new btc.Transaction.Input({
prevTxId,
output: utxoP2TR,
outputIndex: 0,
scriptBuffer: new btc.Script('')
})
const txdata = Tx.create({
vin: [{
txid: utxoP2TR.txId,
vout: utxoP2TR.outputIndex,
prevout: {
value: utxoP2TR.satoshis,
scriptPubKey: ['OP_1', tpubkey]
},
}],
vout: [
{
value: 546,
scriptPubKey: Address.toScriptPubKey(aliceAddrP2WPKH.toString())
}
]
})
txdata.vin[0].witness = [
'7f5b1bc34513931257bb7520d175079c29b18c54812c938cdc6c66a277111bf700',
'58b79cc3b0cfeb8e00561686416f09c355fed61fc13337b0d2fc914a3946759c01',
'55fe3e4a446371d0d522e63662cac69d9e77cd7e87038bcb0fe126a60f24e0f901',
script,
cblock
]
const txHex = Buffer.from(Tx.encode(txdata)).toString('hex')
console.log(txHex)
}
main().catch(error => console.error('Error in main function:', error));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment