import axios from "axios" ; import { BitcoinAPI , DEFAULT_TIMEOUT , fixUTXO , fixUTXOs , fixValue , sortUTXOs , UTXO , } from "./API" ; import { FetchTXsResult } from "./insight" ; export class BitcoinDotCom implements BitcoinAPI { public testnet ? : boolean ; public constructor ( { testnet = false } = { } ) { this . testnet = testnet ; } public endpoint ( ) { return this . testnet ? "https://trest.bitcoin.com/v2/" : "https://rest.bitcoin.com/v2/" ; } public endpointV2 ( ) { return this . testnet ? "https://explorer-tbch.api.bitcoin.com/tbch/v1" : "https://explorer.api.bitcoin.com/bch/v1" ; } public async fetchHeight ( ) { const url = `${ this . endpointV2 ( ) }/blockchain/getBlockCount` ; const response = await axios . get < number > ( url , { timeout : DEFAULT_TIMEOUT , } ) ; return response . data . toString ( ) ; } public async fetchUTXO ( txid , txindex ) { const url = `${ this . endpointV2 ( ) }/tx/${ txid }` ; const response = await axios . get < FetchTXResponse > ( url , { timeout : DEFAULT_TIMEOUT , } ) ; const utxo = response . data ; return fixUTXO ( { txid : txid , amount : utxo . vout [ parseInt ( txindex , 10 ) ] . value . toString ( ) , txindex : txindex , height : utxo . blockheight && utxo . blockheight > 0 ? utxo . blockheight . toString ( ) : null , } , 8 ) ; } public async fetchUTXOs ( address ) { const url = `${ this . endpointV2 ( ) }/addr/${ address }/utxo` ; const response = await axios . get < FetchUTXOSResponse > ( url , { timeout : DEFAULT_TIMEOUT , } ) ; return fixUTXOs ( response . data . map ( ( utxo ) => ( { txid : utxo . txid , amount : utxo . amount . toString ( ) , txindex : utxo . vout . toString ( ) , height : utxo . height && utxo . height > 0 ? utxo . height . toString ( ) : null , } ) ) , 8 ) . sort ( sortUTXOs ) ; } public async fetchTXs ( address ) { const url = `${ this . endpoint ( ) . replace ( /\/$/ , "" ) }/address/transactions/${ address }` ; const { data } = await axios . get < FetchTXsResult > ( url , { timeout : DEFAULT_TIMEOUT , } ) ; const received = [ ] ; for ( const tx of data . txs ) { for ( let i = 0 ; i < tx . vout . length ; i ++ ) { const vout = tx . vout [ i ] ; if ( vout . scriptPubKey . addresses . indexOf ( address ) >= 0 ) { received . push ( { txid : tx . txid , amount : fixValue ( parseFloat ( vout . value ) , 8 ) . toFixed ( ) , txindex : i . toString ( ) , height : tx . blockheight && tx . blockheight > 0 ? tx . blockheight . toString ( ) : null , } ) ; } } } return received . sort ( sortUTXOs ) ; } public async broadcastTransaction ( hexEncodedTransaction ) { const url = `${ this . endpoint ( ) . replace ( /\/$/ , "" ) }/rawtransactions/sendRawTransaction` ; const response = await axios . post < string [ ] > ( url , { hexes : [ hexEncodedTransaction ] } , { timeout : DEFAULT_TIMEOUT } ) ; if ( ( response . data as unknown as BlockchairError ) . error ) { throw new Error ( ( response . data as unknown as BlockchairError ) . error ) ; } return response . data [ 0 ] ; } } interface BlockchairError { error : string ; } type FetchUTXOSResponse = Array < { address : string ; txid : string ; vout : number ; scriptPubKey : string ; amount : number ; satoshis : number ; height : number ; confirmations : number ; } > ; type FetchTXResponse = { txid : string ; version : number ; locktime : number ; vin : Array < { txid : string ; vout : number ; sequence : number ; n : number ; scriptSig : { hex : string ; asm : string ; } ; addr : string ; valueSat : number ; value : number ; doubleSpentTxID : null ; } > ; vout : Array < { value : string ; n : number ; scriptPubKey : { hex : string ; asm : string ; addresses : number ; type : string ; } ; spentTxId : string ; spentIndex : number ; spentHeight : number ; } > ; blockhash : string ; blockheight : number ; confirmations : number ; time : number ; blocktime : number ; firstSeenTime : number ; valueOut : number ; size : number ; valueIn : number ; fees : number ; } ;