@exodus/bitcoin-api 4.9.1 → 4.9.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,26 @@
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.3](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.9.2...@exodus/bitcoin-api@4.9.3) (2026-01-30)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+
12
+ * fix(bitcoin-api): Fix non-BTC nested‑segwit redeem script (#7357)
13
+
14
+
15
+
16
+ ## [4.9.2](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.9.1...@exodus/bitcoin-api@4.9.2) (2026-01-28)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+
22
+ * fix(bitcoin-api): Guard BTC-like send against duplicate broadcast (#7335)
23
+
24
+
25
+
6
26
  ## [4.9.1](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.9.0...@exodus/bitcoin-api@4.9.1) (2026-01-26)
7
27
 
8
28
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/bitcoin-api",
3
- "version": "4.9.1",
3
+ "version": "4.9.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",
@@ -61,5 +61,5 @@
61
61
  "type": "git",
62
62
  "url": "git+https://github.com/ExodusMovement/assets.git"
63
63
  },
64
- "gitHead": "481b327c2b57d6272c47807615be9f967ebb3d13"
64
+ "gitHead": "536e8318e84aa9a812191ff81f3ddd70a518885e"
65
65
  }
package/src/move-funds.js CHANGED
@@ -106,13 +106,13 @@ export const moveFundsFactory = ({
106
106
  assetName,
107
107
  walletAccount,
108
108
  })
109
- const recieveAddressesObjects = await assetClientInterface.getReceiveAddresses({
109
+ const receiveAddressesObjects = await assetClientInterface.getReceiveAddresses({
110
110
  walletAccount,
111
111
  assetName,
112
112
  multiAddressMode: config?.multiAddressMode ?? true,
113
113
  })
114
114
 
115
- const receiveAddresses = recieveAddressesObjects.map(
115
+ const receiveAddresses = receiveAddressesObjects.map(
116
116
  (receiveAddress) =>
117
117
  address.toLegacyAddress?.(receiveAddress.toString()) || receiveAddress.toString()
118
118
  )
@@ -20,9 +20,9 @@ function canParseTx(rawTxBuffer, Transaction) {
20
20
  }
21
21
  }
22
22
 
23
- function getWrappedSegwitRedeemScript({ publicKey, address, context = '' }) {
24
- const p2wpkh = payments.p2wpkh({ pubkey: publicKey })
25
- const p2sh = payments.p2sh({ redeem: p2wpkh })
23
+ function getWrappedSegwitRedeemScript({ publicKey, address, network, context = '' }) {
24
+ const p2wpkh = payments.p2wpkh({ pubkey: publicKey, network })
25
+ const p2sh = payments.p2sh({ redeem: p2wpkh, network })
26
26
 
27
27
  if (address !== p2sh.address) {
28
28
  throw new Error(`Expected P2SH script to be a nested p2wpkh${context ? ' for ' + context : ''}`)
@@ -105,6 +105,7 @@ function createPsbtInput({
105
105
  psbtInput.redeemScript = getWrappedSegwitRedeemScript({
106
106
  publicKey,
107
107
  address: input.address,
108
+ network: asset.coinInfo.toBitcoinJS(),
108
109
  context: `input address ${input.address}`,
109
110
  })
110
111
  }
@@ -170,6 +171,7 @@ function createPsbtOutput({ address, amount, asset, addressPathsMap, purposeXPub
170
171
  psbtOutput.redeemScript = getWrappedSegwitRedeemScript({
171
172
  publicKey,
172
173
  address,
174
+ network: asset.coinInfo.toBitcoinJS(),
173
175
  context: `output address ${address}`,
174
176
  })
175
177
  }
@@ -6,6 +6,15 @@ import { extractTransactionContext } from '../psbt-parser.js'
6
6
  import { broadcastTransaction } from './broadcast-tx.js'
7
7
  import { updateAccountState, updateTransactionLog } from './update-state.js'
8
8
 
9
+ const checkTxExists = async ({ asset, txId }) => {
10
+ try {
11
+ const tx = await asset.insightClient.fetchTx(txId)
12
+ return Boolean(tx)
13
+ } catch {
14
+ return false
15
+ }
16
+ }
17
+
9
18
  const getSize = (tx) => {
10
19
  if (typeof tx.size === 'number') return tx.size
11
20
  if (typeof tx.virtualSize === 'function') {
@@ -199,15 +208,20 @@ export const sendTxFactory = ({
199
208
  try {
200
209
  await broadcastTransaction({ asset, rawTx })
201
210
  } catch (err) {
202
- if (/insight broadcast http error.*missing inputs/i.test(err.message)) {
203
- err.txInfo = JSON.stringify({
204
- amount: sendAmount.toDefaultString({ unit: true }),
205
- fee: ((fee && fee.toDefaultString({ unit: true })) || 0).toString(),
206
- allUtxos: usableUtxos.toJSON(),
207
- })
208
- }
211
+ const txExists = await checkTxExists({ asset, txId })
212
+ if (txExists) {
213
+ console.warn(`tx-send: ${assetName} tx already broadcast`, txId)
214
+ } else {
215
+ if (/insight broadcast http error.*missing inputs/i.test(err.message)) {
216
+ err.txInfo = JSON.stringify({
217
+ amount: sendAmount.toDefaultString({ unit: true }),
218
+ fee: ((fee && fee.toDefaultString({ unit: true })) || 0).toString(),
219
+ allUtxos: usableUtxos.toJSON(),
220
+ })
221
+ }
209
222
 
210
- throw err
223
+ throw err
224
+ }
211
225
  }
212
226
 
213
227
  const changeUtxoIndex = changeOutputIndex ?? -1
@@ -64,8 +64,9 @@ export function createSignWithWallet({
64
64
  // P2WPKH. Exodus doesn't use P2SH addresses so we should only ever be
65
65
  // signing a P2SH input if we are importing a private key
66
66
  // BIP143: As a default policy, only compressed public keys are accepted in P2WPKH and P2WSH
67
- const p2wpkh = payments.p2wpkh({ pubkey: publicKey })
68
- const p2sh = payments.p2sh({ redeem: p2wpkh })
67
+ const network = coinInfo.toBitcoinJS()
68
+ const p2wpkh = payments.p2wpkh({ pubkey: publicKey, network })
69
+ const p2sh = payments.p2sh({ redeem: p2wpkh, network })
69
70
  if (address === p2sh.address) {
70
71
  // Set the redeem script in the psbt in case it's missing.
71
72
  if (!Buffer.isBuffer(input.redeemScript)) {