@exodus/bitcoin-api 2.27.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,34 @@
|
|
|
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
|
+
|
|
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)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* update bip322-js to 2.0.0 ([#4414](https://github.com/ExodusMovement/assets/issues/4414)) ([da2a7dd](https://github.com/ExodusMovement/assets/commit/da2a7dd217a6664f02fa722968bf6ff16f53ea39))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Bug Fixes
|
|
29
|
+
|
|
30
|
+
* stringify sent amount NU's ([#4433](https://github.com/ExodusMovement/assets/issues/4433)) ([f2886a8](https://github.com/ExodusMovement/assets/commit/f2886a85060f5e347df9bb2bf892bb52257ece6b)), closes [#4437](https://github.com/ExodusMovement/assets/issues/4437)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
6
34
|
## [2.27.0](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@2.26.1...@exodus/bitcoin-api@2.27.0) (2024-10-17)
|
|
7
35
|
|
|
8
36
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/bitcoin-api",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.29.0",
|
|
4
4
|
"description": "Exodus bitcoin-api",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"@exodus/asset-lib": "^5.0.0",
|
|
24
24
|
"@exodus/basic-utils": "^3.0.1",
|
|
25
25
|
"@exodus/bip32": "^3.3.0",
|
|
26
|
-
"@exodus/bip322-js": "^
|
|
26
|
+
"@exodus/bip322-js": "^2.0.0",
|
|
27
27
|
"@exodus/bip44-constants": "^195.0.0",
|
|
28
28
|
"@exodus/bitcoin-lib": "^2.4.2",
|
|
29
29
|
"@exodus/bitcoinjs": "^1.1.0",
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"type": "git",
|
|
57
57
|
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
58
58
|
},
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "500277d2c77cd37f7d7a93eceec09e5b8c49f253"
|
|
60
60
|
}
|
package/src/fee/fee-utils.js
CHANGED
|
@@ -58,3 +58,11 @@ export function parseCurrency(val, currency) {
|
|
|
58
58
|
if (typeof val === 'string') return currency.parse(val)
|
|
59
59
|
return currency.parse(val.value + ' ' + val.unit)
|
|
60
60
|
}
|
|
61
|
+
|
|
62
|
+
export function serializeCurrency(val, currency) {
|
|
63
|
+
if (val === undefined) {
|
|
64
|
+
return val
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return parseCurrency(val, currency).toDefaultString({ unit: true })
|
|
68
|
+
}
|
|
@@ -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
|
}
|
|
@@ -505,7 +506,10 @@ export class BitcoinMonitorScanner {
|
|
|
505
506
|
if (isSent && !txLogItem.to) {
|
|
506
507
|
const val = currency.defaultUnit(vout.value)
|
|
507
508
|
const sentDisplayAddress = asset.address.displayAddress?.(sentAddress) || sentAddress
|
|
508
|
-
txLogItem.data.sent.push({
|
|
509
|
+
txLogItem.data.sent.push({
|
|
510
|
+
address: sentDisplayAddress,
|
|
511
|
+
amount: val.toDefaultString({ unit: true }),
|
|
512
|
+
})
|
|
509
513
|
}
|
|
510
514
|
|
|
511
515
|
return
|
|
@@ -541,6 +545,7 @@ export class BitcoinMonitorScanner {
|
|
|
541
545
|
script: vout.scriptPubKey.hex,
|
|
542
546
|
value: val,
|
|
543
547
|
rbfEnabled: txItem.rbf,
|
|
548
|
+
...(txItem.vin.length === 0 ? { isCoinbase: true } : undefined),
|
|
544
549
|
}
|
|
545
550
|
|
|
546
551
|
if (this.#ordinalsEnabled) {
|
package/src/tx-send/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { retry } from '@exodus/simple-retry'
|
|
|
5
5
|
import lodash from 'lodash'
|
|
6
6
|
import assert from 'minimalistic-assert'
|
|
7
7
|
|
|
8
|
-
import { parseCurrency } from '../fee/fee-utils.js'
|
|
8
|
+
import { parseCurrency, serializeCurrency } from '../fee/fee-utils.js'
|
|
9
9
|
import { selectUtxos } from '../fee/utxo-selector.js'
|
|
10
10
|
import { findUnconfirmedSentRbfTxs } from '../tx-utils.js'
|
|
11
11
|
import { getUnconfirmedTxAncestorMap } from '../unconfirmed-ancestor-data.js'
|
|
@@ -71,7 +71,14 @@ export async function getNonWitnessTxs(asset, utxos, insightClient) {
|
|
|
71
71
|
utxos
|
|
72
72
|
.getAddressesForTxId(txId)
|
|
73
73
|
.toAddressStrings()
|
|
74
|
-
.some(
|
|
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) {
|
|
@@ -376,7 +383,7 @@ export const getPrepareSendTransaction =
|
|
|
376
383
|
replaceTx = replaceTx.clone()
|
|
377
384
|
replaceTx = replaceTx.update({ data: { ...replaceTx.data } })
|
|
378
385
|
replaceTx.data.sent = replaceTx.data.sent.map((to) => {
|
|
379
|
-
return { ...to, amount:
|
|
386
|
+
return { ...to, amount: serializeCurrency(to.amount, asset.currency) }
|
|
380
387
|
})
|
|
381
388
|
selectedUtxos = selectedUtxos.union(
|
|
382
389
|
// how to avoid replace tx inputs when inputs are ordinals? !!!!
|
|
@@ -389,7 +396,9 @@ export const getPrepareSendTransaction =
|
|
|
389
396
|
// Inputs and Outputs
|
|
390
397
|
const inputs = shuffle(createInputs(assetName, selectedUtxos.toArray(), rbfEnabled))
|
|
391
398
|
let outputs = replaceTx
|
|
392
|
-
? replaceTx.data.sent.map(({ address, amount }) =>
|
|
399
|
+
? replaceTx.data.sent.map(({ address, amount }) =>
|
|
400
|
+
createOutput(assetName, address, parseCurrency(amount, currency))
|
|
401
|
+
)
|
|
393
402
|
: []
|
|
394
403
|
|
|
395
404
|
// Send output
|
|
@@ -408,7 +417,10 @@ export const getPrepareSendTransaction =
|
|
|
408
417
|
}
|
|
409
418
|
|
|
410
419
|
const totalAmount = replaceTx
|
|
411
|
-
? replaceTx.data.sent.reduce(
|
|
420
|
+
? replaceTx.data.sent.reduce(
|
|
421
|
+
(total, { amount }) => total.add(parseCurrency(amount, asset.currency)),
|
|
422
|
+
sendAmount
|
|
423
|
+
)
|
|
412
424
|
: sendAmount
|
|
413
425
|
|
|
414
426
|
const change = selectedUtxos.value
|
|
@@ -655,8 +667,11 @@ export const createAndBroadcastTXFactory =
|
|
|
655
667
|
? replaceTx.data.sent
|
|
656
668
|
: []
|
|
657
669
|
: replaceTx
|
|
658
|
-
? [
|
|
659
|
-
|
|
670
|
+
? [
|
|
671
|
+
...replaceTx.data.sent,
|
|
672
|
+
{ address: displayReceiveAddress, amount: serializeCurrency(amount, asset.currency) },
|
|
673
|
+
]
|
|
674
|
+
: [{ address: displayReceiveAddress, amount: serializeCurrency(amount, asset.currency) }]
|
|
660
675
|
|
|
661
676
|
const calculateCoinAmount = () => {
|
|
662
677
|
if (selfSend) {
|
|
@@ -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
|
-
|
|
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
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
package/src/utxos-utils.js
CHANGED
|
@@ -233,11 +233,25 @@ function filterDustUtxos({ utxos, feeData }) {
|
|
|
233
233
|
return utxos
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
-
|
|
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(
|
|
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
|
|