@exodus/bitcoin-api 2.6.8-hiro.1 → 2.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/bitcoin-api",
3
- "version": "2.6.8-hiro.1",
3
+ "version": "2.6.8",
4
4
  "description": "Exodus bitcoin-api",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -42,5 +42,6 @@
42
42
  "@scure/base": "^1.1.3",
43
43
  "@scure/btc-signer": "^1.1.0",
44
44
  "jest-when": "^3.5.1"
45
- }
45
+ },
46
+ "gitHead": "eae322a1c75f9cdaebc919af510619cb59ba4c0e"
46
47
  }
@@ -10,7 +10,6 @@ export function createAccountState({ asset, ordinalsEnabled = false, brc20Enable
10
10
 
11
11
  if (ordinalsEnabled) {
12
12
  defaults.ordinalsUtxos = empty
13
- defaults.knownBalanceUtxoIds = []
14
13
  }
15
14
 
16
15
  if (brc20Enabled) {
@@ -67,6 +67,7 @@ export class BitcoinMonitorScanner {
67
67
 
68
68
  const storedUtxos = getUtxos({ asset, accountState })
69
69
  const storedOrdinalUtxos = getOrdinalsUtxos({ asset, accountState })
70
+ const ordinalAddress = await this.getOrdinalAddress({ walletAccount })
70
71
 
71
72
  const currentStoredUtxos = storedUtxos.union(storedOrdinalUtxos)
72
73
 
@@ -89,9 +90,7 @@ export class BitcoinMonitorScanner {
89
90
  : (txs) =>
90
91
  txs.filter((tx) => {
91
92
  const txItem = currentTxs.get(tx.txid)
92
- const confirmed = txItem && txItem.confirmed
93
- const inscriptionsIndexed = txItem?.data?.inscriptionsIndexed
94
- return confirmed && (!this.#ordinalsEnabled || inscriptionsIndexed)
93
+ return txItem && txItem.confirmed
95
94
  }).length >= txs.length
96
95
 
97
96
  const unusedAddressIndexes = await assetClientInterface.getUnusedAddressIndexes({
@@ -345,15 +344,6 @@ export class BitcoinMonitorScanner {
345
344
  currencies: { [assetName]: currency },
346
345
  }
347
346
 
348
- if (this.#ordinalsEnabled) {
349
- txLogItem.data = {
350
- ...txLogItem.data,
351
- inscriptionsIndexed: txItem.inscriptionsIndexed,
352
- sentInscriptions: [],
353
- receivedInscriptions: [],
354
- }
355
- }
356
-
357
347
  let from = []
358
348
 
359
349
  // if txItem.vin has an address that matches ours, means we've spent this tx
@@ -371,13 +361,8 @@ export class BitcoinMonitorScanner {
371
361
  txLogItem.coinAmount = txLogItem.coinAmount.sub(currency.defaultUnit(vin.value))
372
362
  isSent = true
373
363
  txLogItem.data.sent = []
374
- if (this.#ordinalsEnabled && vin.inscriptions) {
375
- txLogItem.data.sentInscriptions.push(
376
- ...vin.inscriptions.map((i) => ({
377
- ...i,
378
- value: currency.defaultUnit(vin.value).toBaseNumber(),
379
- }))
380
- )
364
+ if (ordinalAddress && address.toString() === ordinalAddress.toString()) {
365
+ txLogItem.data.isInscriptionSent = true
381
366
  }
382
367
 
383
368
  // this is only used to exclude the utxos in the reducer which is why we don't care about the other fields
@@ -447,13 +432,8 @@ export class BitcoinMonitorScanner {
447
432
  txLogItem.data.changeAddress = address
448
433
  }
449
434
 
450
- if (this.#ordinalsEnabled && vout.inscriptions) {
451
- txLogItem.data.receivedInscriptions.push(
452
- ...vout.inscriptions.map((i) => ({
453
- ...i,
454
- value: currency.defaultUnit(vout.value).toBaseNumber(),
455
- }))
456
- )
435
+ if (ordinalAddress && address.toString() === ordinalAddress.toString()) {
436
+ txLogItem.data.isInscriptionReceived = true
457
437
  }
458
438
 
459
439
  // it was sent to us...
@@ -470,11 +450,6 @@ export class BitcoinMonitorScanner {
470
450
  rbfEnabled: txItem.rbf,
471
451
  }
472
452
 
473
- if (this.#ordinalsEnabled) {
474
- output.inscriptionsIndexed = txItem.inscriptionsIndexed
475
- output.inscriptions = vout.inscriptions || []
476
- }
477
-
478
453
  if (this.#shouldExcludeVoutUtxo({ asset, output, txItem, vout })) {
479
454
  return
480
455
  }
@@ -565,13 +540,10 @@ export class BitcoinMonitorScanner {
565
540
  return !isEqual(chain, originalChain.chain)
566
541
  })
567
542
 
568
- const ordinalAddress = await this.getOrdinalAddress({ walletAccount })
569
543
  const utxosData = utxoCol
570
544
  ? partitionUtxos({
571
545
  allUtxos: utxoCol,
572
- ordinalsEnabled: this.#ordinalsEnabled,
573
546
  ordinalAddress,
574
- knownBalanceUtxoIds: accountState.knownBalanceUtxoIds,
575
547
  })
576
548
  : {}
577
549
 
@@ -631,9 +603,7 @@ export class BitcoinMonitorScanner {
631
603
 
632
604
  const { utxos, ordinalsUtxos } = partitionUtxos({
633
605
  allUtxos: txConfirmedUtxos,
634
- ordinalsEnabled: this.#ordinalsEnabled,
635
606
  ordinalAddress: await this.getOrdinalAddress({ walletAccount }),
636
- knownBalanceUtxoIds: accountState.knownBalanceUtxoIds,
637
607
  })
638
608
 
639
609
  return {
@@ -14,12 +14,7 @@ import {
14
14
  createOutput as dogecoinCreateOutput,
15
15
  } from './dogecoin'
16
16
  import { findUnconfirmedSentRbfTxs } from '../tx-utils'
17
- import {
18
- getOrdinalsUtxos,
19
- getTransferOrdinalsUtxos,
20
- getUsableUtxos,
21
- getUtxos,
22
- } from '../utxos-utils'
17
+ import { getOrdinalsUtxos, getUsableUtxos, getUtxos } from '../utxos-utils'
23
18
 
24
19
  import * as defaultBitcoinjsLib from '@exodus/bitcoinjs-lib'
25
20
 
@@ -137,7 +132,6 @@ export const createAndBroadcastTXFactory = ({
137
132
  getFeeEstimator,
138
133
  getSizeAndChangeScript = getSizeAndChangeScriptFactory(), // for decred customizations
139
134
  allowUnconfirmedRbfEnabledUtxos,
140
- ordinalsEnabled = false,
141
135
  }) => async (
142
136
  { asset: maybeToken, walletAccount, address, amount: tokenAmount, options },
143
137
  { assetClientInterface }
@@ -173,11 +167,6 @@ export const createAndBroadcastTXFactory = ({
173
167
 
174
168
  const inscriptionIds = nft?.tokenId ? [nft?.tokenId] : brc20 ? brc20.inscriptionIds : undefined
175
169
 
176
- assert(
177
- ordinalsEnabled || !inscriptionIds,
178
- 'inscriptions cannot be sent when ordinalsEnabled=false '
179
- )
180
-
181
170
  const shuffle = (list) => {
182
171
  return inscriptionIds ? list : doShuffle(list) // don't shuffle when sending ordinal!!!!
183
172
  }
@@ -203,9 +192,26 @@ export const createAndBroadcastTXFactory = ({
203
192
 
204
193
  const currentOrdinalsUtxos = getOrdinalsUtxos({ accountState, asset })
205
194
  const transferOrdinalsUtxos = inscriptionIds
206
- ? getTransferOrdinalsUtxos({ inscriptionIds, ordinalsUtxos: currentOrdinalsUtxos })
195
+ ? currentOrdinalsUtxos.filter((utxo) => inscriptionIds.includes(utxo.inscriptionId)) /// this could be bulk transfer if multiple inscription ids are provided
207
196
  : undefined
208
197
 
198
+ if (inscriptionIds) {
199
+ assert(
200
+ transferOrdinalsUtxos?.size === inscriptionIds.length,
201
+ `Expected ordinal utxos ${inscriptionIds.length}. Found: ${transferOrdinalsUtxos?.size || 0}`
202
+ )
203
+
204
+ // const unconfirmedOrdinalUtxos = transferOrdinalsUtxos
205
+ // .toArray()
206
+ // .filter((ordinalUtxo) => !(ordinalUtxo.confirmations > 0))
207
+ // assert(
208
+ // !unconfirmedOrdinalUtxos.length,
209
+ // `OrdinalUtxo with inscription ids ${unconfirmedOrdinalUtxos
210
+ // .map((utxo) => utxo.inscriptionId)
211
+ // .join(', ')} have not confirmed yet`
212
+ // )
213
+ }
214
+
209
215
  const insightClient = asset.baseAsset.insightClient
210
216
  const currency = asset.currency
211
217
  const feeData = await assetClientInterface.getFeeConfig({ assetName })
@@ -428,8 +434,6 @@ export const createAndBroadcastTXFactory = ({
428
434
 
429
435
  const { script, size } = getSizeAndChangeScript({ assetName, tx, rawTx, changeUtxoIndex, txId })
430
436
 
431
- // for ordinals, used to allow users spending change utxos even when unconfirmed and ordinals are unknown
432
- const knownBalanceUtxoIds = accountState.knownBalanceUtxoIds || []
433
437
  let remainingUtxos = usableUtxos.difference(selectedUtxos)
434
438
  if (changeUtxoIndex !== -1) {
435
439
  const address = Address.create(ourAddress.address, ourAddress.meta)
@@ -442,8 +446,6 @@ export const createAndBroadcastTXFactory = ({
442
446
  confirmations: 0,
443
447
  rbfEnabled,
444
448
  }
445
-
446
- knownBalanceUtxoIds.push(`${changeUtxo.txId}:${changeUtxo.vout}`.toLowerCase())
447
449
  remainingUtxos = remainingUtxos.addUtxo(changeUtxo)
448
450
  }
449
451
  if (replaceTx) {
@@ -460,7 +462,6 @@ export const createAndBroadcastTXFactory = ({
460
462
  newData: {
461
463
  utxos: remainingUtxos,
462
464
  ordinalsUtxos: remainingOrdinalsUtxos,
463
- knownBalanceUtxoIds,
464
465
  },
465
466
  })
466
467
 
@@ -521,16 +522,7 @@ export const createAndBroadcastTXFactory = ({
521
522
  inputs: selectedUtxos.toJSON(),
522
523
  replacedTxId: replaceTx ? replaceTx.txId : undefined,
523
524
  nftId: nft ? `${assetName}:${nft.tokenId}` : undefined, // it allows BE to load the nft info while the nft is in transit
524
- inscriptionsIndexed: ordinalsEnabled ? true : undefined,
525
- sentInscriptions: inscriptionIds
526
- ? inscriptionIds.map((inscriptionId) => {
527
- return {
528
- inscriptionId,
529
- offset: 0,
530
- value: 0,
531
- }
532
- })
533
- : undefined,
525
+ inscriptionIds: inscriptionIds,
534
526
  },
535
527
  },
536
528
  ],
@@ -5,46 +5,6 @@ import assert from 'minimalistic-assert'
5
5
 
6
6
  const MAX_ORDINAL_VALUE_POSTAGE = 10000
7
7
 
8
- export function getTransferOrdinalsUtxos({ inscriptionIds, ordinalsUtxos }) {
9
- const transferOrdinalsUtxos = ordinalsUtxos.filter((utxo) =>
10
- utxo.inscriptions?.some((i) => inscriptionIds.includes(i.inscriptionId))
11
- )
12
- const unsafeInscriptions = transferOrdinalsUtxos.toArray().flatMap(
13
- (utxo) =>
14
- utxo.inscriptions?.filter((i) => {
15
- const validInscription = isValidInscription({
16
- value: utxo.value.toBaseNumber(),
17
- offset: i.offset,
18
- })
19
- return validInscription && !inscriptionIds.includes(i.inscriptionId)
20
- }) || []
21
- )
22
- assert(
23
- !unsafeInscriptions.length,
24
- `The following inscriptions are unsafe ${unsafeInscriptions.map(
25
- (i) => i.inscriptionId
26
- )} when ${inscriptionIds} should be spent`
27
- )
28
-
29
- const transferInscriptionIds = transferOrdinalsUtxos
30
- .toArray()
31
- .flatMap((utxo) => utxo.inscriptions)
32
- .filter(({ inscriptionId }) => inscriptionIds.includes(inscriptionId))
33
-
34
- assert(
35
- transferInscriptionIds.length === inscriptionIds.length,
36
- `Expected inscriptions ${inscriptionIds.length}. Found: ${transferInscriptionIds.length}`
37
- )
38
- return transferOrdinalsUtxos
39
- }
40
-
41
- export function isValidInscription({ value, offset }) {
42
- assert(typeof value === 'number', 'value must be a number')
43
- assert(typeof offset === 'number', 'offset must be a number')
44
- // value >= 0 in case offset, alternatively convert to string/ln
45
- return (value >= 0 && value <= MAX_ORDINAL_VALUE_POSTAGE) || offset === 0
46
- }
47
-
48
8
  export function getUtxos({ accountState, asset }) {
49
9
  return (
50
10
  accountState?.utxos ||
@@ -63,41 +23,31 @@ export function getOrdinalsUtxos({ accountState, asset }) {
63
23
  )
64
24
  }
65
25
 
66
- export function getValidInscriptions({ utxo }) {
67
- return (utxo.inscriptions || []).filter((i) =>
68
- isValidInscription({ value: utxo.value.toBaseNumber(), offset: i.offset })
69
- )
70
- }
71
-
72
- function isOrdinalUtxo({ utxo, ordinalsEnabled, knownBalanceUtxoIds }) {
73
- if (!ordinalsEnabled) {
26
+ function isOrdinalUtxo({ utxo, ordinalAddress }) {
27
+ if (utxo.inscriptionId) {
28
+ return true
29
+ }
30
+ if (!ordinalAddress) {
31
+ // exclude utxos splitting
74
32
  return false
75
33
  }
76
34
 
77
- const utxoId = `${utxo.txId}:${utxo.vout}`.toLowerCase()
78
-
79
- if (knownBalanceUtxoIds?.includes(utxoId) && !utxo.inscriptionsIndexed) {
80
- return false // this allows users see and spend change balance after sending before hiro confirmation
35
+ if (utxo.address.toString() === ordinalAddress.toString()) {
36
+ return true // we assume any utxo to the ordinal address is a ordinal utxos just in case
81
37
  }
82
38
 
83
- if (!utxo.inscriptionsIndexed) {
84
- return true
39
+ if (utxo.confirmations) {
40
+ return false
85
41
  }
86
42
 
87
- const validInscriptions = getValidInscriptions({ utxo })
88
- return validInscriptions.length > 0
43
+ return utxo.value.toBaseNumber() <= MAX_ORDINAL_VALUE_POSTAGE // while unconfirmed, put < 10000- sats in the ordinal utxos box just in case
89
44
  }
90
45
 
91
- export function partitionUtxos({ allUtxos, ordinalsEnabled, knownBalanceUtxoIds }) {
46
+ export function partitionUtxos({ allUtxos, ordinalAddress }) {
92
47
  assert(allUtxos, 'allUtxos is required')
93
- // assert(ordinalAddress, 'ordinalAddress is required') // not used atm we may need to tune by ordinalAddress when unconfirmed or rubbish inscriptions
94
48
  return {
95
- utxos: allUtxos.filter(
96
- (utxo) => !isOrdinalUtxo({ utxo, ordinalsEnabled, knownBalanceUtxoIds })
97
- ),
98
- ordinalsUtxos: allUtxos.filter((utxo) =>
99
- isOrdinalUtxo({ utxo, ordinalsEnabled, knownBalanceUtxoIds })
100
- ),
49
+ utxos: allUtxos.filter((utxo) => !isOrdinalUtxo({ utxo, ordinalAddress })),
50
+ ordinalsUtxos: allUtxos.filter((utxo) => isOrdinalUtxo({ utxo, ordinalAddress })),
101
51
  }
102
52
  }
103
53