@exodus/bitcoin-api 2.7.8 → 2.8.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.8",
3
+ "version": "2.8.1",
4
4
  "description": "Exodus bitcoin-api",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -32,7 +32,6 @@
32
32
  "coininfo": "5.1.0",
33
33
  "delay": "4.0.1",
34
34
  "ecpair": "2.0.1",
35
- "querystring": "0.2.0",
36
35
  "socket.io-client": "2.1.1",
37
36
  "tiny-secp256k1": "1.1.3",
38
37
  "url-join": "4.0.0"
@@ -43,5 +42,5 @@
43
42
  "@scure/btc-signer": "^1.1.0",
44
43
  "jest-when": "^3.5.1"
45
44
  },
46
- "gitHead": "bcd735ed4e9775e3436257966d80fc524c1fa312"
45
+ "gitHead": "652eb821c1fe5b2bd561710d05fcb569d9f4a137"
47
46
  }
@@ -11,6 +11,7 @@ export function createAccountState({ asset, ordinalsEnabled = false, brc20Enable
11
11
  if (ordinalsEnabled) {
12
12
  defaults.ordinalsUtxos = empty
13
13
  defaults.knownBalanceUtxoIds = []
14
+ defaults.mustAvoidUtxoIds = []
14
15
  }
15
16
 
16
17
  if (brc20Enabled) {
@@ -3,7 +3,7 @@ import assert from 'minimalistic-assert'
3
3
  import { findUnconfirmedSentRbfTxs } from '../tx-utils'
4
4
  import { getUsableUtxos, getUtxos } from '../utxos-utils'
5
5
 
6
- import { BumpType } from '@exodus/bitcoin-lib/lib/selectors/get-can-bump-tx-factory'
6
+ import { BumpType } from '@exodus/bitcoin-lib'
7
7
 
8
8
  export const ASSET_NAMES = ['bitcoin', 'bitcoinregtest', 'bitcointestnet']
9
9
 
@@ -1,6 +1,5 @@
1
1
  /* global fetch */
2
2
  import urlJoin from 'url-join'
3
- import qs from 'querystring'
4
3
  import delay from 'delay'
5
4
  import { isEmpty } from 'lodash'
6
5
 
@@ -133,7 +132,7 @@ export default class InsightAPIClient {
133
132
  if (!Array.isArray(addrs) || addrs.length === 0) return { items: [], totalItems: 0 }
134
133
 
135
134
  options = { noScriptSig: 1, noAsm: 1, noSpent: 0, from: 0, to: 10, ...options }
136
- const url = `${urlJoin(this._baseURL, `/addrs/txs`)}?${qs.stringify(options)}`
135
+ const url = `${urlJoin(this._baseURL, `/addrs/txs`)}?${new URLSearchParams(options)}`
137
136
 
138
137
  const fetchOptions = {
139
138
  method: 'post',
@@ -590,12 +590,19 @@ export class BitcoinMonitorScanner {
590
590
  })
591
591
 
592
592
  const ordinalAddress = await this.getOrdinalAddress({ walletAccount })
593
+
594
+ // latest account state, in case knownBalanceUtxoIds or mustAvoidUtxoIds gets updated in on another promise
595
+ const latestAccountState = await assetClientInterface.getAccountState({
596
+ assetName,
597
+ walletAccount,
598
+ })
593
599
  const utxosData = utxoCol
594
600
  ? partitionUtxos({
595
601
  allUtxos: utxoCol,
596
602
  ordinalsEnabled: this.#ordinalsEnabled,
597
603
  ordinalAddress,
598
- knownBalanceUtxoIds: accountState.knownBalanceUtxoIds,
604
+ knownBalanceUtxoIds: latestAccountState.knownBalanceUtxoIds,
605
+ mustAvoidUtxoIds: latestAccountState.mustAvoidUtxoIds,
599
606
  })
600
607
  : {}
601
608
 
@@ -653,11 +660,20 @@ export class BitcoinMonitorScanner {
653
660
 
654
661
  const txConfirmedUtxos = allStoredUtxos.updateConfirmations(confirmationsList)
655
662
 
663
+ const ordinalAddress = await this.getOrdinalAddress({ walletAccount })
664
+
665
+ // latest account state, in case knownBalanceUtxoIds or mustAvoidUtxoIds gets updated in on another promise
666
+ const latestAccountState = await aci.getAccountState({
667
+ assetName,
668
+ walletAccount,
669
+ })
670
+
656
671
  const { utxos, ordinalsUtxos } = partitionUtxos({
657
672
  allUtxos: txConfirmedUtxos,
658
673
  ordinalsEnabled: this.#ordinalsEnabled,
659
- ordinalAddress: await this.getOrdinalAddress({ walletAccount }),
660
- knownBalanceUtxoIds: accountState.knownBalanceUtxoIds,
674
+ ordinalAddress: ordinalAddress,
675
+ knownBalanceUtxoIds: latestAccountState.knownBalanceUtxoIds,
676
+ mustAvoidUtxoIds: latestAccountState.mustAvoidUtxoIds,
661
677
  })
662
678
 
663
679
  return {
@@ -5,7 +5,7 @@ import doShuffle from 'lodash/shuffle'
5
5
  import { UtxoCollection, Address } from '@exodus/models'
6
6
  import { retry } from '@exodus/simple-retry'
7
7
  import { selectUtxos } from '../fee/utxo-selector'
8
- import getTxSequence from '@exodus/bitcoin-lib/lib/utils/get-tx-sequence'
8
+ import { getTxSequence } from '@exodus/bitcoin-lib'
9
9
 
10
10
  import { parseCurrency } from '../fee/fee-utils'
11
11
 
@@ -33,8 +33,12 @@ export function createSignWithWallet({
33
33
  // dApps request to sign only specific transaction inputs.
34
34
  if (!inputInfo) continue
35
35
  const { address, sigHash } = inputInfo
36
- // TODO: we can remove SIGHASH_ALL, it is default.
37
- const sigHashTypes = sigHash !== undefined ? [sigHash || Transaction.SIGHASH_ALL] : undefined
36
+ // The sighash value from the PSBT input itself will be used.
37
+ // This list just represents possible sighash values the inputs can have.
38
+ const allowedSigHashTypes =
39
+ sigHash !== undefined
40
+ ? [sigHash, Transaction.SIGHASH_ALL] // `SIGHASH_DEFAULT` is a default safe sig hash, always allow it.
41
+ : undefined
38
42
  const { key, purpose, publicKey } = getKeyAndPurpose(address)
39
43
 
40
44
  const isP2SH = purpose === 49
@@ -67,7 +71,7 @@ export function createSignWithWallet({
67
71
 
68
72
  // desktop / BE / mobile with bip-schnorr signing
69
73
  const signingKey = isTaprootAddress ? tweakSigner({ signer: key, ECPair, network }) : key
70
- await psbt.signInputAsync(index, toAsyncSigner({ keyPair: signingKey }), sigHashTypes)
74
+ await psbt.signInputAsync(index, toAsyncSigner({ keyPair: signingKey }), allowedSigHashTypes)
71
75
  }
72
76
  }
73
77
  }
@@ -5,18 +5,32 @@ import assert from 'minimalistic-assert'
5
5
 
6
6
  const MAX_ORDINAL_VALUE_POSTAGE = 10000
7
7
 
8
+ export const getInscriptionTxId = (inscriptionId) => {
9
+ return inscriptionId.split('i')[0]
10
+ }
11
+
8
12
  export function getTransferOrdinalsUtxos({ inscriptionIds, ordinalsUtxos }) {
9
13
  const transferOrdinalsUtxos = ordinalsUtxos.filter((utxo) =>
10
14
  utxo.inscriptions?.some((i) => inscriptionIds.includes(i.inscriptionId))
11
15
  )
16
+
17
+ // this is for the micky mouse case. It has 2 inscriptions from the same tx but different output
18
+ const inscriptionTxs = inscriptionIds.map(getInscriptionTxId)
19
+
20
+ // https://ordinals.hiro.so/inscription/862ecd0fe343da32d19ff9277639ff71e10d894f55b6dee82dbfb9c158d5d30ci0
21
+ // https://ordinals.hiro.so/inscription/862ecd0fe343da32d19ff9277639ff71e10d894f55b6dee82dbfb9c158d5d30ci1
22
+
12
23
  const unsafeInscriptions = transferOrdinalsUtxos.toArray().flatMap(
13
24
  (utxo) =>
14
- utxo.inscriptions?.filter((i) => {
25
+ utxo.inscriptions?.filter((inscription) => {
15
26
  const validInscription = isValidInscription({
16
27
  value: utxo.value.toBaseNumber(),
17
- offset: i.offset,
28
+ offset: inscription.offset,
18
29
  })
19
- return validInscription && !inscriptionIds.includes(i.inscriptionId)
30
+ return (
31
+ validInscription &&
32
+ !inscriptionTxs.includes(getInscriptionTxId(inscription.inscriptionId))
33
+ )
20
34
  }) || []
21
35
  )
22
36
  assert(
@@ -69,13 +83,23 @@ export function getValidInscriptions({ utxo }) {
69
83
  )
70
84
  }
71
85
 
72
- function isOrdinalUtxo({ utxo, ordinalsEnabled, knownBalanceUtxoIds, ordinalAddress }) {
86
+ function isOrdinalUtxo({
87
+ utxo,
88
+ ordinalsEnabled,
89
+ knownBalanceUtxoIds,
90
+ ordinalAddress,
91
+ mustAvoidUtxoIds,
92
+ }) {
73
93
  if (!ordinalsEnabled) {
74
94
  return false
75
95
  }
76
96
 
77
97
  const utxoId = `${utxo.txId}:${utxo.vout}`.toLowerCase()
78
98
 
99
+ if (mustAvoidUtxoIds && mustAvoidUtxoIds.includes(utxoId)) {
100
+ return true
101
+ }
102
+
79
103
  if (knownBalanceUtxoIds?.includes(utxoId) && !utxo.inscriptionsIndexed) {
80
104
  return false // this allows users see and spend change balance after sending before hiro confirmation
81
105
  }
@@ -98,15 +122,34 @@ function isOrdinalUtxo({ utxo, ordinalsEnabled, knownBalanceUtxoIds, ordinalAddr
98
122
  return hasOrdinals
99
123
  }
100
124
 
101
- export function partitionUtxos({ allUtxos, ordinalsEnabled, knownBalanceUtxoIds, ordinalAddress }) {
125
+ export function partitionUtxos({
126
+ allUtxos,
127
+ ordinalsEnabled,
128
+ knownBalanceUtxoIds,
129
+ ordinalAddress,
130
+ mustAvoidUtxoIds,
131
+ }) {
102
132
  assert(allUtxos, 'allUtxos is required')
103
- if (ordinalsEnabled) assert(ordinalAddress, 'ordinalAddress is required') // not used atm we may need to tune by ordinalAddress when unconfirmed or rubbish inscriptions
133
+ if (ordinalsEnabled) assert(ordinalAddress, 'ordinalAddress is required')
104
134
  return {
105
135
  utxos: allUtxos.filter(
106
- (utxo) => !isOrdinalUtxo({ utxo, ordinalsEnabled, knownBalanceUtxoIds, ordinalAddress })
136
+ (utxo) =>
137
+ !isOrdinalUtxo({
138
+ utxo,
139
+ ordinalsEnabled,
140
+ knownBalanceUtxoIds,
141
+ ordinalAddress,
142
+ mustAvoidUtxoIds,
143
+ })
107
144
  ),
108
145
  ordinalsUtxos: allUtxos.filter((utxo) =>
109
- isOrdinalUtxo({ utxo, ordinalsEnabled, knownBalanceUtxoIds, ordinalAddress })
146
+ isOrdinalUtxo({
147
+ utxo,
148
+ ordinalsEnabled,
149
+ knownBalanceUtxoIds,
150
+ ordinalAddress,
151
+ mustAvoidUtxoIds,
152
+ })
110
153
  ),
111
154
  }
112
155
  }