swap-bch-js
Version:
Signal, Watch and Pay (SWaP) Protocol on the Bitcoin Cash blockchain
144 lines (126 loc) • 6.6 kB
JavaScript
const axios = require('axios');
const utils = require('./utils');
module.exports = class BfpBitdb {
constructor(network) {
// this.bitDbUrl = network === 'mainnet' ? 'https://bitdb.bitcoin.com/q/' : 'https://tbitdb.bitcoin.com/q/';
this.bitDbUrl = network === 'mainnet' ? 'https://bitdb.bch.sx/q/' : 'https://tbitdb.bch.sx/q/';
this.jqQueries = {
signal: [
{ "f": "[ .[] | { timestamp: (if .blk? then (.blk.t | strftime(\"%Y-%m-%d %H:%M\")) else null end), tokenId: .out[0].h4, buyOrSell: .out[0].s5, rate: .out[0].h6, reserves: .out[0].h7, exactUtxoTxId: .out[0].h8, exactUtxoIndex: .out[0].h9, minSatsToExchange: .out[0].h10, URI: \"swap:\\(.tx.h)\" } ]" }, // Signal/Exchange
{ "f": "[ .[] | { timestamp: (if .blk? then (.blk.t | strftime(\"%Y-%m-%d %H:%M\")) else null end), oracleBfp: \"bitcoinfile:\\(.out[0].h4)\", compilerId: .out[0].s5, compilerVersion: .out[0].s6, contractPartyIndex: .out[0].h7, pubKey: .out[0].h8, exactUtxoTxId: .out[0].h9, exactUtxoIndex: .out[0].h10, terms: .out[0].h11, appendedScriptPubKey: .out[0].h12, appendedSats: .out[0].h13, URI: \"swap:\\(.tx.h)\" } ]" }, // Signal/Escrow
{ "f": "[ .[] | { timestamp: (if .blk? then (.blk.t | strftime(\"%Y-%m-%d %H:%M\")) else null end), campaignUri: .out[0].s4, outputs: .out[0].h5, URI: \"swap:\\(.tx.h)\" } ]" }, // Signal/Crowdfund
],
payment: [
{ "f": "[ .[] | { timestamp: (if .blk? then (.blk.t | strftime(\"%Y-%m-%d %H:%M\")) else null end), signalId: .out[0].h5, totalChunks: .out[0].h4, URI: \"swap:\\(.tx.h)\" } ]" }, // Payment/Exchange
{ "f": "[ .[] | { timestamp: (if .blk? then (.blk.t | strftime(\"%Y-%m-%d %H:%M\")) else null end), signalId: .out[0].h5, totalChunks: .out[0].h4, p2shScriptPubKey: .out[0].h6, URI: \"swap:\\(.tx.h)\" } ]" }, // Payment/Escrow
{ "f": "[ .[] | { timestamp: (if .blk? then (.blk.t | strftime(\"%Y-%m-%d %H:%M\")) else null end), signalId: .out[0].h5, totalChunks: .out[0].h4, URI: \"swap:\\(.tx.h)\" } ]" }, // Payment/Crowdfund
]
}
this.qQuery = {
"find": {
"out.h1": "53575000"
}
}
}
async getSignalMetadata(msgType, txid=null, tokenOrOracleId=null, address=null) {
let jqQuery = this.jqQueries.signal[msgType-1]
let qQuery = JSON.parse(JSON.stringify(this.qQuery))
qQuery.find["out.h2"] = "01"
qQuery.find["out.h3"] = utils.int2FixedBuffer(msgType, 1).toString('hex')
if(txid)
qQuery.find["tx.h"] = txid.replace('swap:', '')
// If no txid, search by tokenId or OracleId
else {
if (tokenOrOracleId)
qQuery.find["out.h4"] = tokenOrOracleId
if (address)
qQuery.find["out.e.a"] = address.replace('bitcoincash:', '')
}
let metadata = await this.getFileMetadata(qQuery, jqQuery)
for(let i = 0; i< metadata.length; i++) {
metadata[i].timestamp = metadata[i].timestamp ? metadata[i].timestamp : 'unconfirmed'
if(msgType == 1) {
metadata[i].rate = parseInt(metadata[i].rate, 16)
metadata[i].reserves = metadata[i].reserves == '01' ? true : false
metadata[i].minSatsToExchange = parseInt(metadata[i].minSatsToExchange, 16)
metadata[i].exactUtxoIndex = parseInt(metadata[i].exactUtxoIndex, 16)
} else if (msgType == 2) {
metadata[i].contractPartyIndex = parseInt(metadata[i].contractPartyIndex, 16)
metadata[i].appendedSats = parseInt(metadata[i].appendedSats, 16)
metadata[i].exactUtxoIndex = parseInt(metadata[i].exactUtxoIndex, 16)
} else if (msgType == 3) {
let outputsBuf = Buffer.from(metadata[i].outputs, 'hex')
metadata[i].outputs = utils.outputsBufferToArray(outputsBuf)
}
}
if(qQuery.find["tx.h"])
return metadata[0]
return metadata
}
async getPaymentMetadata(msgType, txid=null, signalId=null, address=null) {
let jqQuery = this.jqQueries.payment[msgType-1]
let qQuery = JSON.parse(JSON.stringify(this.qQuery))
qQuery.find["out.h2"] = "02"
qQuery.find["out.h3"] = utils.int2FixedBuffer(msgType, 1).toString('hex')
if(txid)
qQuery.find["tx.h"] = txid.replace('swap:', '')
// If no txid, search by tokenId or OracleId
else {
if (signalId)
qQuery.find["out.h5"] = signalId.replace('swap:', '')
if (address)
qQuery.find["out.e.a"] = address.replace('bitcoincash:', '')
}
let metadata = await this.getFileMetadata(qQuery, jqQuery)
for(let i = 0; i< metadata.length; i++) {
metadata[i].totalChunks = parseInt(metadata[i].totalChunks, 16)
}
if(qQuery.find["tx.h"])
return metadata[0]
return metadata
}
async getFileMetadata(qQuery, jqQuery=null, apiKey=null) {
if(!jqQuery)
jqQuery = { "f": "[ .[] | { timestamp: (if .blk? then (.blk.t | strftime(\"%Y-%m-%d %H:%M\")) else null end), offer_txid: .out[0].h4, URI: \"swap:\\(.tx.h)\" } ]" }
let query = {
"v": 3,
"q": qQuery,
"r": jqQuery
};
// example response format:
// { filename: 'tes158',
// fileext: '.json',
// size: '017a',
// sha256: '018321383bf2672befe28629d1e159af812260268a8aa77bbd4ec27489d65b58',
// prev_sha256: '',
// ext_uri: '' }
const json_str = JSON.stringify(query);
const data = Buffer.from(json_str).toString('base64');
const response = (await axios({
method: 'GET',
url: this.bitDbUrl + data,
headers: null,
// {
// 'key': apiKey,
// },
json: true,
})).data;
if(response.status === 'error'){
throw new Error(response.message || 'API error message missing');
}
const list = [];
// c = confirmed
if(response.c){
list.push(...response.c);
}
// u = unconfirmed
if(response.u){
list.push(...response.u);
}
if(list.length === 0){
throw new Error('File not found');
}
// console.log('bitdb response: ', list[0]);
return list
}
}