@exodus/bitcoin-api 2.28.0 → 2.29.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,20 @@
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
+ ## [2.29.0](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@2.28.0...@exodus/bitcoin-api@2.29.0) (2024-11-10)
7
+
8
+
9
+ ### Features
10
+
11
+ * filter immature coinbase utxos in getUsableUtxos ([#4507](https://github.com/ExodusMovement/assets/issues/4507)) ([3774f66](https://github.com/ExodusMovement/assets/commit/3774f66ee733ca5a3f07a7556c349d08c2d217e9))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * fetch and require raw txs for segwit inputs ([#4399](https://github.com/ExodusMovement/assets/issues/4399)) ([f5822cc](https://github.com/ExodusMovement/assets/commit/f5822cc60d6e542f9aea88c051e1a2dbe56da13d))
17
+
18
+
19
+
6
20
  ## [2.28.0](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@2.27.0...@exodus/bitcoin-api@2.28.0) (2024-10-31)
7
21
 
8
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/bitcoin-api",
3
- "version": "2.28.0",
3
+ "version": "2.29.0",
4
4
  "description": "Exodus bitcoin-api",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -56,5 +56,5 @@
56
56
  "type": "git",
57
57
  "url": "git+https://github.com/ExodusMovement/assets.git"
58
58
  },
59
- "gitHead": "65c334463a6fd913d73d2873ac455c5ec675705a"
59
+ "gitHead": "500277d2c77cd37f7d7a93eceec09e5b8c49f253"
60
60
  }
@@ -418,6 +418,7 @@ export class BitcoinMonitorScanner {
418
418
  feePerKB: txItem.fees ? (txItem.fees / txItem.vsize) * 1000 * 1e8 : null,
419
419
  rbfEnabled: txItem.rbf,
420
420
  blocksSeen: 0,
421
+ ...(txItem.vin.length === 0 ? { isCoinbase: true } : undefined),
421
422
  },
422
423
  currencies: { [assetName]: currency },
423
424
  }
@@ -544,6 +545,7 @@ export class BitcoinMonitorScanner {
544
545
  script: vout.scriptPubKey.hex,
545
546
  value: val,
546
547
  rbfEnabled: txItem.rbf,
548
+ ...(txItem.vin.length === 0 ? { isCoinbase: true } : undefined),
547
549
  }
548
550
 
549
551
  if (this.#ordinalsEnabled) {
@@ -71,7 +71,14 @@ export async function getNonWitnessTxs(asset, utxos, insightClient) {
71
71
  utxos
72
72
  .getAddressesForTxId(txId)
73
73
  .toAddressStrings()
74
- .some((a) => asset.address.isP2PKH(a) || asset.address.isP2SH(a))
74
+ .some(
75
+ (a) =>
76
+ asset.address.isP2PKH(a) ||
77
+ asset.address.isP2SH(a) ||
78
+ asset.address.isP2SH2?.(a) ||
79
+ asset.address.isP2WPKH?.(a) ||
80
+ asset.address.isP2WSH?.(a)
81
+ )
75
82
  )
76
83
 
77
84
  if (nonWitnessTxIds.length > 0) {
@@ -85,17 +85,24 @@ function createPsbtFromTxData({
85
85
  const isTaprootAddress = purpose === 86
86
86
 
87
87
  const txIn = { hash: txId, index: vout, sequence }
88
- if (isSegwitAddress || isTaprootAddress) {
89
- if (isTaprootAddress && tapLeafScript) {
90
- txIn.tapLeafScript = tapLeafScript
91
- }
92
88
 
93
- // witness outputs only require the value and the script, not the full transaction
89
+ if (isTaprootAddress && tapLeafScript) {
90
+ txIn.tapLeafScript = tapLeafScript
91
+ }
92
+
93
+ if (isSegwitAddress || isTaprootAddress) {
94
+ // taproot outputs only require the value and the script, not the full transaction
94
95
  txIn.witnessUtxo = { value, script: Buffer.from(script, 'hex') }
95
- } else {
96
- const rawTx = (rawTxs || []).find((t) => t.txId === txId)
97
- // non-witness outptus require the full transaction
98
- assert(!!rawTx, 'Non-witness outputs require the full previous transaction.')
96
+ }
97
+
98
+ const rawTx = (rawTxs || []).find((t) => t.txId === txId)
99
+
100
+ // Non-taproot outputs require the full transaction
101
+ assert(
102
+ isTaprootAddress || !!rawTx?.rawData,
103
+ `Non-taproot outputs require the full previous transaction.`
104
+ )
105
+ if (!isTaprootAddress) {
99
106
  const rawTxBuffer = Buffer.from(rawTx.rawData, 'hex')
100
107
  if (canParseTx(Transaction, rawTxBuffer)) {
101
108
  txIn.nonWitnessUtxo = rawTxBuffer
@@ -233,11 +233,25 @@ function filterDustUtxos({ utxos, feeData }) {
233
233
  return utxos
234
234
  }
235
235
 
236
- export function getUsableUtxos({ asset, utxos, feeData, txSet, unconfirmedTxAncestor }) {
236
+ const COINBASE_MATURITY_HEIGHT = 100
237
+
238
+ export function getUsableUtxos({
239
+ asset,
240
+ utxos: someCoinbaseUtxos,
241
+ feeData,
242
+ txSet,
243
+ unconfirmedTxAncestor,
244
+ }) {
237
245
  assert(asset, 'asset is required')
238
- assert(utxos, 'utxos is required')
246
+ assert(someCoinbaseUtxos, 'utxos is required')
239
247
  assert(feeData, 'feeData is required')
240
248
  assert(txSet, 'txSet is required')
249
+
250
+ // Filter out immature coinbase outputs. They are not spendable.
251
+ const utxos = someCoinbaseUtxos.filter(
252
+ (utxo) => !utxo.isCoinbase || utxo.confirmations >= COINBASE_MATURITY_HEIGHT
253
+ )
254
+
241
255
  if (!['bitcoin', 'bitcointestnet', 'bitcoinregtest'].includes(asset.name))
242
256
  return filterDustUtxos({ utxos, feeData })
243
257