@exodus/bitcoin-api 4.5.0 → 4.6.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 +16 -0
- package/package.json +2 -2
- package/src/psbt-builder.js +32 -8
- package/src/psbt-parser.js +23 -7
- package/src/psbt-utils.js +4 -0
- package/src/tx-create/create-tx.js +18 -6
- package/src/tx-send/index.js +14 -3
- package/src/tx-sign/common.js +9 -0
- package/src/tx-sign/default-create-tx.js +2 -6
- package/src/tx-sign/default-prepare-for-signing.js +29 -13
- package/src/tx-sign/default-sign-hardware.js +2 -6
- package/src/tx-sign/maximum-fee-rates.js +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
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.6.0](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.2.1...@exodus/bitcoin-api@4.6.0) (2025-11-14)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
* feat: add PSBT builder infrastructure (#6822)
|
|
13
|
+
|
|
14
|
+
* feat: add PSBT parser functionality (#6823)
|
|
15
|
+
|
|
16
|
+
* feat: integrate PSBT support and legacy chain index (#6819)
|
|
17
|
+
|
|
18
|
+
* feat: integrate PSBT support and legacy chain index (#6819) (#6829)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
## [4.5.0](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.2.1...@exodus/bitcoin-api@4.5.0) (2025-11-12)
|
|
7
23
|
|
|
8
24
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/bitcoin-api",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.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": "
|
|
63
|
+
"gitHead": "660b300377800da2edc9c250d61736b416db73d1"
|
|
64
64
|
}
|
package/src/psbt-builder.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import { payments
|
|
1
|
+
import { payments } from '@exodus/bitcoinjs'
|
|
2
2
|
import { publicKeyToX } from '@exodus/crypto/secp256k1'
|
|
3
3
|
import assert from 'minimalistic-assert'
|
|
4
4
|
|
|
5
5
|
import { SubType, writePsbtGlobalField, writePsbtOutputField } from './psbt-proprietary-types.js'
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import {
|
|
7
|
+
getAddressType,
|
|
8
|
+
getPurposeXPubs,
|
|
9
|
+
setPsbtVersionIfNotBitcoin,
|
|
10
|
+
validatePurpose,
|
|
11
|
+
} from './psbt-utils.js'
|
|
12
|
+
import { getMaximumFeeRate } from './tx-sign/maximum-fee-rates.js'
|
|
13
|
+
|
|
14
|
+
function canParseTx(rawTxBuffer, Transaction) {
|
|
9
15
|
try {
|
|
10
16
|
Transaction.fromBuffer(rawTxBuffer)
|
|
11
17
|
return true
|
|
@@ -70,7 +76,14 @@ function writeGlobalMetadata(psbt, metadata) {
|
|
|
70
76
|
}
|
|
71
77
|
}
|
|
72
78
|
|
|
73
|
-
function createPsbtInput({
|
|
79
|
+
function createPsbtInput({
|
|
80
|
+
input,
|
|
81
|
+
asset,
|
|
82
|
+
addressPathsMap,
|
|
83
|
+
purposeXPubs,
|
|
84
|
+
nonWitnessTxs,
|
|
85
|
+
Transaction,
|
|
86
|
+
}) {
|
|
74
87
|
const psbtInput = {
|
|
75
88
|
hash: input.txId,
|
|
76
89
|
index: input.vout,
|
|
@@ -111,7 +124,7 @@ function createPsbtInput({ input, asset, addressPathsMap, purposeXPubs, nonWitne
|
|
|
111
124
|
)
|
|
112
125
|
|
|
113
126
|
const rawTxBuffer = Buffer.from(rawTx.rawData, 'hex')
|
|
114
|
-
if (canParseTx(rawTxBuffer)) {
|
|
127
|
+
if (canParseTx(rawTxBuffer, Transaction)) {
|
|
115
128
|
psbtInput.nonWitnessUtxo = rawTxBuffer
|
|
116
129
|
} else {
|
|
117
130
|
// bitcoinjs can’t parse a handful of edge-case transactions (Litecoin MWEB, odd
|
|
@@ -181,6 +194,8 @@ export async function createPsbtWithMetadata({
|
|
|
181
194
|
addressPathsMap,
|
|
182
195
|
metadata,
|
|
183
196
|
allowedPurposes,
|
|
197
|
+
Psbt,
|
|
198
|
+
Transaction,
|
|
184
199
|
}) {
|
|
185
200
|
assert(inputs, 'inputs is required')
|
|
186
201
|
assert(outputs, 'outputs is required')
|
|
@@ -189,8 +204,16 @@ export async function createPsbtWithMetadata({
|
|
|
189
204
|
assert(walletAccount, 'walletAccount is required')
|
|
190
205
|
assert(addressPathsMap, 'addressPathsMap is required')
|
|
191
206
|
assert(metadata, 'metadata is required')
|
|
207
|
+
assert(Psbt, 'Psbt is required')
|
|
208
|
+
assert(Transaction, 'Transaction is required')
|
|
209
|
+
const psbtOptions = { network: asset.coinInfo.toBitcoinJS() }
|
|
210
|
+
const maximumFeeRate = getMaximumFeeRate(asset.name)
|
|
211
|
+
if (maximumFeeRate !== undefined) {
|
|
212
|
+
psbtOptions.maximumFeeRate = maximumFeeRate
|
|
213
|
+
}
|
|
192
214
|
|
|
193
|
-
const psbt = new Psbt(
|
|
215
|
+
const psbt = new Psbt(psbtOptions)
|
|
216
|
+
setPsbtVersionIfNotBitcoin(psbt, asset.name)
|
|
194
217
|
|
|
195
218
|
const purposeXPubs = await getPurposeXPubs({
|
|
196
219
|
assetClientInterface,
|
|
@@ -209,6 +232,7 @@ export async function createPsbtWithMetadata({
|
|
|
209
232
|
purposeXPubs,
|
|
210
233
|
nonWitnessTxs,
|
|
211
234
|
allowedPurposes,
|
|
235
|
+
Transaction,
|
|
212
236
|
})
|
|
213
237
|
psbt.addInput(psbtInput)
|
|
214
238
|
}
|
|
@@ -230,5 +254,5 @@ export async function createPsbtWithMetadata({
|
|
|
230
254
|
writePsbtOutputField(psbt, sendOutputIndex, SubType.OutputRole, 'primary')
|
|
231
255
|
}
|
|
232
256
|
|
|
233
|
-
return psbt
|
|
257
|
+
return psbt
|
|
234
258
|
}
|
package/src/psbt-parser.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Psbt, Transaction } from '@exodus/bitcoinjs'
|
|
2
1
|
import { publicKeyToX } from '@exodus/crypto/secp256k1'
|
|
3
2
|
import { UtxoCollection } from '@exodus/models'
|
|
4
3
|
import BipPath from 'bip32-path'
|
|
@@ -11,7 +10,7 @@ import { findUnconfirmedSentRbfTxs } from './tx-utils.js'
|
|
|
11
10
|
import { getUnconfirmedTxAncestorMap } from './unconfirmed-ancestor-data.js'
|
|
12
11
|
import { getUsableUtxos, getUtxos } from './utxos-utils.js'
|
|
13
12
|
|
|
14
|
-
function extractInputUtxoData(psbtInput, txInput, index) {
|
|
13
|
+
function extractInputUtxoData(psbtInput, txInput, index, Transaction) {
|
|
15
14
|
if (psbtInput.nonWitnessUtxo) {
|
|
16
15
|
const prevTx = Transaction.fromBuffer(psbtInput.nonWitnessUtxo)
|
|
17
16
|
const prevOutput = prevTx.outs[txInput.index]
|
|
@@ -34,14 +33,14 @@ function extractInputUtxoData(psbtInput, txInput, index) {
|
|
|
34
33
|
throw new Error(`Input ${index} has no witnessUtxo or nonWitnessUtxo`)
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
function parseSingleInput({ txInput, psbtInput, index, asset, purposeXPubs }) {
|
|
36
|
+
function parseSingleInput({ txInput, psbtInput, index, asset, purposeXPubs, Transaction }) {
|
|
38
37
|
const input = {
|
|
39
38
|
txId: Buffer.from(txInput.hash).reverse().toString('hex'),
|
|
40
39
|
vout: txInput.index,
|
|
41
40
|
sequence: txInput.sequence,
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
const inputUtxoData = extractInputUtxoData(psbtInput, txInput, index)
|
|
43
|
+
const inputUtxoData = extractInputUtxoData(psbtInput, txInput, index, Transaction)
|
|
45
44
|
|
|
46
45
|
input.value = inputUtxoData.value
|
|
47
46
|
input.script = Buffer.from(inputUtxoData.scriptBuffer).toString('hex')
|
|
@@ -117,9 +116,15 @@ function readGlobalMetadata(psbt) {
|
|
|
117
116
|
}
|
|
118
117
|
|
|
119
118
|
function calculateFee(inputs, outputs) {
|
|
120
|
-
const inputSum = inputs.reduce((sum, input) =>
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
const inputSum = inputs.reduce((sum, input) => {
|
|
120
|
+
const value = BigInt(input.value || 0)
|
|
121
|
+
return sum + value
|
|
122
|
+
}, BigInt(0))
|
|
123
|
+
const outputSum = outputs.reduce((sum, output) => {
|
|
124
|
+
const amount = BigInt(output.amount || 0)
|
|
125
|
+
return sum + amount
|
|
126
|
+
}, BigInt(0))
|
|
127
|
+
return inputSum - outputSum // Return BigInt directly
|
|
123
128
|
}
|
|
124
129
|
|
|
125
130
|
function getBip32Derivation(hdkey, bip32Derivations, ignoreY) {
|
|
@@ -329,11 +334,15 @@ export async function parsePsbt({
|
|
|
329
334
|
assetClientInterface,
|
|
330
335
|
walletAccount,
|
|
331
336
|
allowedPurposes,
|
|
337
|
+
Psbt,
|
|
338
|
+
Transaction,
|
|
332
339
|
}) {
|
|
333
340
|
assert(psbtBuffer, 'psbtBuffer is required')
|
|
334
341
|
assert(asset, 'asset is required')
|
|
335
342
|
assert(assetClientInterface, 'assetClientInterface is required')
|
|
336
343
|
assert(walletAccount, 'walletAccount is required')
|
|
344
|
+
assert(Psbt, 'Psbt is required')
|
|
345
|
+
assert(Transaction, 'Transaction is required')
|
|
337
346
|
|
|
338
347
|
const purposeXPubs = await getPurposeXPubs({
|
|
339
348
|
assetClientInterface,
|
|
@@ -353,6 +362,7 @@ export async function parsePsbt({
|
|
|
353
362
|
asset,
|
|
354
363
|
purposeXPubs,
|
|
355
364
|
allowedPurposes,
|
|
365
|
+
Transaction,
|
|
356
366
|
})
|
|
357
367
|
inputs.push(input)
|
|
358
368
|
}
|
|
@@ -388,11 +398,15 @@ export async function extractTransactionContext({
|
|
|
388
398
|
assetClientInterface,
|
|
389
399
|
walletAccount,
|
|
390
400
|
allowedPurposes,
|
|
401
|
+
Psbt,
|
|
402
|
+
Transaction,
|
|
391
403
|
}) {
|
|
392
404
|
assert(psbtBuffer, 'psbtBuffer is required')
|
|
393
405
|
assert(asset, 'asset is required')
|
|
394
406
|
assert(assetClientInterface, 'assetClientInterface is required')
|
|
395
407
|
assert(walletAccount, 'walletAccount is required')
|
|
408
|
+
assert(Psbt, 'Psbt is required')
|
|
409
|
+
assert(Transaction, 'Transaction is required')
|
|
396
410
|
|
|
397
411
|
const {
|
|
398
412
|
inputs: parsedInputs,
|
|
@@ -405,6 +419,8 @@ export async function extractTransactionContext({
|
|
|
405
419
|
assetClientInterface,
|
|
406
420
|
walletAccount,
|
|
407
421
|
allowedPurposes,
|
|
422
|
+
Psbt,
|
|
423
|
+
Transaction,
|
|
408
424
|
})
|
|
409
425
|
|
|
410
426
|
const changeOutputData = await getChangeOutputData({
|
package/src/psbt-utils.js
CHANGED
|
@@ -131,3 +131,7 @@ export async function withUnsafeNonSegwit({ psbt, fn, unsafe = true }) {
|
|
|
131
131
|
cache.__UNSAFE_SIGN_NONSEGWIT = prevValue
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
|
+
|
|
135
|
+
export function setPsbtVersionIfNotBitcoin(psbt, assetName) {
|
|
136
|
+
if (!['bitcoin', 'bitcoinregtest', 'bitcointestnet'].includes(assetName)) psbt.setVersion(1)
|
|
137
|
+
}
|
|
@@ -276,6 +276,8 @@ async function createUnsignedTx({
|
|
|
276
276
|
sendOutputIndex,
|
|
277
277
|
changeOutputIndex,
|
|
278
278
|
allowedPurposes,
|
|
279
|
+
Psbt,
|
|
280
|
+
Transaction,
|
|
279
281
|
}) {
|
|
280
282
|
const nonWitnessTxs = await getNonWitnessTxs(asset, selectedUtxos, insightClient)
|
|
281
283
|
|
|
@@ -292,11 +294,11 @@ async function createUnsignedTx({
|
|
|
292
294
|
},
|
|
293
295
|
}
|
|
294
296
|
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
|
|
298
|
-
if (
|
|
299
|
-
const
|
|
297
|
+
// Create PSBT for all UTXO coins that support it
|
|
298
|
+
// Note: Some coins (like BCash) may not use PSBT in their signing flow,
|
|
299
|
+
// but creating it here allows for a unified transaction flow
|
|
300
|
+
if (Psbt) {
|
|
301
|
+
const psbt = await createPsbtWithMetadata({
|
|
300
302
|
inputs,
|
|
301
303
|
outputs,
|
|
302
304
|
asset,
|
|
@@ -313,9 +315,11 @@ async function createUnsignedTx({
|
|
|
313
315
|
bumpTxId,
|
|
314
316
|
blockHeight,
|
|
315
317
|
},
|
|
318
|
+
Psbt,
|
|
319
|
+
Transaction,
|
|
316
320
|
})
|
|
317
321
|
|
|
318
|
-
result.txData.psbtBuffer =
|
|
322
|
+
result.txData.psbtBuffer = psbt.toBuffer()
|
|
319
323
|
}
|
|
320
324
|
|
|
321
325
|
return result
|
|
@@ -347,6 +351,8 @@ const transferHandler = {
|
|
|
347
351
|
assetClientInterface,
|
|
348
352
|
changeAddressType,
|
|
349
353
|
allowedPurposes,
|
|
354
|
+
Psbt,
|
|
355
|
+
Transaction,
|
|
350
356
|
}) => {
|
|
351
357
|
const assetName = asset.name
|
|
352
358
|
const insightClient = asset.baseAsset.insightClient
|
|
@@ -483,6 +489,8 @@ const transferHandler = {
|
|
|
483
489
|
sendOutputIndex: sendOutput ? outputs.indexOf(sendOutput) : undefined,
|
|
484
490
|
changeOutputIndex: changeOutput ? outputs.indexOf(changeOutput) : undefined,
|
|
485
491
|
allowedPurposes,
|
|
492
|
+
Psbt,
|
|
493
|
+
Transaction,
|
|
486
494
|
})
|
|
487
495
|
|
|
488
496
|
return {
|
|
@@ -516,6 +524,8 @@ export const createTxFactory =
|
|
|
516
524
|
assetClientInterface,
|
|
517
525
|
changeAddressType,
|
|
518
526
|
allowedPurposes,
|
|
527
|
+
Psbt,
|
|
528
|
+
Transaction,
|
|
519
529
|
}) =>
|
|
520
530
|
async ({
|
|
521
531
|
asset,
|
|
@@ -562,5 +572,7 @@ export const createTxFactory =
|
|
|
562
572
|
assetClientInterface,
|
|
563
573
|
changeAddressType,
|
|
564
574
|
allowedPurposes,
|
|
575
|
+
Psbt,
|
|
576
|
+
Transaction,
|
|
565
577
|
})
|
|
566
578
|
}
|
package/src/tx-send/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as defaultBitcoinjsLib from '@exodus/bitcoinjs'
|
|
2
|
+
import { Psbt as DefaultPsbt, Transaction as DefaultTransaction } from '@exodus/bitcoinjs'
|
|
2
3
|
import assert from 'minimalistic-assert'
|
|
3
4
|
|
|
4
5
|
import { extractTransactionContext } from '../psbt-parser.js'
|
|
@@ -110,6 +111,8 @@ const getPrepareSendTransaction = async ({
|
|
|
110
111
|
utxosDescendingOrder,
|
|
111
112
|
walletAccount,
|
|
112
113
|
allowedPurposes,
|
|
114
|
+
Psbt,
|
|
115
|
+
Transaction,
|
|
113
116
|
}) => {
|
|
114
117
|
const createTx = createTxFactory({
|
|
115
118
|
getFeeEstimator,
|
|
@@ -118,6 +121,8 @@ const getPrepareSendTransaction = async ({
|
|
|
118
121
|
assetClientInterface,
|
|
119
122
|
changeAddressType,
|
|
120
123
|
allowedPurposes,
|
|
124
|
+
Psbt,
|
|
125
|
+
Transaction,
|
|
121
126
|
})
|
|
122
127
|
|
|
123
128
|
// Set default values for options
|
|
@@ -134,7 +139,7 @@ const getPrepareSendTransaction = async ({
|
|
|
134
139
|
})
|
|
135
140
|
}
|
|
136
141
|
|
|
137
|
-
function toTransactionDescriptor(txContext, psbtBuffer) {
|
|
142
|
+
function toTransactionDescriptor({ txContext, psbtBuffer }) {
|
|
138
143
|
const {
|
|
139
144
|
inputs,
|
|
140
145
|
outputs,
|
|
@@ -198,8 +203,10 @@ export const createAndBroadcastTXFactory =
|
|
|
198
203
|
assetClientInterface,
|
|
199
204
|
changeAddressType,
|
|
200
205
|
allowedPurposes,
|
|
206
|
+
Psbt = DefaultPsbt,
|
|
207
|
+
Transaction = DefaultTransaction,
|
|
201
208
|
}) =>
|
|
202
|
-
async ({ asset, walletAccount, address, amount, options }) => {
|
|
209
|
+
async ({ asset, walletAccount, address, amount, ...options }) => {
|
|
203
210
|
// Prepare transaction
|
|
204
211
|
const { bumpTxId } = options
|
|
205
212
|
|
|
@@ -218,6 +225,8 @@ export const createAndBroadcastTXFactory =
|
|
|
218
225
|
utxosDescendingOrder,
|
|
219
226
|
walletAccount,
|
|
220
227
|
allowedPurposes,
|
|
228
|
+
Psbt,
|
|
229
|
+
Transaction,
|
|
221
230
|
})
|
|
222
231
|
|
|
223
232
|
// If we already created a PSBT for Bitcoin, hydrate the full transaction
|
|
@@ -231,8 +240,10 @@ export const createAndBroadcastTXFactory =
|
|
|
231
240
|
assetClientInterface,
|
|
232
241
|
walletAccount,
|
|
233
242
|
allowedPurposes,
|
|
243
|
+
Psbt,
|
|
244
|
+
Transaction,
|
|
234
245
|
})
|
|
235
|
-
transactionDescriptor = toTransactionDescriptor(txContext, psbtBuffer)
|
|
246
|
+
transactionDescriptor = toTransactionDescriptor({ txContext, psbtBuffer })
|
|
236
247
|
unsignedTx = transactionDescriptor.unsignedTx
|
|
237
248
|
metadata = transactionDescriptor.metadata
|
|
238
249
|
} else {
|
package/src/tx-sign/common.js
CHANGED
|
@@ -27,3 +27,12 @@ export const serializeTx = ({ tx }) => {
|
|
|
27
27
|
})),
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
|
|
31
|
+
export function shouldSkipFinalize(unsignedTx) {
|
|
32
|
+
const isExternalPsbt =
|
|
33
|
+
unsignedTx.txData.psbtBuffer &&
|
|
34
|
+
unsignedTx.txMeta.addressPathsMap &&
|
|
35
|
+
unsignedTx.txMeta.inputsToSign
|
|
36
|
+
|
|
37
|
+
return isExternalPsbt || unsignedTx.txMeta.returnPsbt
|
|
38
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Psbt as DefaultPsbt, Transaction as DefaultTransaction } from '@exodus/bitcoinjs'
|
|
2
2
|
import assert from 'minimalistic-assert'
|
|
3
3
|
|
|
4
|
-
import { extractTransaction } from './common.js'
|
|
4
|
+
import { extractTransaction, shouldSkipFinalize } from './common.js'
|
|
5
5
|
import { createSignWithWallet } from './create-sign-with-wallet.js'
|
|
6
6
|
import { createPrepareForSigning } from './default-prepare-for-signing.js'
|
|
7
7
|
|
|
@@ -57,11 +57,7 @@ export const signTxFactory = ({
|
|
|
57
57
|
},
|
|
58
58
|
})
|
|
59
59
|
|
|
60
|
-
const
|
|
61
|
-
unsignedTx.txData.psbtBuffer &&
|
|
62
|
-
unsignedTx.txMeta.addressPathsMap &&
|
|
63
|
-
unsignedTx.txMeta.inputsToSign
|
|
64
|
-
const skipFinalize = isExternalPsbt || unsignedTx.txMeta.returnPsbt
|
|
60
|
+
const skipFinalize = shouldSkipFinalize(unsignedTx)
|
|
65
61
|
await signWithWallet(psbt, inputsToSign, skipFinalize)
|
|
66
62
|
return extractTransaction({ psbt, skipFinalize })
|
|
67
63
|
}
|
|
@@ -2,11 +2,8 @@ import { Psbt as DefaultPsbt, Transaction as DefaultTransaction } from '@exodus/
|
|
|
2
2
|
import assert from 'minimalistic-assert'
|
|
3
3
|
|
|
4
4
|
import { writePsbtBlockHeight } from '../psbt-proprietary-types.js'
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
qtumignition: 25_000,
|
|
8
|
-
ravencoin: 1_000_000,
|
|
9
|
-
}
|
|
5
|
+
import { setPsbtVersionIfNotBitcoin } from '../psbt-utils.js'
|
|
6
|
+
import { getMaximumFeeRate } from './maximum-fee-rates.js'
|
|
10
7
|
|
|
11
8
|
/**
|
|
12
9
|
* Factory function that create the prepareForSigning function for a bitcoin-like asset.
|
|
@@ -26,9 +23,19 @@ export function createPrepareForSigning({
|
|
|
26
23
|
|
|
27
24
|
return ({ unsignedTx }) => {
|
|
28
25
|
const networkInfo = coinInfo.toBitcoinJS()
|
|
26
|
+
const maximumFeeRate = getMaximumFeeRate(assetName)
|
|
27
|
+
|
|
28
|
+
const psbtOptions = { network: networkInfo }
|
|
29
|
+
if (maximumFeeRate !== undefined) {
|
|
30
|
+
psbtOptions.maximumFeeRate = maximumFeeRate
|
|
31
|
+
}
|
|
29
32
|
|
|
30
33
|
if (unsignedTx.txData.psbtBuffer) {
|
|
31
|
-
return createPsbtFromBuffer({
|
|
34
|
+
return createPsbtFromBuffer({
|
|
35
|
+
Psbt,
|
|
36
|
+
psbtBuffer: unsignedTx.txData.psbtBuffer,
|
|
37
|
+
psbtOptions,
|
|
38
|
+
})
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
// Create PSBT based on internal Exodus data structure
|
|
@@ -41,15 +48,17 @@ export function createPrepareForSigning({
|
|
|
41
48
|
Psbt,
|
|
42
49
|
Transaction,
|
|
43
50
|
})
|
|
44
|
-
|
|
51
|
+
setPsbtVersionIfNotBitcoin(psbt, assetName)
|
|
45
52
|
|
|
46
53
|
return psbt
|
|
47
54
|
}
|
|
48
55
|
}
|
|
49
56
|
|
|
50
57
|
// Creates a PSBT instance from the passed transaction buffer provided by 3rd parties (e.g. dApps).
|
|
51
|
-
function createPsbtFromBuffer({ Psbt, psbtBuffer, ecc,
|
|
52
|
-
|
|
58
|
+
function createPsbtFromBuffer({ Psbt, psbtBuffer, ecc, psbtOptions = {} }) {
|
|
59
|
+
const fromBufferOptions = { ...psbtOptions }
|
|
60
|
+
if (ecc) fromBufferOptions.eccLib = ecc
|
|
61
|
+
return Psbt.fromBuffer(psbtBuffer, fromBufferOptions)
|
|
53
62
|
}
|
|
54
63
|
|
|
55
64
|
// Creates a PSBT instance from the passed inputs, outputs etc. The wallet itself provides this data.
|
|
@@ -64,11 +73,18 @@ function createPsbtFromTxData({
|
|
|
64
73
|
Transaction,
|
|
65
74
|
blockHeight,
|
|
66
75
|
}) {
|
|
67
|
-
// use
|
|
68
|
-
// if undefined,
|
|
69
|
-
|
|
76
|
+
// use hardcoded max fee rates for specific assets
|
|
77
|
+
// if undefined, the PSBT default (5000 sat/vB) is used. Passing `undefined`
|
|
78
|
+
// directly into the constructor used to clobber the default (buggy behavior),
|
|
79
|
+
// so we now only set the override when we actually have one.
|
|
80
|
+
const maximumFeeRate = getMaximumFeeRate(assetName)
|
|
81
|
+
|
|
82
|
+
const psbtOptions = { network: networkInfo }
|
|
83
|
+
if (maximumFeeRate !== undefined) {
|
|
84
|
+
psbtOptions.maximumFeeRate = maximumFeeRate
|
|
85
|
+
}
|
|
70
86
|
|
|
71
|
-
const psbt = new Psbt(
|
|
87
|
+
const psbt = new Psbt(psbtOptions)
|
|
72
88
|
|
|
73
89
|
// If present, add blockHeight as a proprietary field
|
|
74
90
|
if (blockHeight !== undefined) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from 'minimalistic-assert'
|
|
2
2
|
|
|
3
|
-
import { extractTransaction } from './common.js'
|
|
3
|
+
import { extractTransaction, shouldSkipFinalize } from './common.js'
|
|
4
4
|
import { createPrepareForSigning } from './default-prepare-for-signing.js'
|
|
5
5
|
|
|
6
6
|
export const signHardwareFactory = ({ assetName, resolvePurpose, keys, coinInfo }) => {
|
|
@@ -40,11 +40,7 @@ export const signHardwareFactory = ({ assetName, resolvePurpose, keys, coinInfo
|
|
|
40
40
|
multisigData,
|
|
41
41
|
})
|
|
42
42
|
|
|
43
|
-
const
|
|
44
|
-
unsignedTx.txData.psbtBuffer &&
|
|
45
|
-
unsignedTx.txMeta.addressPathsMap &&
|
|
46
|
-
unsignedTx.txMeta.inputsToSign
|
|
47
|
-
const skipFinalize = isExternalPsbt || unsignedTx.txMeta.returnPsbt
|
|
43
|
+
const skipFinalize = shouldSkipFinalize(unsignedTx)
|
|
48
44
|
return extractTransaction({ psbt, skipFinalize })
|
|
49
45
|
}
|
|
50
46
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// We keep hardcoded fee caps here so both signing and PSBT builders reuse the
|
|
2
|
+
// same per-asset overrides (e.g. Dogecoin needs a higher ceiling than the
|
|
3
|
+
// bitcoinjs default). If an asset is missing, the library fallback (5000 sat/vB)
|
|
4
|
+
// will apply instead.
|
|
5
|
+
const MAXIMUM_FEE_RATES = {
|
|
6
|
+
dogecoin: 12_000,
|
|
7
|
+
qtumignition: 25_000,
|
|
8
|
+
ravencoin: 1_000_000,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getMaximumFeeRate(assetName) {
|
|
12
|
+
return MAXIMUM_FEE_RATES[assetName]
|
|
13
|
+
}
|