Files
bch-dex/tools/burn-and-write.js
T

220 lines
7.0 KiB
JavaScript
Raw Normal View History

2022-02-23 05:51:12 -08:00
/*
This example burns a token and writes an Object as clear-text JSON string to
the P2WDB. The data is crafted to trigger a webhook and add an entry to
Torlist.
For reference, this curl will delete the webhook in P2WDB:
curl -H "Content-Type: application/json" -X DELETE -d '{ "url": "http://localhost:5002/entry", "appId": "torlist" }' localhost:5001/webhook/
*/
const axios = require('axios')
// CUSTOMIZE THESE VALUES FOR YOUR USE
// Private key holding the tokens and some BCH.
const WIF = process.env.WIF
if (!WIF) throw new Error('Add WIF to environment variable.')
// The BCH address corresponding to the WIF.
const BCHADDR = 'bitcoincash:qqp2fu2y8wra8afkefcx04yach8lhuaqvq3dxs5ddv'
const TOKENID =
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
const TOKENQTY = 0.01
const MESSAGE = 'test'
const SIGNATURE =
'H7Io3txwhjqOYFrAE/xBUzYGow510HL0U+G0LqelbHcDfqH/vQig/xcGfvTTBZpVoZtCoqOdvpPrsAFuL8VHWws='
const SERVER = 'http://localhost:5001/entry/write'
// const SERVER = 'http://192.168.0.76:5001/entry/write'
// const SERVER = 'https://p2wdb.fullstackcash.nl/entry/write'
const now = new Date()
const dataObj = {
appId: 'torlist',
title: Math.floor(Math.random() * 100000).toString(),
sourceUrl: Math.floor(Math.random() * 100000).toString(),
ipfsUrl: Math.floor(Math.random() * 100000).toString(),
timestamp: now.toISOString(),
localTimestamp: now.toLocaleString()
}
console.log(
`Adding this object to the P2WDB: ${JSON.stringify(dataObj, null, 2)}`
)
// REST API servers.
const BCHN_MAINNET = 'https://bchn.fullstack.cash/v5/'
// bch-js-examples require code from the main bch-js repo
const BCHJS = require('@psf/bch-js')
const bchjs = new BCHJS({ restURL: BCHN_MAINNET })
async function burnAndWrite () {
try {
const keyPair = bchjs.ECPair.fromWIF(WIF)
// Get UTXOs held by this address.
const data = await bchjs.Electrumx.utxo(BCHADDR)
const utxos = data.utxos
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
if (utxos.length === 0) throw new Error('No UTXOs to spend! Exiting.')
// Identify the SLP token UTXOs.
let tokenUtxos = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`tokenUtxos: ${JSON.stringify(tokenUtxos, null, 2)}`)
// Filter out the non-SLP token UTXOs.
const bchUtxos = utxos.filter((utxo, index) => {
const tokenUtxo = tokenUtxos[index]
if (!tokenUtxo.isValid) return true
return false
})
// console.log(`bchUTXOs: ${JSON.stringify(bchUtxos, null, 2)}`)
if (bchUtxos.length === 0) {
throw new Error('Wallet does not have a BCH UTXO to pay miner fees.')
}
// Filter out the token UTXOs that match the user-provided token ID.
tokenUtxos = tokenUtxos.filter((utxo, index) => {
if (
utxo && // UTXO is associated with a token.
utxo.tokenId === TOKENID && // UTXO matches the token ID.
utxo.utxoType === 'token' // UTXO is not a minting baton.
) {
return true
}
return false
})
// console.log(`tokenUtxos: ${JSON.stringify(tokenUtxos, null, 2)}`);
if (tokenUtxos.length === 0) {
throw new Error('No token UTXOs for the specified token could be found.')
}
// Choose a UTXO to pay for the transaction.
const bchUtxo = bchjs.Utxo.findBiggestUtxo(bchUtxos)
console.log(`bchUtxo: ${JSON.stringify(bchUtxo, null, 2)}`)
// Generate the SLP OP_RETURN.
const slpData = bchjs.SLP.TokenType1.generateBurnOpReturn(
tokenUtxos,
TOKENQTY
)
// BEGIN transaction construction.
// instance of transaction builder
const transactionBuilder = new bchjs.TransactionBuilder()
// Add the BCH UTXO as input to pay for the transaction.
const originalAmount = bchUtxo.value
transactionBuilder.addInput(bchUtxo.tx_hash, bchUtxo.tx_pos)
// add each token UTXO as an input.
for (let i = 0; i < tokenUtxos.length; i++) {
transactionBuilder.addInput(tokenUtxos[i].tx_hash, tokenUtxos[i].tx_pos)
}
// get byte count to calculate fee. paying 1 sat
// Note: This may not be totally accurate. Just guessing on the byteCount size.
// const byteCount = this.BITBOX.BitcoinCash.getByteCount(
// { P2PKH: 3 },
// { P2PKH: 5 }
// )
// //console.log(`byteCount: ${byteCount}`)
// const satoshisPerByte = 1.1
// const txFee = Math.floor(satoshisPerByte * byteCount)
// console.log(`txFee: ${txFee} satoshis\n`)
const txFee = 250
// amount to send back to the sending address. It's the original amount - 1 sat/byte for tx size
const remainder = originalAmount - txFee - 546
if (remainder < 1) {
throw new Error('Selected UTXO does not have enough satoshis')
}
// console.log(`remainder: ${remainder}`)
// Add OP_RETURN as first output.
transactionBuilder.addOutput(slpData, 0)
// Send the token back to the same wallet if the user hasn't specified a
// different address.
// if (TO_SLPADDR === "") TO_SLPADDR = walletInfo.slpAddress;
// Send dust transaction representing tokens being sent.
transactionBuilder.addOutput(
bchjs.SLP.Address.toLegacyAddress(BCHADDR),
546
)
// Last output: send the BCH change back to the wallet.
transactionBuilder.addOutput(
bchjs.Address.toLegacyAddress(BCHADDR),
remainder
)
// Sign the transaction with the private key for the BCH UTXO paying the fees.
let redeemScript
transactionBuilder.sign(
0,
keyPair,
redeemScript,
transactionBuilder.hashTypes.SIGHASH_ALL,
originalAmount
)
// Sign each token UTXO being consumed.
for (let i = 0; i < tokenUtxos.length; i++) {
const thisUtxo = tokenUtxos[i]
transactionBuilder.sign(
1 + i,
keyPair,
redeemScript,
transactionBuilder.hashTypes.SIGHASH_ALL,
thisUtxo.value
)
}
// build tx
const tx = transactionBuilder.build()
// output rawhex
const hex = tx.toHex()
// console.log(`Transaction raw hex: `, hex)
// END transaction construction.
// Broadcast transation to the network
const txidStr = await bchjs.RawTransactions.sendRawTransaction([hex])
console.log(`Transaction ID: ${txidStr}`)
console.log('Check the status of your transaction on this block explorer:')
console.log(`https://explorer.bitcoin.com/bch/tx/${txidStr}`)
console.log(' ')
// Sleep for 5 seconds.
await bchjs.Util.sleep(5000)
const bodyData = {
// const result = await axios.post('https://p2wdb.fullstackcash.nl/p2wdb', {
txid: txidStr[0],
message: MESSAGE,
signature: SIGNATURE,
data: JSON.stringify(dataObj)
}
console.log(`bodyData: ${JSON.stringify(bodyData, null, 2)}`)
// Submit the txid as proof-of-burn to write data to the database.
const result = await axios.post(SERVER, bodyData)
console.log(`Response from API: ${JSON.stringify(result.data, null, 2)}`)
} catch (err) {
console.error('Error in burnTokens: ', err)
console.log(`Error message: ${err.message}`)
}
}
burnAndWrite()