-
Notifications
You must be signed in to change notification settings - Fork 69
/
BitcoinEsploraBaseProvider.ts
90 lines (79 loc) · 3.72 KB
/
BitcoinEsploraBaseProvider.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import { HttpClient } from '@liquality/client';
import { AddressType } from '@liquality/types';
import { flatten } from 'lodash';
import { UTXO } from '../../types';
import { decodeRawTransaction, normalizeTransactionObject } from '../../utils';
import { BitcoinBaseChainProvider } from '../BitcoinBaseChainProvider';
import * as EsploraTypes from './types';
export class BitcoinEsploraBaseProvider extends BitcoinBaseChainProvider {
public httpClient: HttpClient;
protected _options: EsploraTypes.EsploraApiProviderOptions;
constructor(options: EsploraTypes.EsploraApiProviderOptions) {
super();
this.httpClient = new HttpClient({ baseURL: options.url });
this._options = {
numberOfBlockConfirmation: 1,
defaultFeePerByte: 3,
...options,
};
}
public async formatTransaction(tx: EsploraTypes.Transaction, currentHeight: number) {
const hex = await this.getTransactionHex(tx.txid);
const confirmations = tx.status.confirmed ? currentHeight - tx.status.block_height + 1 : 0;
const decodedTx = decodeRawTransaction(hex, this._options.network);
decodedTx.confirmations = confirmations;
return normalizeTransactionObject(decodedTx, tx.fee, { hash: tx.status.block_hash, number: tx.status.block_height });
}
public async getRawTransactionByHash(transactionHash: string) {
return this.getTransactionHex(transactionHash);
}
public async getTransactionHex(transactionHash: string): Promise<string> {
return this.httpClient.nodeGet(`/tx/${transactionHash}/hex`);
}
public async getFeePerByte(numberOfBlocks = this._options.numberOfBlockConfirmation) {
try {
const feeEstimates: EsploraTypes.FeeEstimates = await this.httpClient.nodeGet('/fee-estimates');
const blockOptions = Object.keys(feeEstimates).map((block) => parseInt(block));
const closestBlockOption = blockOptions.reduce((prev, curr) => {
return Math.abs(prev - numberOfBlocks) < Math.abs(curr - numberOfBlocks) ? prev : curr;
});
const rate = Math.round(feeEstimates[closestBlockOption]);
return rate;
} catch (e) {
return this._options.defaultFeePerByte;
}
}
public async getUnspentTransactions(_addresses: AddressType[]): Promise<UTXO[]> {
const addresses = _addresses.map((a) => a.toString());
const utxoSets = await Promise.all(addresses.map((addr) => this._getUnspentTransactions(addr)));
const utxos = flatten(utxoSets);
return utxos;
}
public async getAddressTransactionCounts(_addresses: AddressType[]) {
const addresses = _addresses.map((a) => a.toString());
const transactionCountsArray = await Promise.all(
addresses.map(async (addr) => {
const txCount = await this._getAddressTransactionCount(addr);
return { [addr]: txCount };
})
);
const transactionCounts = Object.assign({}, ...transactionCountsArray);
return transactionCounts;
}
public async getMinRelayFee() {
return 1;
}
private async _getUnspentTransactions(address: string): Promise<UTXO[]> {
const data: EsploraTypes.UTXO[] = await this.httpClient.nodeGet(`/address/${address}/utxo`);
return data.map((utxo) => ({
...utxo,
address,
value: utxo.value,
blockHeight: utxo.status.block_height,
}));
}
private async _getAddressTransactionCount(address: string) {
const data: EsploraTypes.Address = await this.httpClient.nodeGet(`/address/${address}`);
return data.chain_stats.tx_count + data.mempool_stats.tx_count;
}
}