Created
January 19, 2024 08:39
-
-
Save msinkec/886ded5bff9a6b5fc0bf1f40530531e6 to your computer and use it in GitHub Desktop.
Ordinal w/ custom script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @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