@exodus/bitcoin-api 4.8.3 → 4.9.0

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,16 @@
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.9.0](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.8.3...@exodus/bitcoin-api@4.9.0) (2025-12-23)
7
+
8
+
9
+ ### Features
10
+
11
+
12
+ * feat: Expose utxo sendTx workflow to accept provided unsigned tx (#6890)
13
+
14
+
15
+
6
16
  ## [4.8.3](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.8.2...@exodus/bitcoin-api@4.8.3) (2025-12-16)
7
17
 
8
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/bitcoin-api",
3
- "version": "4.8.3",
3
+ "version": "4.9.0",
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": "84be327e5d39ecfd0e36abc038f33f7f3b9c33b8"
63
+ "gitHead": "39b743562bc1171bf3d145b47382db9023c660dd"
64
64
  }
package/src/index.js CHANGED
@@ -16,6 +16,7 @@ export * from './unconfirmed-ancestor-data.js'
16
16
  export * from './parse-unsigned-tx.js'
17
17
  export { getCreateBatchTransaction } from './tx-send/batch-tx.js'
18
18
  export { createPsbtToUnsignedTx } from './psbt-utils.js'
19
+ export { createTxFactory } from './tx-create/create-tx.js'
19
20
  export * from './insight-api-client/util.js'
20
21
  export * from './move-funds.js'
21
22
  export { createEncodeMultisigContract } from './multisig-address.js'
@@ -318,7 +318,7 @@ function buildAddressPathsMap(selectedUtxos, changeOutputData) {
318
318
  function buildOutputAddressPurposesMap(outputs) {
319
319
  const map = Object.create(null)
320
320
  for (const output of outputs) {
321
- if (output.address?.meta?.purpose) {
321
+ if (output.address?.meta?.purpose && output.address?.meta?.path) {
322
322
  map[output.address.address] = output.address.meta.purpose
323
323
  }
324
324
  }
@@ -1,3 +1,4 @@
1
+ import { Psbt as DefaultPsbt, Transaction as DefaultTransaction } from '@exodus/bitcoinjs'
1
2
  import { Address, UtxoCollection } from '@exodus/models'
2
3
  import lodash from 'lodash'
3
4
  import assert from 'minimalistic-assert'
@@ -471,7 +472,8 @@ const transferHandler = {
471
472
  changeAddress: context.changeAddress,
472
473
  })
473
474
 
474
- // Create a map of wallet's own output addresses and their purposes.
475
+ // Track our own outputs and their purposes. Today this is only the change
476
+ // output; in the future we may add self-send primaries when applicable.
475
477
  const outputAddressPurposesMap = Object.create(null)
476
478
 
477
479
  // Add the keypath of change address to support Trezor detect the change output.
@@ -517,13 +519,14 @@ export const createTxFactory =
517
519
  assetClientInterface,
518
520
  changeAddressType,
519
521
  allowedPurposes,
520
- Psbt,
521
- Transaction,
522
+ Psbt = DefaultPsbt,
523
+ Transaction = DefaultTransaction,
522
524
  }) =>
523
525
  async ({
524
526
  asset,
525
527
  walletAccount,
526
528
  type,
529
+ address, // same as toAddress.
527
530
  toAddress,
528
531
  amount,
529
532
  multipleAddressesEnabled,
@@ -532,7 +535,7 @@ export const createTxFactory =
532
535
  isSendAll,
533
536
  bumpTxId,
534
537
  isExchange,
535
- isRbfAllowed,
538
+ isRbfAllowed = true,
536
539
  taprootInputWitnessSize,
537
540
  }) => {
538
541
  const assetName = asset.name
@@ -547,7 +550,7 @@ export const createTxFactory =
547
550
  return txHandler.buildTransaction({
548
551
  asset,
549
552
  walletAccount,
550
- toAddress,
553
+ toAddress: toAddress ?? address,
551
554
  amount,
552
555
  multipleAddressesEnabled,
553
556
  feePerKB,
@@ -3,7 +3,6 @@ import { Psbt as DefaultPsbt, Transaction as DefaultTransaction } from '@exodus/
3
3
  import assert from 'minimalistic-assert'
4
4
 
5
5
  import { extractTransactionContext } from '../psbt-parser.js'
6
- import { createTxFactory } from '../tx-create/create-tx.js'
7
6
  import { broadcastTransaction } from './broadcast-tx.js'
8
7
  import { updateAccountState, updateTransactionLog } from './update-state.js'
9
8
 
@@ -99,46 +98,6 @@ export async function signTransaction({
99
98
  return { rawTx, txId, tx }
100
99
  }
101
100
 
102
- function getTransferUnsignedTx(txContext) {
103
- const {
104
- inputs,
105
- outputs,
106
- psbtBuffer,
107
- useCashAddress,
108
- addressPathsMap,
109
- outputAddressPurposesMap,
110
- blockHeight,
111
- rawTxs,
112
- txType,
113
- rbfEnabled,
114
- bumpTxId,
115
- changeOutputIndex,
116
- sendOutputIndexes,
117
- } = txContext
118
-
119
- return {
120
- unsignedTx: {
121
- txData: {
122
- inputs,
123
- outputs,
124
- psbtBuffer,
125
- },
126
- txMeta: {
127
- useCashAddress,
128
- addressPathsMap,
129
- outputAddressPurposesMap,
130
- blockHeight,
131
- rawTxs,
132
- txType,
133
- rbfEnabled,
134
- bumpTxId,
135
- changeOutputIndex,
136
- sendOutputIndexes,
137
- },
138
- },
139
- }
140
- }
141
-
142
101
  // Get additional transaction metadata that are not part of the unsignedTx.txMeta
143
102
  function getExtendedTxMeta(txContext) {
144
103
  const {
@@ -170,64 +129,40 @@ function getExtendedTxMeta(txContext) {
170
129
 
171
130
  // not ported from Exodus; but this demos signing / broadcasting
172
131
  // NOTE: this will be ripped out in the coming weeks
173
- export const createAndBroadcastTXFactory =
174
- ({
175
- getFeeEstimator,
176
- getSizeAndChangeScript = getSizeAndChangeScriptFactory(), // for decred customizations
177
- allowUnconfirmedRbfEnabledUtxos,
178
- utxosDescendingOrder,
179
- assetClientInterface,
180
- changeAddressType,
181
- allowedPurposes,
182
- Psbt = DefaultPsbt,
183
- Transaction = DefaultTransaction,
184
- }) =>
185
- async ({
186
- asset,
187
- walletAccount,
188
- address,
189
- amount,
190
- multipleAddressesEnabled,
191
- feePerKB,
192
- customFee,
193
- isSendAll,
194
- bumpTxId: bumpTxIdProvided,
195
- isExchange,
196
- isRbfAllowed = true,
197
- taprootInputWitnessSize,
198
- }) => {
132
+ export const sendTxFactory = ({
133
+ createTx,
134
+ getSizeAndChangeScript = getSizeAndChangeScriptFactory(), // for decred customizations
135
+ assetClientInterface,
136
+ allowedPurposes,
137
+ Psbt = DefaultPsbt,
138
+ Transaction = DefaultTransaction,
139
+ }) => {
140
+ assert(assetClientInterface, 'assetClientInterface is required')
141
+ assert(createTx, 'createTx is required')
142
+
143
+ return async ({ asset, walletAccount, unsignedTx: providedUnsignedTx, ...legacyParams }) => {
144
+ const { address, ...createTxParams } = legacyParams
199
145
  const assetName = asset.name
200
146
  const accountState = await assetClientInterface.getAccountState({ assetName, walletAccount })
201
147
 
202
- const createTx = createTxFactory({
203
- getFeeEstimator,
204
- allowUnconfirmedRbfEnabledUtxos,
205
- utxosDescendingOrder,
206
- assetClientInterface,
207
- changeAddressType,
208
- allowedPurposes,
209
- Psbt,
210
- Transaction,
211
- })
148
+ const resolveUnsignedTx = async () => {
149
+ if (providedUnsignedTx) {
150
+ return { unsignedTx: providedUnsignedTx }
151
+ }
212
152
 
213
- const { unsignedTx: unsignedTxByCreateTx } = await createTx({
214
- asset,
215
- walletAccount,
216
- type: 'transfer',
217
- toAddress: address,
218
- amount,
219
- isRbfAllowed,
220
- multipleAddressesEnabled,
221
- feePerKB,
222
- customFee,
223
- isSendAll,
224
- bumpTxId: bumpTxIdProvided,
225
- isExchange,
226
- taprootInputWitnessSize,
227
- })
153
+ return createTx({
154
+ asset,
155
+ walletAccount,
156
+ type: 'transfer',
157
+ toAddress: address,
158
+ ...createTxParams,
159
+ })
160
+ }
161
+
162
+ const { unsignedTx } = await resolveUnsignedTx()
228
163
 
229
164
  const txContext = await extractTransactionContext({
230
- unsignedTx: unsignedTxByCreateTx,
165
+ unsignedTx,
231
166
  asset,
232
167
  assetClientInterface,
233
168
  walletAccount,
@@ -236,8 +171,6 @@ export const createAndBroadcastTXFactory =
236
171
  Transaction,
237
172
  })
238
173
 
239
- const { unsignedTx } = getTransferUnsignedTx(txContext)
240
-
241
174
  const {
242
175
  fee,
243
176
  sendAmount,
@@ -330,6 +263,7 @@ export const createAndBroadcastTXFactory =
330
263
  replacedTxId: replaceTx?.txId,
331
264
  }
332
265
  }
266
+ }
333
267
 
334
268
  // back compatibiliy
335
269
  export { getSendDustValue as getDustValue } from '../dust.js'