@exodus/bitcoin-api 2.3.5 → 2.3.7

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.3.5",
3
+ "version": "2.3.7",
4
4
  "description": "Exodus bitcoin-api",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -20,12 +20,14 @@
20
20
  "dependencies": {
21
21
  "@exodus/asset-lib": "^3.7.2",
22
22
  "@exodus/basic-utils": "^2.0.1",
23
+ "@exodus/bip-schnorr": "0.6.6-fork-1",
23
24
  "@exodus/bip44-constants": "^195.0.0",
24
25
  "@exodus/bitcoinjs-lib": "6.0.2-beta.5",
25
26
  "@exodus/models": "^8.10.4",
26
27
  "@exodus/secp256k1": "4.0.2-exodus.0",
27
28
  "@exodus/simple-retry": "0.0.6",
28
29
  "@exodus/timer": "^1.0.0",
30
+ "@noble/secp256k1": "~1.5.3",
29
31
  "bech32": "^1.1.3",
30
32
  "coininfo": "5.1.0",
31
33
  "delay": "4.0.1",
@@ -36,11 +38,8 @@
36
38
  "url-join": "4.0.0"
37
39
  },
38
40
  "devDependencies": {
39
- "@exodus/asset-lib": "^3.7.1",
40
- "@exodus/bcash-meta": "^1.0.0",
41
- "@exodus/bip-schnorr": "0.6.6-fork-1",
42
41
  "@exodus/bitcoin-meta": "^1.0.1",
43
- "@noble/secp256k1": "~1.5.3"
42
+ "jest-when": "^3.5.1"
44
43
  },
45
- "gitHead": "3dd9a1846d6f34252c12e966e1ee1f851cca4173"
44
+ "gitHead": "7a83d2ff5d672615938fba868779293df74ff51d"
46
45
  }
@@ -1,7 +1,7 @@
1
1
  /* @flow */
2
2
  import { orderTxs } from '../insight-api-client/util'
3
3
  import { Address, UtxoCollection } from '@exodus/models'
4
- import { isEqual, compact } from 'lodash'
4
+ import { isEqual, compact, uniq } from 'lodash'
5
5
  import ms from 'ms'
6
6
 
7
7
  import assert from 'minimalistic-assert'
@@ -505,4 +505,56 @@ export class BitcoinMonitorScanner {
505
505
  changedUnusedAddressIndexes,
506
506
  }
507
507
  }
508
+
509
+ async rescanOnNewBlock({ walletAccount }) {
510
+ const aci = this.#assetClientInterface
511
+ const asset = this.#asset
512
+ const assetName = this.#asset.name
513
+
514
+ const accountState = await aci.getAccountState({ assetName, walletAccount })
515
+
516
+ const allStoredUtxos = getUtxos({ accountState, asset })
517
+
518
+ const currentTxs = Array.from(await aci.getTxLog({ assetName, walletAccount }))
519
+
520
+ const unconfirmedTxIds = uniq([
521
+ ...allStoredUtxos
522
+ .toArray()
523
+ .filter((utxos) => !utxos.confirmations)
524
+ .map((utxos) => utxos.txId),
525
+ ...currentTxs.filter((tx) => !tx.dropped && !tx.confirmations).map((tx) => tx.txId),
526
+ ])
527
+
528
+ const confirmationsList = (
529
+ await Promise.all(
530
+ unconfirmedTxIds.map(async (txId) => {
531
+ const txStatus = await this.#insightClient.fetchTx(txId)
532
+ if (!txStatus?.confirmations) {
533
+ return undefined
534
+ }
535
+ return { txId, confirmations: txStatus.confirmations }
536
+ })
537
+ )
538
+ ).filter(Boolean)
539
+
540
+ const updatedPropertiesTxs = currentTxs
541
+ .map((tx) => {
542
+ const updatedProperties = {}
543
+ const confirmations = confirmationsList.find(({ txId }) => tx.txId === txId)?.confirmations
544
+ if (!tx.dropped && !tx.confirmations && confirmations > 0) {
545
+ updatedProperties.confirmations = confirmations
546
+ }
547
+ return { txId: tx.txId, ...updatedProperties }
548
+ })
549
+ .filter((tx) => Object.keys(tx).length > 1)
550
+
551
+ const txConfirmedUtxos = confirmationsList.length
552
+ ? allStoredUtxos.updateConfirmations(confirmationsList)
553
+ : null
554
+
555
+ return {
556
+ utxos: txConfirmedUtxos,
557
+ txsToUpdate: updatedPropertiesTxs,
558
+ }
559
+ }
508
560
  }
@@ -94,7 +94,6 @@ export class Monitor extends BaseMonitor {
94
94
  }
95
95
 
96
96
  #connectWS = async (wsUrl) => {
97
- const aci = this.aci
98
97
  if (this.#ws) {
99
98
  this.#ws.close()
100
99
  this.#ws = null
@@ -125,46 +124,7 @@ export class Monitor extends BaseMonitor {
125
124
  )
126
125
  this.tickWalletAccounts({ walletAccount })
127
126
  })
128
- this.#ws.on('block', async () => {
129
- await this.#yieldToUI(100)
130
- const assetName = this.asset.name
131
-
132
- const walletAccounts = await aci.getWalletAccounts({ assetName })
133
- for (const walletAccount of walletAccounts) {
134
- const currentTxs = Array.from(await aci.getTxLog({ assetName, walletAccount }))
135
- const updatedPropertiesTxs = await Promise.all(
136
- currentTxs.map(async (tx) => {
137
- if (tx.dropped || (tx.confirmations && tx.confirmations > 0)) return null
138
- const txStatus = await this.#insightClient.fetchTx(tx.txId)
139
- if (!txStatus) return null
140
- if (txStatus.confirmations <= 0) return null
141
- return { txId: tx.txId, confirmations: txStatus.confirmations }
142
- })
143
- ).then((res) => res.filter((status) => !!status))
144
- if (updatedPropertiesTxs.length) {
145
- await this.updateTxLog({ assetName, walletAccount, logItems: updatedPropertiesTxs })
146
-
147
- const accountState = await aci.getAccountState({ assetName, walletAccount })
148
-
149
- const utxos = accountState.utxos.updateConfirmations(updatedPropertiesTxs)
150
- await aci.updateAccountState({
151
- accountState,
152
- assetName,
153
- walletAccount,
154
- newData: { utxos },
155
- })
156
-
157
- if (['bitcoin', 'bitcoinregtest', 'bitcointestnet'].includes(assetName)) {
158
- updateUnconfirmedAncestorData({
159
- asset: this.asset,
160
- walletAccount,
161
- accountState,
162
- insightClient: this.#insightClient,
163
- })
164
- }
165
- }
166
- }
167
- })
127
+ this.#ws.on('block', () => this.onNewBlock())
168
128
  this.#ws.on('disconnect', () => {
169
129
  this.#logWsStatus('disconnect')
170
130
  this.timer.setNewInterval(this.interval)
@@ -175,6 +135,42 @@ export class Monitor extends BaseMonitor {
175
135
  })
176
136
  }
177
137
 
138
+ async onNewBlock() {
139
+ await this.#yieldToUI(100)
140
+ const aci = this.aci
141
+ const asset = this.asset
142
+ const assetName = asset.name
143
+ const walletAccounts = await aci.getWalletAccounts({ assetName })
144
+ for (const walletAccount of walletAccounts) {
145
+ const { txsToUpdate, utxos } = await this.#scanner.rescanOnNewBlock({
146
+ walletAccount,
147
+ })
148
+
149
+ if (utxos) {
150
+ await aci.updateAccountState({
151
+ assetName,
152
+ walletAccount,
153
+ newData: {
154
+ utxos,
155
+ },
156
+ })
157
+ }
158
+
159
+ if (txsToUpdate.length) {
160
+ const accountState = await aci.getAccountState({ assetName, walletAccount })
161
+ await this.updateTxLog({ assetName, walletAccount, logItems: txsToUpdate })
162
+ if (['bitcoin', 'bitcoinregtest', 'bitcointestnet'].includes(assetName)) {
163
+ updateUnconfirmedAncestorData({
164
+ asset,
165
+ walletAccount,
166
+ accountState,
167
+ insightClient: this.#insightClient,
168
+ })
169
+ }
170
+ }
171
+ }
172
+ }
173
+
178
174
  #subscribeToNewAddresses = async () => {
179
175
  const newAddressesByWalletAccount = await this.#getReceiveAddressesByWalletAccount()
180
176
  if (!isEqual(newAddressesByWalletAccount, this.#addressesByWalletAccount)) {
@@ -92,21 +92,10 @@ export const getSizeAndChangeScriptFactory = ({ bitcoinJsLib = defaultBitcoinjsL
92
92
  assert(rawTx, 'rawTx is required')
93
93
  assert(typeof changeUtxoIndex === 'number', 'changeUtxoIndex must be a number')
94
94
 
95
- const getSize = (tx) => {
96
- if (typeof tx.size === 'number') return tx.size
97
- if (typeof tx.virtualSize === 'function') {
98
- return tx.virtualSize()
99
- }
100
- if (typeof tx.virtualSize === 'number') {
101
- return tx.virtualSize
102
- }
103
- return undefined
104
- }
105
-
106
95
  if (tx) {
107
96
  return {
108
- script: tx.outs?.[changeUtxoIndex]?.script.toString('hex'),
109
- size: getSize(tx),
97
+ script: tx.outs?.[changeUtxoIndex]?.script,
98
+ size: tx.virtualSize,
110
99
  }
111
100
  }
112
101
  // Trezor doesn't return tx!! we need to reparse it!
@@ -114,7 +103,7 @@ export const getSizeAndChangeScriptFactory = ({ bitcoinJsLib = defaultBitcoinjsL
114
103
  try {
115
104
  return {
116
105
  script: parsedTx.outs?.[changeUtxoIndex]?.script.toString('hex'),
117
- size: getSize(parsedTx),
106
+ size: tx.virtualSize?.(),
118
107
  }
119
108
  } catch (e) {
120
109
  console.warn(
@@ -24,6 +24,16 @@ const canParseTx = (rawTxBuffer) => {
24
24
  }
25
25
  }
26
26
 
27
+ export const serializeTx = ({ tx }) => {
28
+ // for desktop compatibility
29
+ return {
30
+ virtualSize: tx.virtualSize?.(),
31
+ outs: tx.outs?.map((out) => ({
32
+ script: out.script.toString('hex'),
33
+ })),
34
+ }
35
+ }
36
+
27
37
  export const signTxFactory = ({ assetName, resolvePurpose, keys, coinInfo, network, ecc }) => {
28
38
  assert(assetName, 'assetName is required')
29
39
  assert(resolvePurpose, 'resolvePurpose is required')
@@ -134,6 +144,6 @@ export const signTxFactory = ({ assetName, resolvePurpose, keys, coinInfo, netwo
134
144
  const txId = tx.getId()
135
145
 
136
146
  // tx needs to be serializable for desktop RPC send => sign communication
137
- return { rawTx, txId, tx: { ...tx, virtualSize: tx.virtualSize?.() } }
147
+ return { rawTx, txId, tx: serializeTx({ tx }) }
138
148
  }
139
149
  }
@@ -1 +1 @@
1
- export { signTxFactory } from './default-create-tx'
1
+ export { signTxFactory, serializeTx } from './default-create-tx'
@@ -12,18 +12,12 @@ export function getUtxos({ accountState, asset }) {
12
12
  )
13
13
  }
14
14
 
15
- export function getConfirmedUtxos({ asset, utxos }) {
16
- assert(asset, 'asset is required')
15
+ export function getConfirmedUtxos({ utxos }) {
17
16
  assert(utxos, 'utxos is required')
18
- const currency = asset.currency
19
- return UtxoCollection.fromArray(
20
- utxos.toArray().filter(({ confirmations }) => confirmations > 0),
21
- { currency }
22
- )
17
+ return utxos.filter(({ confirmations }) => confirmations > 0)
23
18
  }
24
19
 
25
- export function getConfirmedOrRfbDisabledUtxos({ asset, utxos, allowUnconfirmedRbfEnabledUtxos }) {
26
- assert(asset, 'asset is required')
20
+ export function getConfirmedOrRfbDisabledUtxos({ utxos, allowUnconfirmedRbfEnabledUtxos }) {
27
21
  assert(utxos, 'utxos is required')
28
22
  assert(
29
23
  allowUnconfirmedRbfEnabledUtxos !== undefined,
@@ -32,11 +26,7 @@ export function getConfirmedOrRfbDisabledUtxos({ asset, utxos, allowUnconfirmedR
32
26
  if (allowUnconfirmedRbfEnabledUtxos) {
33
27
  return utxos
34
28
  }
35
- const currency = asset.currency
36
- return UtxoCollection.fromArray(
37
- utxos.toArray().filter((utxo) => utxo.confirmations > 0 || !utxo.rbfEnabled),
38
- { currency }
39
- )
29
+ return utxos.filter((utxo) => utxo.confirmations > 0 || !utxo.rbfEnabled)
40
30
  }
41
31
 
42
32
  export function getUsableUtxos({ asset, utxos, feeData, txSet }) {
@@ -58,8 +48,5 @@ export function getUsableUtxos({ asset, utxos, feeData, txSet }) {
58
48
  })
59
49
  return largeUnconfirmedTxs.size === 0
60
50
  ? utxos
61
- : UtxoCollection.fromArray(
62
- utxos.toArray().filter((utxo) => !largeUnconfirmedTxs.has(utxo.txId)),
63
- { currency: asset.currency }
64
- )
51
+ : utxos.filter((utxo) => !largeUnconfirmedTxs.has(utxo.txId))
65
52
  }