@teleportdao/bitcoin 1.4.6 → 1.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,255 @@
1
+ import { AxiosInstance } from "axios"
2
+ import { getAxiosInstance } from "./utils/tools"
3
+
4
+ class BlockStream {
5
+ api: AxiosInstance
6
+ constructor(
7
+ testnet = false,
8
+ // not used yet
9
+ _config?: {
10
+ token?: string
11
+ },
12
+ ) {
13
+ const baseUrl = testnet
14
+ ? "https://mempool.space/testnet/api/v1"
15
+ : "https://mempool.space/api/v1"
16
+ this.api = getAxiosInstance({
17
+ baseUrl,
18
+ } as any)
19
+ }
20
+
21
+ async getOutspend(txId: string, index: number) {
22
+ let response = await this.api.get(`/tx/${txId}/outspend/${index}`)
23
+ return response.data
24
+ }
25
+ async getOutspends(txId: string) {
26
+ let response = await this.api.get(`/tx/${txId}/outspends`)
27
+ return response.data
28
+ }
29
+
30
+ async getLatestBlockNumber() {
31
+ const result = await this.api.get(`/blocks/tip/height`)
32
+ return result.data
33
+ }
34
+
35
+ async getBlockHash(blockNumber: number) {
36
+ const result = await this.api.get(`/block-height/${blockNumber}`)
37
+ return result.data
38
+ }
39
+
40
+ async getBlockHeaderHex(blockNumber: number) {
41
+ const hash = await this.getBlockHash(blockNumber)
42
+ const result = await this.api.get(`/block/${hash}/header`)
43
+ return result.data
44
+ }
45
+
46
+ // --------------
47
+
48
+ async getConfirmedTransactions(userAddress: string, lastReceivedTxId = "") {
49
+ const result = await this.api.get(`/address/${userAddress}/txs/chain/${lastReceivedTxId}`)
50
+ return result.data
51
+ }
52
+
53
+ async getMempoolTransactions(userAddress: string) {
54
+ const result = await this.api.get(`/address/${userAddress}/txs/mempool`)
55
+ return result.data
56
+ }
57
+
58
+ async getTransaction(txId: string) {
59
+ const result = await this.api.get(`/tx/${txId}`)
60
+ let tx = result.data
61
+ return {
62
+ txId: tx.txid,
63
+ version: tx.version,
64
+ locktime: tx.locktime,
65
+ blockTime: tx.status.block_time,
66
+ blockNumber: tx.status.block_height || null,
67
+ blockHash: tx.status.block_hash || null,
68
+ vout: tx.vout.map((vo: any) => ({
69
+ address: vo.scriptpubkey_address || null,
70
+ script: vo.scriptpubkey,
71
+ value: vo.value,
72
+ })),
73
+ vin: tx.vin.map((vi: any) => ({
74
+ txId: vi.txid,
75
+ index: vi.vout,
76
+ address: vi.prevout.scriptpubkey_address || null,
77
+ script: vi.prevout.scriptpubkey,
78
+ value: vi.prevout.value,
79
+ })),
80
+ }
81
+ }
82
+
83
+ async getRawTransaction(txId: string) {
84
+ const result = await this.api.get(`/tx/${txId}/hex`)
85
+ return result.data
86
+ }
87
+
88
+ async getTransactionHistory(userAddress: string, blockNumber = 0, lastSeenTxId = undefined) {
89
+ let maximumTxsLength = 15
90
+ // get transactions from lastSeenTxId to the end
91
+ let fetchTxs = true
92
+ let allResults = []
93
+ let lastReceivedTxId = ""
94
+ while (fetchTxs) {
95
+ let result = await this.getConfirmedTransactions(userAddress, lastReceivedTxId)
96
+ let lastSeenTxIdIndex = result.findIndex((tx: any) => tx.txid === lastSeenTxId)
97
+ result = result.filter((_value: any) => +_value.status.block_height > blockNumber)
98
+ result =
99
+ lastSeenTxIdIndex < 0
100
+ ? result
101
+ : result.filter((_value: any, index: number) => index < lastSeenTxIdIndex)
102
+ allResults.push(...result)
103
+ lastReceivedTxId = result[result.length - 1]?.txid
104
+ fetchTxs = result.length === maximumTxsLength && lastSeenTxIdIndex < 0 && result.length !== 0
105
+ }
106
+
107
+ return allResults.map((tx) => ({
108
+ address: userAddress,
109
+ txId: tx.txid,
110
+ version: tx.version,
111
+ locktime: tx.locktime,
112
+ blockNumber: tx.status.block_height || null,
113
+ blockHash: tx.status.block_hash || null,
114
+ blockTime: tx.status.block_time || null,
115
+ vout: tx.vout.map((vo: any) => ({
116
+ address: vo.scriptpubkey_address || null,
117
+ script: vo.scriptpubkey,
118
+ value: vo.value,
119
+ })),
120
+ vin: tx.vin.map((vi: any) => ({
121
+ txId: vi.txid,
122
+ index: vi.vout,
123
+ address: vi.prevout.scriptpubkey_address || null,
124
+ script: vi.prevout.scriptpubkey,
125
+ value: vi.prevout.value,
126
+ })),
127
+ }))
128
+ }
129
+
130
+ async getMempoolTransactionHistory(userAddress: string) {
131
+ let result = await this.getMempoolTransactions(userAddress)
132
+ return result.map((tx: any) => ({
133
+ address: userAddress,
134
+ txId: tx.txid,
135
+ version: tx.version,
136
+ locktime: tx.locktime,
137
+ vout: tx.vout.map((vo: any) => ({
138
+ address: vo.scriptpubkey_address || null,
139
+ script: vo.scriptpubkey,
140
+ value: vo.value,
141
+ })),
142
+ vin: tx.vin.map((vi: any) => ({
143
+ txId: vi.txid,
144
+ index: vi.vout,
145
+ address: vi.prevout.scriptpubkey_address || null,
146
+ script: vi.prevout.scriptpubkey,
147
+ value: vi.prevout.value,
148
+ })),
149
+ }))
150
+ }
151
+
152
+ async getMempoolTransactionHistoryForMultipleAddresses(addresses: string[], blockNumber = 0) {
153
+ const allPromises = []
154
+ for (let address of addresses) {
155
+ let promise = await this.getMempoolTransactionHistory(address)
156
+ allPromises.push(promise)
157
+ }
158
+ let result = await Promise.all(allPromises)
159
+ return result
160
+ }
161
+
162
+ async getTransactionHistoryForMultipleAddresses(userAddresses: string[], blockNumber = 0) {
163
+ const allPromises = []
164
+ for (let address of userAddresses) {
165
+ let promise = await this.getTransactionHistory(address, blockNumber)
166
+ allPromises.push(promise)
167
+ }
168
+ let result = await Promise.all(allPromises)
169
+ return result
170
+ }
171
+
172
+ async getUtxos(userAddress: string): Promise<
173
+ {
174
+ address: string
175
+ txId: string
176
+ index: number
177
+ value: number
178
+ blockNumber?: number
179
+ }[]
180
+ > {
181
+ const result = await this.api.get(`/address/${userAddress}/utxo`)
182
+ return result.data.map((tx: any) => ({
183
+ address: userAddress,
184
+ txId: tx.txid,
185
+ index: tx.vout,
186
+ value: tx.value,
187
+ blockNumber: tx.status?.block_height || undefined,
188
+ }))
189
+ }
190
+
191
+ async getBalance(address: string) {
192
+ let utxos = await this.getUtxos(address)
193
+ return utxos.reduce((a: any, tx: any) => a + Number(tx.value), 0)
194
+ }
195
+
196
+ async getBlockTransactionIds(blockHash: string) {
197
+ let result = await this.api.get(`/block/${blockHash}/txids`)
198
+ return result.data
199
+ }
200
+
201
+ async getFeeRate(speed: "normal" | "slow" | "fast" = "normal") {
202
+ let result = await this.api.get(`/fee-estimates`)
203
+ let fees = {
204
+ slow: +result.data[10],
205
+ normal: +result.data[6],
206
+ fast: +result.data[3],
207
+ }
208
+ return fees[speed] || fees.normal
209
+ }
210
+ async getRecommendedFeeRate(speed: "normal" | "slow" | "fast" = "normal") {
211
+ let result = await this.api.get(`/fees/recommended`)
212
+
213
+ let fees = {
214
+ slow: +result.data.economyFee,
215
+ normal: +result.data.halfHourFee,
216
+ fast: +result.data.fastestFee,
217
+ }
218
+ return fees[speed] || fees.normal
219
+ }
220
+
221
+ async sendRawTransaction(rawTransaction: string) {
222
+ const result = await this.api.post(`/tx`, rawTransaction)
223
+ return result.data
224
+ }
225
+
226
+ // not used
227
+
228
+ async getMerkleProof(txId: string) {
229
+ let result = (await this.api.get(`/tx/${txId}/merkle-proof`)).data
230
+ let intermediateNodes = result.merkle.reduce(
231
+ (a: any, merkle: any) => a + Buffer.from(merkle, "hex").reverse().toString("hex"),
232
+ "0x",
233
+ )
234
+ let transactionIndex = result.pos
235
+ return {
236
+ intermediateNodes,
237
+ transactionIndex,
238
+ }
239
+ }
240
+
241
+ async getLatestBlock() {
242
+ const result = await this.api.get(`/blocks`)
243
+ // 10 newest blocks
244
+ const blocks = result.data
245
+ return blocks[0]
246
+ }
247
+
248
+ async getBlock(blockNumber: number) {
249
+ const blockHash = await this.getBlockHash(blockNumber)
250
+ const result = await this.api.get(`/block/${blockHash}`)
251
+ return result.data
252
+ }
253
+ }
254
+
255
+ export default BlockStream
@@ -19,7 +19,7 @@ const componentBytes = {
19
19
  bytePerInput: {
20
20
  p2pkh: TX_INPUT_BASE + TX_INPUT_P2PKH,
21
21
  p2wpkh: TX_INPUT_BASE + TX_INPUT_P2WPKH,
22
- p2shp2wpkh: TX_INPUT_BASE + TX_INPUT_P2SH_P2PKH,
22
+ "p2sh-p2wpkh": TX_INPUT_BASE + TX_INPUT_P2SH_P2PKH,
23
23
  },
24
24
  baseTxBytes: TX_EMPTY_SIZE,
25
25
  bytePerOutput: TX_OUTPUT_BASE + TX_OUTPUT_P2PKH,
@@ -230,29 +230,47 @@ class BaseBitcoinLikeTransaction {
230
230
  if (!inputs || !outputs) {
231
231
  throw new Error("not enough balance")
232
232
  }
233
+ // console.log(inputs, outputs, feeRate)
234
+ const inputsSizes = inputs.map(
235
+ (i) =>
236
+ componentBytes.bytePerInput[
237
+ i.signerInfo.addressType as keyof typeof componentBytes.bytePerInput
238
+ ],
239
+ )
240
+ const outputSizes = outputs.length * componentBytes.bytePerOutput
241
+
242
+ // console.log(inputsSizes, outputSizes)
243
+
244
+ const txSize =
245
+ componentBytes.baseTxBytes +
246
+ inputsSizes.reduce((a, c) => a + c, 0) +
247
+ outputSizes +
248
+ componentBytes.bytePerOutput
249
+
250
+ const txFee = txSize * feeRate
251
+ let diff = fee - txFee
252
+ // console.log({ txSize, txFee, diff, fee })
253
+
233
254
  let changeIndex = outputs.findIndex((x) => !x?.address && !x.script && (x.value || 0) > 0)
234
255
  let change: ChangeTarget | undefined
235
- if (changeIndex >= 0) {
256
+ if (changeIndex >= 0 || diff > 600) {
257
+ if (changeIndex >= 0) {
258
+ diff = diff + componentBytes.bytePerOutput * feeRate
259
+ }
260
+
261
+ if (diff < 0) {
262
+ diff = 0
263
+ }
264
+
236
265
  if (!changeObject) throw new Error("change not exist")
237
- if (changeObject.derivationPath && changeObject.masterFingerprint && changeObject.publicKey) {
238
- change = {
239
- address: changeObject.address,
240
- value: outputs[changeIndex].value,
241
- bip32Derivation: [
242
- {
243
- path: changeObject.derivationPath,
244
- pubkey: Buffer.from(changeObject.publicKey, "hex"),
245
- masterFingerprint: Buffer.from(changeObject.masterFingerprint, "hex"),
246
- },
247
- ],
248
- }
249
- } else {
250
- change = {
251
- address: changeObject.address,
252
- value: outputs[changeIndex].value,
253
- }
266
+ change = {
267
+ address: changeObject.address,
268
+ value: changeIndex >= 0 ? outputs[changeIndex].value + diff : diff,
269
+ }
270
+ fee = fee - diff
271
+ if (changeIndex >= 0) {
272
+ outputs.splice(changeIndex, 1)
254
273
  }
255
- outputs.splice(changeIndex, 1)
256
274
  }
257
275
 
258
276
  return {