@exodus/bitcoin-api 2.3.7 → 2.3.8-ordinals.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.3.7",
3
+ "version": "2.3.8-ordinals.1",
4
4
  "description": "Exodus bitcoin-api",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -40,6 +40,5 @@
40
40
  "devDependencies": {
41
41
  "@exodus/bitcoin-meta": "^1.0.1",
42
42
  "jest-when": "^3.5.1"
43
- },
44
- "gitHead": "7a83d2ff5d672615938fba868779293df74ff51d"
43
+ }
45
44
  }
@@ -1,11 +1,18 @@
1
1
  import { AccountState, UtxoCollection } from '@exodus/models'
2
2
 
3
- export function createAccountState({ asset }) {
3
+ export function createAccountState({ asset, ordinalsEnabled = false }) {
4
+ const empty = UtxoCollection.createEmpty({
5
+ currency: asset.currency,
6
+ })
7
+ const defaults = ordinalsEnabled
8
+ ? {
9
+ utxos: empty,
10
+ ordinalsUtxos: empty,
11
+ }
12
+ : {
13
+ utxos: empty,
14
+ }
4
15
  return class BitcoinAccountState extends AccountState {
5
- static defaults = {
6
- utxos: UtxoCollection.createEmpty({
7
- currency: asset.currency,
8
- }),
9
- }
16
+ static defaults = defaults
10
17
  }
11
18
  }
@@ -0,0 +1,24 @@
1
+ import assert from 'minimalistic-assert'
2
+
3
+ export function isOrdinalAddress(address, ordinalChainIndex) {
4
+ assert(typeof ordinalChainIndex === 'number', `ordinalChainIndex must be a number`)
5
+ return parsePath(address)[0] === ordinalChainIndex
6
+ }
7
+
8
+ export function isReceiveAddress(address): boolean {
9
+ return parsePath(address)[0] === 0
10
+ }
11
+
12
+ export function isChangeAddress(address): boolean {
13
+ return parsePath(address)[0] === 1
14
+ }
15
+
16
+ function parsePath(address) {
17
+ assert(
18
+ address.meta.path,
19
+ `address parameter ${address} does not have a meta.path. Is it a valid Address object?`
20
+ )
21
+ const path = address.meta.path
22
+ const p1 = path ? path.replace('m/', '').split('/') : ['0', '0']
23
+ return p1.map((i) => parseInt(i, 10))
24
+ }
@@ -14,7 +14,13 @@ export class GetFeeResolver {
14
14
  this.#allowUnconfirmedRbfEnabledUtxos = allowUnconfirmedRbfEnabledUtxos
15
15
  }
16
16
 
17
- getFee = ({ asset, accountState, txSet, feeData, amount, customFee, isSendAll }) => {
17
+ getFee = ({ asset, accountState, txSet, feeData, amount, customFee, isSendAll, nft }) => {
18
+ const inscriptionId = nft?.tokenId
19
+ if (inscriptionId) {
20
+ assert(!amount, 'Only inscriptionId or amount is allowed!!!')
21
+ assert(!isSendAll, 'Only inscriptionId or isSendAll:true is allowed!!!')
22
+ }
23
+
18
24
  const { resolvedFee, extraFee } = this.#getUtxosData({
19
25
  asset,
20
26
  accountState,
@@ -23,6 +29,7 @@ export class GetFeeResolver {
23
29
  amount,
24
30
  customFee,
25
31
  isSendAll,
32
+ inscriptionId,
26
33
  })
27
34
  return { fee: resolvedFee, extraFee }
28
35
  }
package/src/move-funds.js CHANGED
@@ -27,6 +27,16 @@ export const getAddressesFromPrivateKeyFactory = ({ purposes, keys, coinInfo })
27
27
  }
28
28
  }
29
29
 
30
+ export const renderAddresses = (addresses) => {
31
+ if (!addresses.length) {
32
+ return ''
33
+ }
34
+ if (addresses.length === 1) {
35
+ return `${addresses[0]}`
36
+ }
37
+ return `${addresses.slice(0, -1).join(', ')}, or ${addresses[addresses.length - 1]}`
38
+ }
39
+
30
40
  export const moveFundsFactory = ({
31
41
  asset,
32
42
  insightClient,
@@ -93,7 +103,8 @@ export const moveFundsFactory = ({
93
103
  }
94
104
  throw new MoveFundsError('balance-zero', {
95
105
  ...formatProps,
96
- fromAddress: `${addresses.slice(0, -1).join(', ')}, or ${addresses[addresses.length - 1]}`,
106
+ fromAddress: renderAddresses(addresses),
107
+ fromAddresses: addresses.map(String),
97
108
  })
98
109
  }
99
110
 
@@ -1,28 +1,17 @@
1
1
  /* @flow */
2
2
  import { orderTxs } from '../insight-api-client/util'
3
3
  import { Address, UtxoCollection } from '@exodus/models'
4
- import { isEqual, compact, uniq } from 'lodash'
4
+ import { compact, isEqual, uniq } from 'lodash'
5
5
  import ms from 'ms'
6
6
 
7
7
  import assert from 'minimalistic-assert'
8
- import { getUtxos } from '../utxos-utils'
8
+ import { isChangeAddress, isReceiveAddress } from '../address-utils'
9
+
10
+ import { getOrdinalsUtxos, getUtxos, partitionUtxos } from '../utxos-utils'
9
11
 
10
12
  // Time to check whether to drop a sent tx
11
13
  const SENT_TIME_TO_DROP = ms('2m')
12
14
 
13
- function isReceiveAddress(addr: Address): boolean {
14
- return parsePath(addr.meta.path)[0] === 0
15
- }
16
- function isChangeAddress(addr: Address): boolean {
17
- return parsePath(addr.meta.path)[0] === 1
18
- }
19
-
20
- function parsePath(path) {
21
- let p1 = path ? path.replace('m/', '').split('/') : ['0', '0']
22
- p1 = p1.map((i) => parseInt(i, 10))
23
- return p1
24
- }
25
-
26
15
  export class BitcoinMonitorScanner {
27
16
  #asset
28
17
  #insightClient
@@ -30,7 +19,8 @@ export class BitcoinMonitorScanner {
30
19
  #txFetchLimitResolver
31
20
  #shouldExcludeVoutUtxo
32
21
  #yieldToUI
33
-
22
+ #ordinalsEnabled
23
+ #ordinalChainIndex
34
24
  constructor({
35
25
  asset,
36
26
  assetClientInterface,
@@ -38,6 +28,8 @@ export class BitcoinMonitorScanner {
38
28
  yieldToUI = () => {},
39
29
  shouldExcludeVoutUtxo = () => false,
40
30
  txFetchLimitResolver = ({ refresh }) => (refresh ? 50 : 10),
31
+ ordinalsEnabled,
32
+ ordinalChainIndex,
41
33
  }) {
42
34
  assert(asset, 'asset is required!')
43
35
  assert(assetClientInterface, 'assetClientInterface is required!')
@@ -51,6 +43,8 @@ export class BitcoinMonitorScanner {
51
43
  this.#assetClientInterface = assetClientInterface
52
44
  this.#txFetchLimitResolver = txFetchLimitResolver
53
45
  this.#shouldExcludeVoutUtxo = shouldExcludeVoutUtxo
46
+ this.#ordinalsEnabled = ordinalsEnabled
47
+ this.#ordinalChainIndex = ordinalChainIndex
54
48
  }
55
49
 
56
50
  async rescanBlockchainInsight({ walletAccount, refresh }) {
@@ -64,7 +58,11 @@ export class BitcoinMonitorScanner {
64
58
  const accountState = await assetClientInterface.getAccountState({ assetName, walletAccount })
65
59
  const currency = asset.currency
66
60
  const currentTxs = await assetClientInterface.getTxLog({ assetName, walletAccount })
67
- const currentUtxos = getUtxos({ asset, accountState })
61
+
62
+ const regularUtxos = getUtxos({ asset, accountState })
63
+ const ordinalsUtxos = getOrdinalsUtxos({ asset, accountState })
64
+
65
+ const currentUtxos = regularUtxos.union(ordinalsUtxos)
68
66
 
69
67
  const currentTime = new Date().getTime()
70
68
  const unconfirmedTxsToCheck = Array.from(currentTxs).reduce((txs, tx) => {
@@ -221,6 +219,21 @@ export class BitcoinMonitorScanner {
221
219
  })
222
220
  .flat()
223
221
 
222
+ if (
223
+ fetchCount === 0 &&
224
+ this.#ordinalsEnabled &&
225
+ this.#ordinalChainIndex > 1 &&
226
+ purposes.includes(86)
227
+ ) {
228
+ // this is the ordinal address
229
+ chainObjects.push({
230
+ purpose: 86,
231
+ chainIndex: this.#ordinalChainIndex,
232
+ startAddressIndex: 0,
233
+ endAddressIndex: 1,
234
+ })
235
+ }
236
+
224
237
  const addresses = await aggregateAddresses(chainObjects)
225
238
 
226
239
  const txs = await fetchAllTxs(addresses)
@@ -243,6 +256,11 @@ export class BitcoinMonitorScanner {
243
256
  const metaAddressIndex = parseInt(pd[2])
244
257
  const addressString = String(address)
245
258
  const purposeToUpdate = purposeMap[addressString]
259
+
260
+ if (metaChainIndex === this.#ordinalChainIndex && this.#ordinalChainIndex > 1) {
261
+ return
262
+ }
263
+
246
264
  if (!purposeToUpdate) {
247
265
  console.warn(`${assetName}: Cannot resolve purpose from address ${addressString}`)
248
266
  return
@@ -310,6 +328,7 @@ export class BitcoinMonitorScanner {
310
328
  rbfEnabled: txItem.rbf,
311
329
  blocksSeen: 0,
312
330
  },
331
+ currencies: { [assetName]: currency },
313
332
  }
314
333
 
315
334
  let from = []
@@ -345,13 +364,15 @@ export class BitcoinMonitorScanner {
345
364
  ['bitcoin', 'bitcoinregtest', 'bitcointestnet'].includes(asset.name)
346
365
  ) {
347
366
  txLogItem.data.inputs = UtxoCollection.fromArray(
348
- txItem.vin.map((vin) => ({
349
- address: addrMap[vin.addr],
350
- script: asset.address.toScriptPubKey(vin.addr).toString('hex'),
351
- txId: vin.txid,
352
- vout: vin.vout,
353
- value: currency.defaultUnit(vin.value || 0),
354
- }))
367
+ txItem.vin
368
+ .filter((vin) => addrMap[vin.addr])
369
+ .map((vin) => ({
370
+ address: addrMap[vin.addr],
371
+ script: asset.address.toScriptPubKey(vin.addr).toString('hex'),
372
+ txId: vin.txid,
373
+ vout: vin.vout,
374
+ value: currency.defaultUnit(vin.value || 0),
375
+ }))
355
376
  ).toJSON()
356
377
  }
357
378
 
@@ -501,7 +522,9 @@ export class BitcoinMonitorScanner {
501
522
  return {
502
523
  txsToUpdate: existingTxs,
503
524
  txsToAdd: newTxs,
504
- utxos: utxoCol,
525
+ ...(utxoCol
526
+ ? partitionUtxos({ allUtxos: utxoCol, ordinalChainIndex: this.#ordinalChainIndex })
527
+ : {}),
505
528
  changedUnusedAddressIndexes,
506
529
  }
507
530
  }
@@ -513,12 +536,14 @@ export class BitcoinMonitorScanner {
513
536
 
514
537
  const accountState = await aci.getAccountState({ assetName, walletAccount })
515
538
 
516
- const allStoredUtxos = getUtxos({ accountState, asset })
539
+ const storedUtxos = getUtxos({ accountState, asset })
540
+ const storedOrdinalsUtxos = getOrdinalsUtxos({ accountState, asset })
541
+ const allStoredUtxos = storedUtxos.union(storedOrdinalsUtxos)
517
542
 
518
543
  const currentTxs = Array.from(await aci.getTxLog({ assetName, walletAccount }))
519
544
 
520
545
  const unconfirmedTxIds = uniq([
521
- ...allStoredUtxos
546
+ ...storedUtxos
522
547
  .toArray()
523
548
  .filter((utxos) => !utxos.confirmations)
524
549
  .map((utxos) => utxos.txId),
@@ -548,12 +573,16 @@ export class BitcoinMonitorScanner {
548
573
  })
549
574
  .filter((tx) => Object.keys(tx).length > 1)
550
575
 
551
- const txConfirmedUtxos = confirmationsList.length
552
- ? allStoredUtxos.updateConfirmations(confirmationsList)
553
- : null
576
+ const txConfirmedUtxos = allStoredUtxos.updateConfirmations(confirmationsList)
577
+
578
+ const { utxos, ordinalsUtxos } = partitionUtxos({
579
+ allUtxos: txConfirmedUtxos,
580
+ ordinalChainIndex: this.#ordinalChainIndex,
581
+ })
554
582
 
555
583
  return {
556
- utxos: txConfirmedUtxos,
584
+ utxos: utxos.equals(storedUtxos) ? null : utxos,
585
+ ordinalsUtxos: ordinalsUtxos.equals(storedOrdinalsUtxos) ? null : ordinalsUtxos,
557
586
  txsToUpdate: updatedPropertiesTxs,
558
587
  }
559
588
  }
@@ -1,5 +1,5 @@
1
1
  import assert from 'minimalistic-assert'
2
- import { isEmpty, isEqual } from 'lodash'
2
+ import { isEmpty, isEqual, pickBy } from 'lodash'
3
3
 
4
4
  import { BaseMonitor } from '@exodus/asset-lib'
5
5
  import InsightWSClient from '../insight-api-client/ws'
@@ -142,17 +142,21 @@ export class Monitor extends BaseMonitor {
142
142
  const assetName = asset.name
143
143
  const walletAccounts = await aci.getWalletAccounts({ assetName })
144
144
  for (const walletAccount of walletAccounts) {
145
- const { txsToUpdate, utxos } = await this.#scanner.rescanOnNewBlock({
145
+ const { txsToUpdate, utxos, ordinalsUtxos } = await this.#scanner.rescanOnNewBlock({
146
146
  walletAccount,
147
147
  })
148
148
 
149
- if (utxos) {
149
+ if (utxos || ordinalsUtxos) {
150
150
  await aci.updateAccountState({
151
151
  assetName,
152
152
  walletAccount,
153
- newData: {
154
- utxos,
155
- },
153
+ newData: pickBy(
154
+ {
155
+ utxos,
156
+ ordinalsUtxos,
157
+ },
158
+ Boolean
159
+ ),
156
160
  })
157
161
  }
158
162
 
@@ -211,6 +215,7 @@ export class Monitor extends BaseMonitor {
211
215
  txsToAdd,
212
216
  txsToUpdate,
213
217
  utxos,
218
+ ordinalsUtxos,
214
219
  changedUnusedAddressIndexes,
215
220
  } = await this.#scanner.rescanBlockchainInsight({
216
221
  walletAccount,
@@ -218,7 +223,18 @@ export class Monitor extends BaseMonitor {
218
223
  })
219
224
  const accountState = await aci.getAccountState({ assetName, walletAccount })
220
225
 
221
- if (utxos) await aci.updateAccountState({ assetName, walletAccount, newData: { utxos } })
226
+ if (utxos || ordinalsUtxos)
227
+ await aci.updateAccountState({
228
+ assetName,
229
+ walletAccount,
230
+ newData: pickBy(
231
+ {
232
+ utxos,
233
+ ordinalsUtxos,
234
+ },
235
+ Boolean
236
+ ),
237
+ })
222
238
 
223
239
  if (!isEmpty(changedUnusedAddressIndexes)) {
224
240
  // Only for mobile atm, browser and hydra calculates from the latest txLogs
@@ -1,6 +1,6 @@
1
1
  import assert from 'minimalistic-assert'
2
2
  // Using this notation so it can be mocked by jest
3
- import shuffle from 'lodash/shuffle'
3
+ import doShuffle from 'lodash/shuffle'
4
4
 
5
5
  import { UtxoCollection, Address } from '@exodus/models'
6
6
  import { retry } from '@exodus/simple-retry'
@@ -14,7 +14,7 @@ import {
14
14
  createOutput as dogecoinCreateOutput,
15
15
  } from './dogecoin'
16
16
  import { findUnconfirmedSentRbfTxs } from '../tx-utils'
17
- import { getUsableUtxos, getUtxos } from '../utxos-utils'
17
+ import { getOrdinalsUtxos, getUsableUtxos, getUtxos } from '../utxos-utils'
18
18
 
19
19
  import * as defaultBitcoinjsLib from '@exodus/bitcoinjs-lib'
20
20
 
@@ -130,13 +130,25 @@ export const createAndBroadcastTXFactory = ({
130
130
  isBip70,
131
131
  bumpTxId,
132
132
  isRbfAllowed = true,
133
+ nft,
133
134
  } = options
134
135
 
136
+ const inscriptionId = nft?.tokenId
137
+
138
+ const shuffle = (list) => {
139
+ return inscriptionId ? list : doShuffle(list) // don't shuffle when sending ordinal!!!!
140
+ }
141
+
135
142
  assert(assetClientInterface, `assetClientInterface must be supplied in sendTx for ${asset.name}`)
136
143
  assert(
137
144
  address || bumpTxId,
138
145
  'should not be called without either a receiving address or to bump a tx'
139
146
  )
147
+ if (inscriptionId) {
148
+ assert(!bumpTxId, 'only inscriptionId or bumpTxId must be provided')
149
+ assert(!amount, 'only inscriptionId or amount must be provided')
150
+ assert(address, 'address must be provided when sending an ordinal')
151
+ }
140
152
 
141
153
  const assetName = asset.name
142
154
 
@@ -152,6 +164,25 @@ export const createAndBroadcastTXFactory = ({
152
164
 
153
165
  const txSet = await assetClientInterface.getTxLog({ assetName, walletAccount })
154
166
  const accountState = await assetClientInterface.getAccountState({ assetName, walletAccount })
167
+
168
+ const currentOrdinalsUtxos = getOrdinalsUtxos({ accountState, asset })
169
+ const transferOrdinalsUtxos = inscriptionId
170
+ ? currentOrdinalsUtxos.filter((utxo) => utxo.inscriptionId === inscriptionId) /// this could be bulk transfer if multiple inscription ids are provided
171
+ : undefined
172
+
173
+ if (inscriptionId) {
174
+ assert(
175
+ transferOrdinalsUtxos?.size === 1,
176
+ `There should only be one utxo with inscription id ${inscriptionId}. Found: ${transferOrdinalsUtxos?.size ||
177
+ 0}`
178
+ )
179
+
180
+ assert(
181
+ transferOrdinalsUtxos.toArray().every((ordinalUtxo) => ordinalUtxo.confirmations > 0),
182
+ `OrdinalUtxo for inscription id ${inscriptionId} has not confirmed yet`
183
+ )
184
+ }
185
+
155
186
  const insightClient = asset.baseAsset.insightClient
156
187
  const currency = asset.currency
157
188
  const feeData = await assetClientInterface.getFeeConfig({ assetName })
@@ -190,9 +221,10 @@ export const createAndBroadcastTXFactory = ({
190
221
  }
191
222
  }
192
223
 
193
- const sendAmount = bumpTxId ? asset.currency.ZERO : amount.toDefault()
224
+ const sendAmount = bumpTxId || transferOrdinalsUtxos ? asset.currency.ZERO : amount.toDefault()
194
225
  let receiveAddress = bumpTxId ? (replaceableTxs.length > 0 ? null : 'P2WPKH') : address
195
226
  const feeRate = feeData.feePerKB
227
+ const resolvedIsSendAll = (!rbfEnabled && feePerKB) || transferOrdinalsUtxos ? false : isSendAll
196
228
 
197
229
  let { selectedUtxos, fee, replaceTx } = selectUtxos({
198
230
  asset,
@@ -201,7 +233,7 @@ export const createAndBroadcastTXFactory = ({
201
233
  amount: sendAmount,
202
234
  feeRate: customFee || feeRate,
203
235
  receiveAddress,
204
- isSendAll: !rbfEnabled && feePerKB ? false : isSendAll,
236
+ isSendAll: resolvedIsSendAll,
205
237
  getFeeEstimator: (asset, { feePerKB }) => getFeeEstimator(asset, feePerKB),
206
238
  mustSpendUtxos: utxosToBump,
207
239
  allowUnconfirmedRbfEnabledUtxos,
@@ -217,7 +249,9 @@ export const createAndBroadcastTXFactory = ({
217
249
  if (bumpTxId && (!selectedUtxos || (replaceTx && replaceTx.txId !== bumpTxId))) {
218
250
  throw new Error(`Unable to bump ${bumpTxId}`)
219
251
  }
220
-
252
+ if (transferOrdinalsUtxos) {
253
+ selectedUtxos = transferOrdinalsUtxos.union(selectedUtxos)
254
+ }
221
255
  if (replaceTx) {
222
256
  replaceTx = replaceTx.clone()
223
257
  replaceTx = replaceTx.update({ data: { ...replaceTx.data } })
@@ -241,14 +275,25 @@ export const createAndBroadcastTXFactory = ({
241
275
  outputs = []
242
276
  }
243
277
  if (address) {
244
- outputs.push(createOutput(assetName, address, sendAmount))
278
+ if (transferOrdinalsUtxos) {
279
+ outputs.push(
280
+ ...transferOrdinalsUtxos
281
+ .toArray()
282
+ .map((ordinalUtxo) => createOutput(assetName, address, ordinalUtxo.value))
283
+ )
284
+ } else {
285
+ outputs.push(createOutput(assetName, address, sendAmount))
286
+ }
245
287
  }
246
288
 
247
289
  const totalAmount = replaceTx
248
290
  ? replaceTx.data.sent.reduce((total, { amount }) => total.add(amount), sendAmount)
249
291
  : sendAmount
250
292
 
251
- const change = selectedUtxos.value.sub(totalAmount).sub(fee)
293
+ const change = selectedUtxos.value
294
+ .sub(totalAmount)
295
+ .sub(transferOrdinalsUtxos?.value || currency.ZERO)
296
+ .sub(fee)
252
297
  const dust = getDustValue(asset)
253
298
  let ourAddress = replaceTx?.data?.changeAddress || changeAddress
254
299
  if (asset.address.toLegacyAddress) {
@@ -362,11 +407,17 @@ export const createAndBroadcastTXFactory = ({
362
407
  remainingUtxos = remainingUtxos.difference(remainingUtxos.getTxIdUtxos(replaceTx.txId))
363
408
  }
364
409
 
410
+ const remainingOrdinalsUtxos = transferOrdinalsUtxos
411
+ ? currentOrdinalsUtxos.difference(transferOrdinalsUtxos)
412
+ : undefined
365
413
  await assetClientInterface.updateAccountState({
366
414
  assetName,
367
415
  walletAccount,
368
416
  accountState,
369
- newData: { utxos: remainingUtxos },
417
+ newData: {
418
+ utxos: remainingUtxos,
419
+ ordinalsUtxos: remainingOrdinalsUtxos,
420
+ },
370
421
  })
371
422
 
372
423
  // There are two cases of bumping, replacing or chaining a self-send.
@@ -406,6 +457,7 @@ export const createAndBroadcastTXFactory = ({
406
457
  blocksSeen: 0,
407
458
  inputs: selectedUtxos.toJSON(),
408
459
  replacedTxId: replaceTx ? replaceTx.txId : undefined,
460
+ nftId: inscriptionId ? `${assetName}:${inscriptionId}` : undefined, // it allows BE to load the nft info while the nft is in transit
409
461
  },
410
462
  },
411
463
  ],
@@ -442,6 +494,7 @@ function defaultCreateInputs(utxos, rbfEnabled) {
442
494
  value: parseInt(utxo.value.toBaseString(), 10),
443
495
  script: utxo.script,
444
496
  sequence: getTxSequence(rbfEnabled),
497
+ inscriptionId: utxo.inscriptionId,
445
498
  }))
446
499
  }
447
500
 
@@ -2,6 +2,8 @@
2
2
  import { UtxoCollection } from '@exodus/models'
3
3
  import { findLargeUnconfirmedTxs } from './tx-utils'
4
4
  import assert from 'minimalistic-assert'
5
+ import { isOrdinalAddress } from './address-utils'
6
+ // import { isOrdinalAddress } from './address-utils'
5
7
 
6
8
  export function getUtxos({ accountState, asset }) {
7
9
  return (
@@ -12,6 +14,38 @@ export function getUtxos({ accountState, asset }) {
12
14
  )
13
15
  }
14
16
 
17
+ export function getOrdinalsUtxos({ accountState, asset }) {
18
+ return (
19
+ accountState?.ordinalsUtxos ||
20
+ UtxoCollection.createEmpty({
21
+ currency: asset.currency,
22
+ })
23
+ )
24
+ }
25
+
26
+ const ordinalValueUnconfirmedThreshold = 10000
27
+
28
+ function isOrdinalUtxo({ utxo, ordinalChainIndex }) {
29
+ if (utxo.inscriptionId) {
30
+ return true
31
+ }
32
+ const maybeAnOrdinalAddress =
33
+ ordinalChainIndex && isOrdinalAddress(utxo.address, ordinalChainIndex) // if wallet receives utxos to the special address, consider it as ordinal
34
+ if (utxo.confirmations) {
35
+ return maybeAnOrdinalAddress
36
+ }
37
+
38
+ return utxo.value.toBaseNumber() <= ordinalValueUnconfirmedThreshold || maybeAnOrdinalAddress // while unconfirmed, put < 10000- sats in the ordinal utxos box just in case
39
+ }
40
+
41
+ export function partitionUtxos({ allUtxos, ordinalChainIndex }) {
42
+ assert(allUtxos, 'allUtxos is required')
43
+ return {
44
+ utxos: allUtxos.filter((utxo) => !isOrdinalUtxo({ utxo, ordinalChainIndex })),
45
+ ordinalsUtxos: allUtxos.filter((utxo) => isOrdinalUtxo({ utxo, ordinalChainIndex })),
46
+ }
47
+ }
48
+
15
49
  export function getConfirmedUtxos({ utxos }) {
16
50
  assert(utxos, 'utxos is required')
17
51
  return utxos.filter(({ confirmations }) => confirmations > 0)