import BigNumber from "bignumber.js" ; export interface UTXO { txid : string ; txindex : string ; amount : string ; height : string | null ; } export interface BitcoinAPI { fetchHeight ? : ( ) => Promise < string > ; fetchUTXO ? : ( txid : string , txindex : string ) => Promise < UTXO > ; fetchUTXOs ? : ( address : string , confirmations ? : number ) => Promise < UTXO [ ] > ; fetchTXs ? : ( address : string , confirmations ? : number ) => Promise < UTXO [ ] > ; broadcastTransaction ? : ( hex : string ) => Promise < string > ; } const SECONDS = 1000 ; export const DEFAULT_TIMEOUT = 30 * SECONDS ; returns export const sortUTXOs = ( a , b ) : number => { const aHeight = a . height ? new BigNumber ( a . height ) : null ; const bHeight = b . height ? new BigNumber ( b . height ) : null ; if ( aHeight ) { if ( bHeight ) { if ( ! aHeight . isEqualTo ( bHeight ) ) return bHeight . minus ( aHeight ) . toNumber ( ) ; } else { return - 1 ; } } else if ( bHeight ) { return 1 ; } const aAmount = new BigNumber ( a . amount ) ; const bAmount = new BigNumber ( b . amount ) ; if ( aAmount . isEqualTo ( bAmount ) ) { return bAmount . minus ( aAmount ) . toNumber ( ) ; } if ( a . txid !== b . txid ) { return a . txid <= b . txid ? - 1 : 1 ; } const aTxindex = new BigNumber ( a . txindex ) ; const bTxindex = new BigNumber ( b . txindex ) ; return bTxindex . minus ( aTxindex ) . toNumber ( ) ; } ; example param value param decimals export const fixValue = ( value , decimals ) : BigNumber => new BigNumber ( value ) . shiftedBy ( decimals ) . decimalPlaces ( 0 ) ; /**
 * fixUTXO calls {{fixValue}} on the value of the UTXO.
 */ export const fixUTXO = ( tx , decimals ) : UTXO => ( { ... tx , amount : fixValue ( tx . amount , decimals ) . toFixed ( ) , } ) ; /**
 * fixUTXOs maps over an array of UTXOs and calls {{fixValue}}.
 */ export const fixUTXOs = ( utxos , decimals ) : UTXO [ ] => utxos . map ( ( utxo ) => fixUTXO ( utxo , decimals ) ) ; export interface APIWithPriority { api : BitcoinAPI ; priority : number ; } const notNull = < T > ( x ) : T => { if ( x === undefined || x === null ) { throw new Error ( `Unexpected ${ String ( x ) } value.` ) ; } return x ; } ; const withPriority = ( api , defaultPriority = 0 ) => ( api as APIWithPriority ) . api && ( api as APIWithPriority ) . priority !== undefined ? ( api as APIWithPriority ) : { api : api as BitcoinAPI , priority : defaultPriority } ; export class CombinedAPI implements BitcoinAPI { public apis : APIWithPriority [ ] ; public constructor ( apis = [ ] , { priority = 0 } = { } ) { this . apis = apis . map ( ( api ) => withPriority ( api , priority ) ) ; } param api param config param config . priority public withAPI ( api , { priority = 0 } = { } ) { this . apis . push ( withPriority ( api , priority ) ) ; return this ; } public async fetchHeight ( ) { return this . forEachAPI ( ( api ) => api . fetchHeight !== undefined , async ( api ) => notNull ( api . fetchHeight ) ( ) ) ; } public async fetchUTXO ( txid , txindex ) { return this . forEachAPI ( ( api ) => api . fetchUTXO !== undefined , ( api ) => notNull ( api . fetchUTXO ) . bind ( this ) ( txid , txindex ) ) ; } public fetchUTXOs = async ( address , confirmations ? ) : Promise < UTXO [ ] > => this . forEachAPI ( ( api ) => api . fetchUTXOs !== undefined , ( api ) => notNull ( api . fetchUTXOs ) . bind ( api ) ( address , confirmations ) ) ; public fetchTXs = async ( address , confirmations ? ) : Promise < UTXO [ ] > => this . forEachAPI ( ( api ) => api . fetchTXs !== undefined , ( api ) => notNull ( api . fetchTXs ) . bind ( api ) ( address , confirmations ) ) ; public async broadcastTransaction ( hex ) { return this . forEachAPI ( ( api ) => api . broadcastTransaction !== undefined , ( api ) => notNull ( api . broadcastTransaction ) . bind ( api ) ( hex ) ) ; } private forEachAPI = async < T > ( filter , onAPI ) => { const apis = this . apis . map ( ( api , index ) => ( { api , index } ) ) . filter ( ( { api } ) => filter ( api . api ) ) . sort ( ( { api : a } , { api : b } ) => a . priority !== b . priority ? a . priority - b . priority : Math . random ( ) * 2 - 1 ) ; if ( ! apis . length ) { throw new Error ( `No API available for call.` ) ; } let firstError ; const previousIndices = [ ] ; for ( const { api , index } of apis ) { try { const result = await onAPI ( api . api ) ; for ( const previousIndex of previousIndices ) { this . apis [ previousIndex ] . priority -= 5 ; } return result ; } catch ( error ) { previousIndices . push ( index ) ; firstError = firstError || error ; } } throw firstError ; } ; }