@exodus/bitcoin-api 4.1.2 → 4.1.3

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/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [4.1.3](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.1.1...@exodus/bitcoin-api@4.1.3) (2025-10-14)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+
12
+ * fix: remove isBip70 from bitcoin libs (#6660)
13
+
14
+ * fix: remove unused bitcoin.api.prepareSendTx (#6662)
15
+
16
+
17
+
6
18
  ## [4.1.2](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.1.1...@exodus/bitcoin-api@4.1.2) (2025-10-09)
7
19
 
8
20
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/bitcoin-api",
3
- "version": "4.1.2",
3
+ "version": "4.1.3",
4
4
  "description": "Bitcoin transaction and fee monitors, RPC with the blockchain node, other networking code.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -60,5 +60,5 @@
60
60
  "type": "git",
61
61
  "url": "git+https://github.com/ExodusMovement/assets.git"
62
62
  },
63
- "gitHead": "6237b0d98791ed9e6290260947068d2600bf8270"
63
+ "gitHead": "48e0d08e2fe044fb98d2fb16fc790f642a3c1eb2"
64
64
  }
@@ -2,13 +2,7 @@ import assert from 'minimalistic-assert'
2
2
 
3
3
  import { findUnconfirmedSentRbfTxs } from '../tx-utils.js'
4
4
  import { getUnconfirmedTxAncestorMap } from '../unconfirmed-ancestor-data.js'
5
- import {
6
- getInscriptionIds,
7
- getOrdinalsUtxos,
8
- getTransferOrdinalsUtxos,
9
- getUsableUtxos,
10
- getUtxos,
11
- } from '../utxos-utils.js'
5
+ import { getUsableUtxos, getUtxos } from '../utxos-utils.js'
12
6
  import { canBumpTx } from './can-bump-tx.js'
13
7
  import { getUtxosData } from './utxo-selector.js'
14
8
 
@@ -40,17 +34,9 @@ export class GetFeeResolver {
40
34
  amount,
41
35
  customFee,
42
36
  isSendAll,
43
- nft, // sending one nft
44
37
  receiveAddress,
45
38
  taprootInputWitnessSize,
46
39
  }) => {
47
- if (nft) {
48
- assert(!amount, 'amount must not be provided when nft is provided!!!')
49
- assert(!isSendAll, 'isSendAll must not be provided when nft is provided!!!')
50
- }
51
-
52
- const inscriptionIds = getInscriptionIds({ nft })
53
-
54
40
  const { fee, unspendableFee, extraFeeData } = this.#getUtxosData({
55
41
  asset,
56
42
  accountState,
@@ -60,7 +46,6 @@ export class GetFeeResolver {
60
46
  amount,
61
47
  customFee,
62
48
  isSendAll,
63
- inscriptionIds,
64
49
  taprootInputWitnessSize,
65
50
  })
66
51
  return { fee, unspendableFee, extraFeeData }
@@ -75,7 +60,6 @@ export class GetFeeResolver {
75
60
  amount,
76
61
  customFee,
77
62
  isSendAll,
78
- inscriptionIds,
79
63
  taprootInputWitnessSize,
80
64
  }) => {
81
65
  assert(asset, 'asset must be provided')
@@ -87,12 +71,6 @@ export class GetFeeResolver {
87
71
  const utxos = getUtxos({ accountState, asset })
88
72
  const unconfirmedTxAncestor = getUnconfirmedTxAncestorMap({ accountState })
89
73
 
90
- const ordinalsUtxos = getOrdinalsUtxos({ accountState, asset })
91
-
92
- const transferOrdinalsUtxos = inscriptionIds
93
- ? getTransferOrdinalsUtxos({ inscriptionIds, ordinalsUtxos })
94
- : undefined
95
-
96
74
  const usableUtxos = getUsableUtxos({
97
75
  asset,
98
76
  utxos,
@@ -110,8 +88,6 @@ export class GetFeeResolver {
110
88
  amount,
111
89
  feeRate: feePerKB,
112
90
  receiveAddress,
113
- transferOrdinalsUtxos,
114
- inscriptionIds,
115
91
  isSendAll,
116
92
  getFeeEstimator: this.#getFeeEstimator,
117
93
  allowUnconfirmedRbfEnabledUtxos: this.#allowUnconfirmedRbfEnabledUtxos,
@@ -11,11 +11,7 @@ const { sortBy } = lodash
11
11
 
12
12
  const MIN_RELAY_FEE = 1000
13
13
 
14
- const getBestReceiveAddresses = ({ asset, receiveAddress, inscriptionIds }) => {
15
- if (inscriptionIds) {
16
- return receiveAddress || 'P2TR'
17
- }
18
-
14
+ const getBestReceiveAddresses = ({ asset, receiveAddress }) => {
19
15
  if (receiveAddress === null) {
20
16
  return null
21
17
  }
@@ -38,20 +34,15 @@ export const selectUtxos = ({
38
34
  mustSpendUtxos,
39
35
  allowUnconfirmedRbfEnabledUtxos,
40
36
  unconfirmedTxAncestor,
41
- inscriptionIds, // for each inscription transfer, we need to calculate one more input and one more output
42
- transferOrdinalsUtxos, // to calculate the size of the input
43
37
  taprootInputWitnessSize,
44
38
  changeAddressType = 'P2PKH',
45
39
  }) => {
46
40
  const resolvedReceiveAddresses = getBestReceiveAddresses({
47
41
  asset,
48
42
  receiveAddress,
49
- inscriptionIds,
50
43
  })
51
44
 
52
- if (inscriptionIds) {
53
- receiveAddresses.push(...inscriptionIds.map(() => resolvedReceiveAddresses))
54
- } else if (receiveAddresses.length === 0) {
45
+ if (receiveAddresses.length === 0) {
55
46
  receiveAddresses.push(resolvedReceiveAddresses)
56
47
  }
57
48
 
@@ -66,7 +57,6 @@ export const selectUtxos = ({
66
57
  // We can only replace for a sendAll if only 1 replaceable tx and no unconfirmed utxos
67
58
  const confirmedUtxosArray = getConfirmedUtxos({ asset, utxos: usableUtxos }).toArray()
68
59
  const canReplace =
69
- !inscriptionIds &&
70
60
  !mustSpendUtxos &&
71
61
  !disableReplacement &&
72
62
  replaceableTxs &&
@@ -86,7 +76,6 @@ export const selectUtxos = ({
86
76
  }
87
77
 
88
78
  const replaceFeeEstimator = getFeeEstimator(asset, { feePerKB, unconfirmedTxAncestor })
89
- // how to avoid replace tx inputs when inputs are ordinals? !!!!
90
79
  const inputs = UtxoCollection.fromJSON(tx.data.inputs, { currency })
91
80
  const outputs = isSendAll
92
81
  ? tx.data.sent.map(({ address }) => address)
@@ -211,15 +200,10 @@ export const selectUtxos = ({
211
200
  selectedUtxosValue = selectedUtxosValue.add(newUtxo.value)
212
201
  }
213
202
 
214
- let selectedUtxos = (transferOrdinalsUtxos || UtxoCollection.createEmpty({ currency })).union(
215
- UtxoCollection.fromArray(selectedUtxosArray, { currency })
216
- ) // extremelly important, orden must be kept!!! ordinals utxos go first!!!
203
+ let selectedUtxos = UtxoCollection.fromArray(selectedUtxosArray, { currency })
217
204
 
218
205
  // start figuring out fees
219
- const outputs =
220
- amount.isZero && !inscriptionIds
221
- ? [changeAddressType]
222
- : [...receiveAddresses, changeAddressType]
206
+ const outputs = amount.isZero ? [changeAddressType] : [...receiveAddresses, changeAddressType]
223
207
 
224
208
  let fee = feeEstimator({ inputs: selectedUtxos, outputs, taprootInputWitnessSize })
225
209
 
@@ -259,8 +243,6 @@ export const getUtxosData = ({
259
243
  disableReplacement,
260
244
  mustSpendUtxos,
261
245
  allowUnconfirmedRbfEnabledUtxos,
262
- inscriptionIds,
263
- transferOrdinalsUtxos,
264
246
  unconfirmedTxAncestor,
265
247
  utxosDescendingOrder,
266
248
  taprootInputWitnessSize,
@@ -283,8 +265,6 @@ export const getUtxosData = ({
283
265
  mustSpendUtxos,
284
266
  allowUnconfirmedRbfEnabledUtxos,
285
267
  unconfirmedTxAncestor,
286
- inscriptionIds,
287
- transferOrdinalsUtxos,
288
268
  utxosDescendingOrder,
289
269
  taprootInputWitnessSize,
290
270
  changeAddressType,
@@ -7,13 +7,7 @@ import { parseCurrency, serializeCurrency } from '../fee/fee-utils.js'
7
7
  import { selectUtxos } from '../fee/utxo-selector.js'
8
8
  import { findUnconfirmedSentRbfTxs } from '../tx-utils.js'
9
9
  import { getUnconfirmedTxAncestorMap } from '../unconfirmed-ancestor-data.js'
10
- import {
11
- getInscriptionIds,
12
- getOrdinalsUtxos,
13
- getTransferOrdinalsUtxos,
14
- getUsableUtxos,
15
- getUtxos,
16
- } from '../utxos-utils.js'
10
+ import { getUsableUtxos, getUtxos } from '../utxos-utils.js'
17
11
  import { createInputs, createOutput, getBlockHeight, getNonWitnessTxs } from './tx-create-utils.js'
18
12
 
19
13
  async function createUnsignedTx({
@@ -64,13 +58,11 @@ const transferHandler = {
64
58
  customFee,
65
59
  isSendAll,
66
60
  bumpTxId,
67
- nft,
68
61
  isExchange,
69
62
  isRbfAllowed,
70
63
  taprootInputWitnessSize,
71
64
  accountState,
72
65
  feeData,
73
- ordinalsEnabled,
74
66
  getFeeEstimator,
75
67
  allowUnconfirmedRbfEnabledUtxos,
76
68
  utxosDescendingOrder,
@@ -84,18 +76,11 @@ const transferHandler = {
84
76
  const blockHeight = providedBlockHeight || (await getBlockHeight({ assetName, insightClient }))
85
77
 
86
78
  const rbfEnabled =
87
- providedRbfEnabled || (updatedFeeData.rbfEnabled && !isExchange && isRbfAllowed && !nft)
88
-
89
- const inscriptionIds = getInscriptionIds({ nft })
90
-
91
- assert(
92
- ordinalsEnabled || !inscriptionIds,
93
- 'inscriptions cannot be sent when ordinalsEnabled=false '
94
- )
79
+ providedRbfEnabled || (updatedFeeData.rbfEnabled && !isExchange && isRbfAllowed)
95
80
 
96
81
  const shuffle = (list) => {
97
82
  // Using full lodash.shuffle notation so it can be mocked with spyOn in tests
98
- return inscriptionIds ? list : lodash.shuffle(list) // don't shuffle when sending ordinal!!!!
83
+ return lodash.shuffle(list)
99
84
  }
100
85
 
101
86
  assert(
@@ -107,11 +92,6 @@ const transferHandler = {
107
92
  'should not be called without either a receiving toAddress or to bump a tx'
108
93
  )
109
94
 
110
- if (inscriptionIds) {
111
- assert(!bumpTxId, 'only inscriptionIds or bumpTxId must be provided')
112
- assert(toAddress, 'toAddress must be provided when sending ordinals')
113
- }
114
-
115
95
  const useCashAddress = asset.address.isCashAddress?.(toAddress)
116
96
 
117
97
  const changeAddress = multipleAddressesEnabled
@@ -120,11 +100,6 @@ const transferHandler = {
120
100
 
121
101
  const txSet = await assetClientInterface.getTxLog({ assetName, walletAccount })
122
102
 
123
- const currentOrdinalsUtxos = getOrdinalsUtxos({ accountState, asset })
124
- const transferOrdinalsUtxos = inscriptionIds
125
- ? getTransferOrdinalsUtxos({ inscriptionIds, ordinalsUtxos: currentOrdinalsUtxos })
126
- : undefined
127
-
128
103
  const unconfirmedTxAncestor = getUnconfirmedTxAncestorMap({ accountState })
129
104
  const usableUtxos = getUsableUtxos({
130
105
  asset,
@@ -160,14 +135,14 @@ const transferHandler = {
160
135
  }
161
136
  }
162
137
 
163
- const sendAmount = bumpTxId || transferOrdinalsUtxos ? asset.currency.ZERO : amount
138
+ const sendAmount = bumpTxId ? asset.currency.ZERO : amount
164
139
  const receiveAddress = bumpTxId
165
140
  ? replaceableTxs.length > 0
166
141
  ? null
167
142
  : changeAddressType
168
143
  : processedAddress
169
144
  const feeRate = updatedFeeData.feePerKB
170
- const resolvedIsSendAll = (!rbfEnabled && feePerKB) || transferOrdinalsUtxos ? false : isSendAll
145
+ const resolvedIsSendAll = !rbfEnabled && feePerKB ? false : isSendAll
171
146
 
172
147
  let { selectedUtxos, fee, replaceTx } = selectUtxos({
173
148
  asset,
@@ -182,8 +157,6 @@ const transferHandler = {
182
157
  mustSpendUtxos: utxosToBump,
183
158
  allowUnconfirmedRbfEnabledUtxos,
184
159
  unconfirmedTxAncestor,
185
- inscriptionIds,
186
- transferOrdinalsUtxos,
187
160
  utxosDescendingOrder,
188
161
  taprootInputWitnessSize,
189
162
  changeAddressType,
@@ -207,7 +180,6 @@ const transferHandler = {
207
180
  return { ...to, amount: serializeCurrency(to.amount, asset.currency) }
208
181
  })
209
182
  selectedUtxos = selectedUtxos.union(
210
- // how to avoid replace tx inputs when inputs are ordinals? !!!!
211
183
  UtxoCollection.fromJSON(replaceTx.data.inputs, { currency: asset.currency })
212
184
  )
213
185
  }
@@ -225,16 +197,8 @@ const transferHandler = {
225
197
  // Send output
226
198
  let sendOutput
227
199
  if (processedAddress) {
228
- if (transferOrdinalsUtxos) {
229
- outputs.push(
230
- ...transferOrdinalsUtxos
231
- .toArray()
232
- .map((ordinalUtxo) => createOutput(assetName, processedAddress, ordinalUtxo.value))
233
- )
234
- } else {
235
- sendOutput = createOutput(assetName, processedAddress, sendAmount)
236
- outputs.push(sendOutput)
237
- }
200
+ sendOutput = createOutput(assetName, processedAddress, sendAmount)
201
+ outputs.push(sendOutput)
238
202
  }
239
203
 
240
204
  const totalAmount = replaceTx
@@ -244,10 +208,7 @@ const transferHandler = {
244
208
  )
245
209
  : sendAmount
246
210
 
247
- const change = selectedUtxos.value
248
- .sub(totalAmount)
249
- .sub(transferOrdinalsUtxos?.value || asset.currency.ZERO)
250
- .sub(fee)
211
+ const change = selectedUtxos.value.sub(totalAmount).sub(fee)
251
212
  const dust = getChangeDustValue(asset)
252
213
  let ourAddress = replaceTx?.data?.changeAddress || changeAddress
253
214
  if (asset.address.toLegacyAddress) {
@@ -285,8 +246,6 @@ const transferHandler = {
285
246
  amount,
286
247
  change,
287
248
  totalAmount,
288
- currentOrdinalsUtxos,
289
- inscriptionIds,
290
249
  address: processedAddress,
291
250
  ourAddress,
292
251
  receiveAddress,
@@ -294,7 +253,6 @@ const transferHandler = {
294
253
  fee,
295
254
  usableUtxos,
296
255
  selectedUtxos,
297
- transferOrdinalsUtxos,
298
256
  replaceTx,
299
257
  sendOutput,
300
258
  changeOutput,
@@ -305,7 +263,6 @@ const transferHandler = {
305
263
 
306
264
  export const createTxFactory =
307
265
  ({
308
- ordinalsEnabled,
309
266
  getFeeEstimator,
310
267
  allowUnconfirmedRbfEnabledUtxos,
311
268
  utxosDescendingOrder,
@@ -325,7 +282,6 @@ export const createTxFactory =
325
282
  customFee,
326
283
  isSendAll,
327
284
  bumpTxId,
328
- nft,
329
285
  isExchange,
330
286
  isRbfAllowed,
331
287
  taprootInputWitnessSize,
@@ -351,13 +307,11 @@ export const createTxFactory =
351
307
  customFee,
352
308
  isSendAll,
353
309
  bumpTxId,
354
- nft,
355
310
  isExchange,
356
311
  isRbfAllowed,
357
312
  taprootInputWitnessSize,
358
313
  accountState,
359
314
  feeData,
360
- ordinalsEnabled,
361
315
  getFeeEstimator,
362
316
  allowUnconfirmedRbfEnabledUtxos,
363
317
  utxosDescendingOrder,
@@ -109,13 +109,11 @@ const getPrepareSendTransaction = async ({
109
109
  changeAddressType,
110
110
  getFeeEstimator,
111
111
  options,
112
- ordinalsEnabled,
113
112
  rbfEnabled,
114
113
  utxosDescendingOrder,
115
114
  walletAccount,
116
115
  }) => {
117
116
  const createTx = createTxFactory({
118
- ordinalsEnabled,
119
117
  getFeeEstimator,
120
118
  allowUnconfirmedRbfEnabledUtxos,
121
119
  utxosDescendingOrder,
@@ -142,21 +140,20 @@ export const createAndBroadcastTXFactory =
142
140
  getFeeEstimator,
143
141
  getSizeAndChangeScript = getSizeAndChangeScriptFactory(), // for decred customizations
144
142
  allowUnconfirmedRbfEnabledUtxos,
145
- ordinalsEnabled = false,
146
143
  utxosDescendingOrder,
147
144
  assetClientInterface,
148
145
  changeAddressType,
149
146
  }) =>
150
147
  async ({ asset, walletAccount, address, amount, options }) => {
151
148
  // Prepare transaction
152
- const { bumpTxId, nft, isExchange, isRbfAllowed = true } = options
149
+ const { bumpTxId, isExchange, isRbfAllowed = true } = options
153
150
 
154
151
  const assetName = asset.name
155
152
  const feeData = await assetClientInterface.getFeeConfig({ assetName })
156
153
  const accountState = await assetClientInterface.getAccountState({ assetName, walletAccount })
157
154
  const insightClient = asset.baseAsset.insightClient
158
155
 
159
- const rbfEnabled = feeData.rbfEnabled && !isExchange && isRbfAllowed && !nft
156
+ const rbfEnabled = feeData.rbfEnabled && !isExchange && isRbfAllowed
160
157
 
161
158
  // blockHeight
162
159
  const blockHeight = await getBlockHeight({ assetName, insightClient })
@@ -171,7 +168,6 @@ export const createAndBroadcastTXFactory =
171
168
  changeAddressType,
172
169
  getFeeEstimator,
173
170
  options,
174
- ordinalsEnabled,
175
171
  rbfEnabled,
176
172
  utxosDescendingOrder,
177
173
  walletAccount,
@@ -179,15 +175,12 @@ export const createAndBroadcastTXFactory =
179
175
  const {
180
176
  change,
181
177
  totalAmount,
182
- currentOrdinalsUtxos,
183
- inscriptionIds,
184
178
  ourAddress,
185
179
  receiveAddress,
186
180
  sendAmount,
187
181
  fee,
188
182
  usableUtxos,
189
183
  selectedUtxos,
190
- transferOrdinalsUtxos,
191
184
  replaceTx,
192
185
  sendOutput,
193
186
  changeOutput,
@@ -265,7 +258,6 @@ export const createAndBroadcastTXFactory =
265
258
 
266
259
  const { script, size } = getSizeAndChangeScript({ assetName, tx, rawTx, changeUtxoIndex, txId })
267
260
 
268
- // for ordinals, used to allow users spending change utxos even when unconfirmed and ordinals are unknown
269
261
  const knownBalanceUtxoIds = accountState.knownBalanceUtxoIds || []
270
262
  let remainingUtxos = usableUtxos.difference(selectedUtxos)
271
263
  if (changeUtxoIndex !== -1) {
@@ -288,16 +280,11 @@ export const createAndBroadcastTXFactory =
288
280
  remainingUtxos = remainingUtxos.difference(remainingUtxos.getTxIdUtxos(replaceTx.txId))
289
281
  }
290
282
 
291
- const remainingOrdinalsUtxos = transferOrdinalsUtxos
292
- ? currentOrdinalsUtxos.difference(transferOrdinalsUtxos)
293
- : currentOrdinalsUtxos
294
-
295
283
  await assetClientInterface.updateAccountState({
296
284
  assetName,
297
285
  walletAccount,
298
286
  newData: {
299
287
  utxos: remainingUtxos,
300
- ordinalsUtxos: remainingOrdinalsUtxos,
301
288
  knownBalanceUtxoIds,
302
289
  },
303
290
  })
@@ -335,10 +322,6 @@ export const createAndBroadcastTXFactory =
335
322
  return asset.currency.ZERO
336
323
  }
337
324
 
338
- if (nft) {
339
- return transferOrdinalsUtxos.value.abs().negate()
340
- }
341
-
342
325
  return totalAmount.abs().negate()
343
326
  }
344
327
 
@@ -365,17 +348,6 @@ export const createAndBroadcastTXFactory =
365
348
  blocksSeen: 0,
366
349
  inputs: selectedUtxos.toJSON(),
367
350
  replacedTxId: replaceTx ? replaceTx.txId : undefined,
368
- nftId: nft ? `${assetName}:${nft.tokenId}` : undefined, // it allows BE to load the nft info while the nft is in transit
369
- inscriptionsIndexed: ordinalsEnabled ? true : undefined,
370
- sentInscriptions: inscriptionIds
371
- ? inscriptionIds.map((inscriptionId) => {
372
- return {
373
- inscriptionId,
374
- offset: 0,
375
- value: 0,
376
- }
377
- })
378
- : undefined,
379
351
  },
380
352
  },
381
353
  ],