@exodus/bitcoin-api 2.7.0 → 2.7.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/bitcoin-api",
3
- "version": "2.7.0",
3
+ "version": "2.7.1",
4
4
  "description": "Exodus bitcoin-api",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -43,5 +43,5 @@
43
43
  "@scure/btc-signer": "^1.1.0",
44
44
  "jest-when": "^3.5.1"
45
45
  },
46
- "gitHead": "5c306496cb4086ff823640e8eadb0d9750ac3683"
46
+ "gitHead": "9c993eed3a3bcfc1c49d86377fdc5c7cb731d632"
47
47
  }
@@ -2,6 +2,7 @@
2
2
  import urlJoin from 'url-join'
3
3
  import qs from 'querystring'
4
4
  import delay from 'delay'
5
+ import { isEmpty } from 'lodash'
5
6
 
6
7
  const getTextFromResponse = async (response) => {
7
8
  try {
@@ -105,13 +106,22 @@ export default class InsightAPIClient {
105
106
  const url = urlJoin(this._baseURL, `/tx/${encodedTxId}`)
106
107
  const response = await fetch(url)
107
108
 
108
- // change in https://github.com/jprichardson/exodus-rn/pull/4336 may break Insight compatibility
109
- // we're probably past the point of just spinning up an Insight server and have it plug n' play for Magnifier
110
-
111
109
  if (response.status === 404) return null
112
110
  return response.json()
113
111
  }
114
112
 
113
+ async fetchTxObject(txId) {
114
+ const url = urlJoin(this._baseURL, `/fulltx?${new URLSearchParams({ hash: txId })}`)
115
+ const response = await fetch(url)
116
+
117
+ if (response.status === 404) return null
118
+ const object = await response.json()
119
+ if (isEmpty(object)) {
120
+ return null
121
+ }
122
+ return object
123
+ }
124
+
115
125
  async fetchRawTx(txId) {
116
126
  const encodedTxId = encodeURIComponent(txId)
117
127
  const url = urlJoin(this._baseURL, `/rawtx/${encodedTxId}`)
@@ -1,11 +1,12 @@
1
1
  import { orderTxs } from '../insight-api-client/util'
2
2
  import { Address, UtxoCollection } from '@exodus/models'
3
- import { isEqual, compact, uniq } from 'lodash'
3
+ import { compact, isEqual, uniq } from 'lodash'
4
4
  import ms from 'ms'
5
5
  import assert from 'minimalistic-assert'
6
6
  import { isChangeAddress, isReceiveAddress } from '../address-utils'
7
7
  import { getOrdinalsUtxos, getUtxos, partitionUtxos } from '../utxos-utils'
8
8
  import { getOrdinalAddress } from '../ordinals-utils'
9
+ import { indexOrdinalUnconfirmedTx } from './ordinals-indexer-utils'
9
10
 
10
11
  // Time to check whether to drop a sent tx
11
12
  const SENT_TIME_TO_DROP = ms('2m')
@@ -191,8 +192,30 @@ export class BitcoinMonitorScanner {
191
192
  promises.push(promise)
192
193
  }
193
194
 
194
- const txArrays = await Promise.all(promises)
195
- return txArrays.reduce((total, some) => total.concat(some), [])
195
+ const insightTxs = (await Promise.all(promises)).reduce(
196
+ (total, some) => total.concat(some),
197
+ []
198
+ )
199
+ if (!this.#ordinalsEnabled) {
200
+ return insightTxs
201
+ }
202
+ return Promise.all(
203
+ insightTxs.map((tx) => {
204
+ try {
205
+ return indexOrdinalUnconfirmedTx({
206
+ tx,
207
+ currency: this.#asset.currency,
208
+ insightClient,
209
+ })
210
+ } catch (e) {
211
+ console.warn(
212
+ `Could not index ${asset.name} ordinal tx ${tx.txid} for wallet account ${walletAccount}. message: ${e.message}`,
213
+ e
214
+ )
215
+ return tx
216
+ }
217
+ })
218
+ )
196
219
  }
197
220
 
198
221
  const gapSearchParameters = newChains.map(({ purpose, chain }) => {
@@ -0,0 +1,53 @@
1
+ import { memoizeLruCache } from '@exodus/asset-lib'
2
+ import { cloneDeep } from 'lodash'
3
+
4
+ export const indexOutputs = ({ tx, currency }) => {
5
+ const inscriptions = []
6
+
7
+ let inputOffset = 0
8
+ for (let i = 0; i < tx.vin.length; i++) {
9
+ const vin = tx.vin[i]
10
+ const value = currency.defaultUnit(vin.value).toBaseNumber()
11
+ inscriptions.push(
12
+ ...(vin.inscriptions || []).map((i) => ({ ...i, offset: i.offset + inputOffset }))
13
+ )
14
+ inputOffset = value
15
+ }
16
+
17
+ let outputOffset = 0
18
+ for (let i = 0; i < tx.vout.length; i++) {
19
+ const vout = tx.vout[i]
20
+ const value = currency.defaultUnit(vout.value).toBaseNumber()
21
+ vout.inscriptions = inscriptions
22
+ .map((i) => ({ ...i, offset: i.offset - outputOffset }))
23
+ .filter((i) => i.offset >= 0 && i.offset < value)
24
+ outputOffset = value
25
+ }
26
+ tx.inscriptionsMemoryIndexed = true // avoids btc being spent even when the mempool index was done in memory
27
+ return tx
28
+ }
29
+
30
+ export const indexOrdinalUnconfirmedTx = memoizeLruCache(
31
+ async ({ insightClient, currency, tx }) => {
32
+ if (tx.inscriptionsIndexed || tx.inscriptionsMemoryIndexed) {
33
+ return tx
34
+ }
35
+ const copyTx = cloneDeep(tx)
36
+ await Promise.all(
37
+ copyTx.vin.map(async (vin) => {
38
+ const outputTx = await indexOrdinalUnconfirmedTx({
39
+ insightClient,
40
+ currency,
41
+ tx: await insightClient.fetchTxObject(vin.txid),
42
+ })
43
+ if (!outputTx.inscriptionsIndexed && !outputTx.inscriptionsMemoryIndexed) {
44
+ throw new Error(`Cannot index ${tx.txid}. Input tx ${outputTx.txid} is not indexed. `)
45
+ }
46
+ vin.inscriptions = outputTx.vout[vin.vout].inscriptions
47
+ })
48
+ )
49
+ return indexOutputs({ tx: copyTx, currency })
50
+ },
51
+ ({ tx }) => tx.txid,
52
+ 100
53
+ )