Skip to content

Instantly share code, notes, and snippets.

@roshnet
Last active November 24, 2021 10:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roshnet/3eba1e3a8e246a4a8df31c3235153fcd to your computer and use it in GitHub Desktop.
Save roshnet/3eba1e3a8e246a4a8df31c3235153fcd to your computer and use it in GitHub Desktop.
Implementing the given methods in the Wallet class
/*
Mnemoics used -
quality armor buddy double food laugh tunnel marine lizard west cup what basket lumber walnut link frog rice pigeon abstract nurse volcano curve twice
Generated tpub from iancoleman.io/bip32
tpubDC93eCC1ySXoBp7AKVv9M9uRi4evZjwgd8LbmYuhXs6NnarvhHsNFPcrzwPqsrUa3y5E5FjnbGE4Li55Y5G96aaPJsb2DKagQC8zgpcRcMQ
Generated Account extended private key from iancoleman.io/bip32
tprv8fT1Vn9mq4r8JM5NRrFYwkFK938zQQkn3pjpV2sQ7bHyx6cA4u3n4tzzpn9vJysFzvbLW2VSUDgG9bPQhxGKhfxvyc8wVMahKNFVPL8aMqv
*/
const bjl = require('bitcoinjs-lib')
const axios = require('axios').default
const BIP32Factory = require('bip32').default
const coinselect = require('coinselect')
const BLOCKCYPHER_API_ROOT = 'https://api.blockcypher.com'
const feeRate = 55
class Wallet {
constructor(network) {
this.network = network
this.token = 'd6c3025e283c468e8b7c5268cd5b550f'
}
// Generate bitcoin testnet addresses using "xpub" for "chain" index = 0 or 1 from range index "start" to "end".
async address_list(xpub, chain = null, start = 0, end = 1) {
const ecc = await import('tiny-secp256k1')
const bip32 = await BIP32Factory(ecc)
let node = bip32.fromBase58(xpub, this.network)
const { address } = bjl.payments.p2pkh({
pubkey: node.derive(start).derive(end).publicKey,
network: this.network,
})
return address
}
// Add the "addresses" list on blockcypher database. This list is recognised by the "name" argument.
async add_wallet(name, addresses) {
const endpoint = new URL('/v1/btc/test3/wallets', BLOCKCYPHER_API_ROOT)
endpoint.searchParams.set('token', this.token)
try {
const response = await axios.post(endpoint.href, { name, addresses })
return response.data
} catch (e) {
throw new Error(e.message)
}
}
// Add the "addresses" on blockcypher database to an already existing wallet recognised by the "name" argument.
add_addresses(name, addresses) {
const endpoint = new URL(
`/v1/btc/test3/wallets/${name}/addresses`,
BLOCKCYPHER_API_ROOT,
)
endpoint.searchParams.set('token', this.token)
axios
.post(endpoint.href)
.then(({ data }) => {
console.log('Added', data['addresses'], 'to wallet', name)
})
.catch((e) => {
console.error(e.message)
})
}
// Fetch the "addresses" from blockcypher database of an already existing wallet recognised by the "name" argument.
async fetch_wallet(name) {
const endpoint = new URL(
`/v1/btc/test3/wallets/${name}`,
BLOCKCYPHER_API_ROOT,
)
endpoint.searchParams.set('token', this.token)
try {
const response = await axios.get(endpoint.href)
return response.data.addresses
} catch (e) {
throw new Error(e.message)
}
}
// Fetch "UTXOs" using wallet name provided in "receive" and "change" argumnets using blockcypher APIs
async fetch_utxo(walletName, receive, change) {
const addresses = await this.fetch_wallet(walletName)
const utxos = []
for (let addr of addresses) {
const endpoint = new URL(
`/v1/btc/test3/addrs/${addr}`,
BLOCKCYPHER_API_ROOT,
)
endpoint.searchParams.set('unspentOnly', true)
const response = await axios.get(endpoint.href)
utxos.push(response.data)
}
return utxos
}
// Generate unsigned txn using "xpub" to send "amount" to "output_address"
async generate_unsigned_transaction(
xpub,
outputAddress,
amount,
config = { index: 1 },
) {
const inputAddress = await this.address_list(xpub, null, config['index'])
const endpoint = new URL(
'/v1/btc/test3/txs/new',
'https://api.blockcypher.com',
)
const txPayload = {
inputs: [{ addresses: [inputAddress] }],
outputs: [
{
addresses: [outputAddress],
value: amount,
},
],
}
const response = await axios.post(endpoint.href, JSON.stringify(txPayload))
const txResponse = response.data
let txId = txResponse['tx']['hash']
const tx = new bjl.Psbt({ network: bjl.networks.testnet })
const txInput = {
hash: txId,
index: txResponse['tx']['inputs'][0]['output_index'],
}
const txOutput = {
address: outputAddress,
value: amount,
}
let { inputs, outputs, fee } = coinselect([txInput], [txOutput], feeRate)
// Remove below condition to create raw unsigned TX
if (!inputs || !outputs) throw new Error('Inputs or outputs not generated')
tx.addInput(txInput)
tx.addOutput(txOutput)
return tx.toHex()
}
}
async function main() {
const w = new Wallet(bjl.networks.testnet)
// const tx = await w.generate_unsigned_transaction(
// 'tpubDC93eCC1ySXoBp7AKVv9M9uRi4evZjwgd8LbmYuhXs6NnarvhHsNFPcrzwPqsrUa3y5E5FjnbGE4Li55Y5G96aaPJsb2DKagQC8zgpcRcMQ',
// 'n1oAxbBijCqVDYhfmxkW6CzR7abTx6zoud',
// 50000,
// { index: 1 },
// )
// console.log('[OK] Created TX', tx)
const utxo = await w.fetch_utxo('rosh0', 'n1oAxbBijCqVDYhfmxkW6CzR7abTx6zoud')
console.log(utxo)
}
main().catch((e) => {
console.error('[FAILED]', e.response ? e.response.data : e.message)
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment