@exodus/bitcoin-api 4.8.3 → 4.9.1
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 +20 -0
- package/package.json +3 -2
- package/src/fee/utxo-selector.js +4 -7
- package/src/index.js +1 -0
- package/src/insight-api-client/index.js +58 -33
- package/src/psbt-parser.js +1 -1
- package/src/tx-create/create-tx.js +8 -5
- package/src/tx-send/index.js +29 -95
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.1](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.9.0...@exodus/bitcoin-api@4.9.1) (2026-01-26)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
* fix(bitcoin-api): add safe-string errors for Insight failures (#7320)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## [4.9.0](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.8.3...@exodus/bitcoin-api@4.9.0) (2025-12-23)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
* feat: Expose utxo sendTx workflow to accept provided unsigned tx (#6890)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
6
26
|
## [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
27
|
|
|
8
28
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/bitcoin-api",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.1",
|
|
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",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"@exodus/i18n-dummy": "^1.0.0",
|
|
34
34
|
"@exodus/key-identifier": "^1.3.0",
|
|
35
35
|
"@exodus/models": "^12.0.1",
|
|
36
|
+
"@exodus/safe-string": "^1.4.0",
|
|
36
37
|
"@exodus/send-validation-model": "^1.0.0",
|
|
37
38
|
"@exodus/simple-retry": "^0.0.6",
|
|
38
39
|
"bech32": "^1.1.3",
|
|
@@ -60,5 +61,5 @@
|
|
|
60
61
|
"type": "git",
|
|
61
62
|
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
62
63
|
},
|
|
63
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "481b327c2b57d6272c47807615be9f967ebb3d13"
|
|
64
65
|
}
|
package/src/fee/utxo-selector.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import NumberUnit from '@exodus/currency'
|
|
2
1
|
import { UtxoCollection } from '@exodus/models'
|
|
3
2
|
import lodash from 'lodash'
|
|
4
3
|
import assert from 'minimalistic-assert'
|
|
@@ -68,12 +67,10 @@ export const selectUtxos = ({
|
|
|
68
67
|
const changeUtxos = usableUtxos.getTxIdUtxos(tx.txId)
|
|
69
68
|
// Don't replace a tx that has already been spent
|
|
70
69
|
if (tx.data.changeAddress && changeUtxos.size === 0) continue
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
feePerKB = feeRate
|
|
76
|
-
}
|
|
70
|
+
const feePerKB =
|
|
71
|
+
tx.data.feePerKB + MIN_RELAY_FEE > feeRate.toBaseNumber()
|
|
72
|
+
? currency.baseUnit(tx.data.feePerKB + MIN_RELAY_FEE)
|
|
73
|
+
: feeRate
|
|
77
74
|
|
|
78
75
|
const replaceFeeEstimator = getFeeEstimator(asset, { feePerKB, unconfirmedTxAncestor })
|
|
79
76
|
const inputs = UtxoCollection.fromJSON(tx.data.inputs, { currency })
|
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'
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { safeString } from '@exodus/safe-string'
|
|
1
2
|
import { retry } from '@exodus/simple-retry'
|
|
2
3
|
import delay from 'delay'
|
|
3
4
|
import lodash from 'lodash'
|
|
@@ -5,16 +6,29 @@ import urlJoin from 'url-join'
|
|
|
5
6
|
|
|
6
7
|
const { isEmpty } = lodash
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
9
|
+
const INSIGHT_HTTP_ERROR_MESSAGE = safeString`insight-api-http-error`
|
|
10
|
+
const INSIGHT_JSON_ERROR_MESSAGE = safeString`insight-api-invalid-json`
|
|
11
|
+
const INSIGHT_MISSING_TXID_MESSAGE = safeString`insight-api-missing-txid`
|
|
12
|
+
const INSIGHT_HTTP_ERROR_BALANCE_MESSAGE = safeString`insight-api-http-error:balance`
|
|
13
|
+
const INSIGHT_HTTP_ERROR_STATUS_MESSAGE = safeString`insight-api-http-error:status`
|
|
14
|
+
const INSIGHT_HTTP_ERROR_ADDR_MESSAGE = safeString`insight-api-http-error:addr`
|
|
15
|
+
const INSIGHT_HTTP_ERROR_UTXO_MESSAGE = safeString`insight-api-http-error:utxo`
|
|
16
|
+
const INSIGHT_HTTP_ERROR_TX_MESSAGE = safeString`insight-api-http-error:tx`
|
|
17
|
+
const INSIGHT_HTTP_ERROR_FULLTX_MESSAGE = safeString`insight-api-http-error:fulltx`
|
|
18
|
+
const INSIGHT_HTTP_ERROR_RAWTX_MESSAGE = safeString`insight-api-http-error:rawtx`
|
|
19
|
+
const INSIGHT_HTTP_ERROR_ADDRS_TXS_MESSAGE = safeString`insight-api-http-error:addrs-txs`
|
|
20
|
+
const INSIGHT_HTTP_ERROR_UNCONFIRMED_ANCESTOR_MESSAGE = safeString`insight-api-http-error:unconfirmed-ancestor`
|
|
21
|
+
const INSIGHT_HTTP_ERROR_FEES_MESSAGE = safeString`insight-api-http-error:fees`
|
|
22
|
+
const INSIGHT_HTTP_ERROR_BROADCAST_MESSAGE = safeString`insight-api-http-error:broadcast`
|
|
23
|
+
const INSIGHT_HTTP_ERROR_CLAIMABLE_MESSAGE = safeString`insight-api-http-error:claimable`
|
|
24
|
+
const INSIGHT_HTTP_ERROR_UNCLAIMED_MESSAGE = safeString`insight-api-http-error:unclaimed`
|
|
25
|
+
|
|
26
|
+
const fetchJson = async (
|
|
27
|
+
url,
|
|
28
|
+
fetchOptions,
|
|
29
|
+
nullWhen404,
|
|
30
|
+
httpErrorMessage = INSIGHT_HTTP_ERROR_MESSAGE
|
|
31
|
+
) => {
|
|
18
32
|
const response = await fetch(url, fetchOptions)
|
|
19
33
|
|
|
20
34
|
if (nullWhen404 && response.status === 404) {
|
|
@@ -22,20 +36,22 @@ const fetchJson = async (url, fetchOptions, nullWhen404) => {
|
|
|
22
36
|
}
|
|
23
37
|
|
|
24
38
|
if (!response.ok) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}. Body: ${await getTextFromResponse(response)}`
|
|
29
|
-
)
|
|
39
|
+
const error = new Error(httpErrorMessage)
|
|
40
|
+
error.code = `${response.status}`
|
|
41
|
+
throw error
|
|
30
42
|
}
|
|
31
43
|
|
|
32
|
-
|
|
44
|
+
try {
|
|
45
|
+
return await response.json()
|
|
46
|
+
} catch (err) {
|
|
47
|
+
throw new Error(INSIGHT_JSON_ERROR_MESSAGE, { cause: err })
|
|
48
|
+
}
|
|
33
49
|
}
|
|
34
50
|
|
|
35
|
-
async function fetchJsonRetry(url, fetchOptions) {
|
|
51
|
+
async function fetchJsonRetry(url, fetchOptions, httpErrorMessage) {
|
|
36
52
|
const waitTimes = ['5s', '10s', '20s', '30s']
|
|
37
53
|
const fetchWithRetry = retry(fetchJson, { delayTimesMs: waitTimes })
|
|
38
|
-
return fetchWithRetry(url, fetchOptions)
|
|
54
|
+
return fetchWithRetry(url, fetchOptions, false, httpErrorMessage)
|
|
39
55
|
}
|
|
40
56
|
|
|
41
57
|
export default class InsightAPIClient {
|
|
@@ -56,12 +72,17 @@ export default class InsightAPIClient {
|
|
|
56
72
|
async fetchBalance(address) {
|
|
57
73
|
const encodedAddress = encodeURIComponent(address)
|
|
58
74
|
const url = urlJoin(this._baseURL, `/balance/${encodedAddress}`)
|
|
59
|
-
return fetchJson(url)
|
|
75
|
+
return fetchJson(url, undefined, false, INSIGHT_HTTP_ERROR_BALANCE_MESSAGE)
|
|
60
76
|
}
|
|
61
77
|
|
|
62
78
|
async fetchBlockHeight() {
|
|
63
79
|
const url = urlJoin(this._baseURL, '/status')
|
|
64
|
-
const status = await fetchJson(
|
|
80
|
+
const status = await fetchJson(
|
|
81
|
+
url,
|
|
82
|
+
{ timeout: 10_000 },
|
|
83
|
+
false,
|
|
84
|
+
INSIGHT_HTTP_ERROR_STATUS_MESSAGE
|
|
85
|
+
)
|
|
65
86
|
return status.info.blocks
|
|
66
87
|
}
|
|
67
88
|
|
|
@@ -71,7 +92,7 @@ export default class InsightAPIClient {
|
|
|
71
92
|
this._baseURL,
|
|
72
93
|
opts?.includeTxs ? `/addr/${encodedAddress}` : `/addr/${encodedAddress}?noTxList=1`
|
|
73
94
|
)
|
|
74
|
-
return fetchJson(url)
|
|
95
|
+
return fetchJson(url, undefined, false, INSIGHT_HTTP_ERROR_ADDR_MESSAGE)
|
|
75
96
|
}
|
|
76
97
|
|
|
77
98
|
async fetchUTXOs(addresses, { assetNames = [] } = {}) {
|
|
@@ -83,7 +104,7 @@ export default class InsightAPIClient {
|
|
|
83
104
|
|
|
84
105
|
const encodedAddresses = encodeURIComponent(addresses)
|
|
85
106
|
const url = urlJoin(this._baseURL, `/addrs/${encodedAddresses}/utxo?${query}`)
|
|
86
|
-
const utxos = await fetchJson(url)
|
|
107
|
+
const utxos = await fetchJson(url, undefined, false, INSIGHT_HTTP_ERROR_UTXO_MESSAGE)
|
|
87
108
|
|
|
88
109
|
return utxos.map((utxo) => ({
|
|
89
110
|
address: utxo.address,
|
|
@@ -100,12 +121,12 @@ export default class InsightAPIClient {
|
|
|
100
121
|
async fetchTx(txId) {
|
|
101
122
|
const encodedTxId = encodeURIComponent(txId)
|
|
102
123
|
const url = urlJoin(this._baseURL, `/tx/${encodedTxId}`)
|
|
103
|
-
return fetchJson(url, undefined, true)
|
|
124
|
+
return fetchJson(url, undefined, true, INSIGHT_HTTP_ERROR_TX_MESSAGE)
|
|
104
125
|
}
|
|
105
126
|
|
|
106
127
|
async fetchTxObject(txId) {
|
|
107
128
|
const url = urlJoin(this._baseURL, `/fulltx?${new URLSearchParams({ hash: txId })}`)
|
|
108
|
-
const object = await fetchJson(url, undefined, true)
|
|
129
|
+
const object = await fetchJson(url, undefined, true, INSIGHT_HTTP_ERROR_FULLTX_MESSAGE)
|
|
109
130
|
if (!object || isEmpty(object)) {
|
|
110
131
|
return null
|
|
111
132
|
}
|
|
@@ -116,7 +137,7 @@ export default class InsightAPIClient {
|
|
|
116
137
|
async fetchRawTx(txId) {
|
|
117
138
|
const encodedTxId = encodeURIComponent(txId)
|
|
118
139
|
const url = urlJoin(this._baseURL, `/rawtx/${encodedTxId}`)
|
|
119
|
-
const { rawtx } = await fetchJson(url)
|
|
140
|
+
const { rawtx } = await fetchJson(url, undefined, false, INSIGHT_HTTP_ERROR_RAWTX_MESSAGE)
|
|
120
141
|
return rawtx
|
|
121
142
|
}
|
|
122
143
|
|
|
@@ -136,7 +157,7 @@ export default class InsightAPIClient {
|
|
|
136
157
|
timeout: 10_000,
|
|
137
158
|
}
|
|
138
159
|
|
|
139
|
-
return fetchJsonRetry(url, fetchOptions)
|
|
160
|
+
return fetchJsonRetry(url, fetchOptions, INSIGHT_HTTP_ERROR_ADDRS_TXS_MESSAGE)
|
|
140
161
|
}
|
|
141
162
|
|
|
142
163
|
async fetchAllTxData(addrs = [], chunk = 25, httpDelay = 2000, shouldStopFetching = () => {}) {
|
|
@@ -159,12 +180,12 @@ export default class InsightAPIClient {
|
|
|
159
180
|
async fetchUnconfirmedAncestorData(txId) {
|
|
160
181
|
const encodedTxId = encodeURIComponent(txId)
|
|
161
182
|
const url = urlJoin(this._baseURL, `/unconfirmed_ancestor/${encodedTxId}`)
|
|
162
|
-
return fetchJson(url)
|
|
183
|
+
return fetchJson(url, undefined, false, INSIGHT_HTTP_ERROR_UNCONFIRMED_ANCESTOR_MESSAGE)
|
|
163
184
|
}
|
|
164
185
|
|
|
165
186
|
async fetchFeeRate() {
|
|
166
187
|
const url = urlJoin(this._baseURL, '/v2/fees')
|
|
167
|
-
return fetchJson(url)
|
|
188
|
+
return fetchJson(url, undefined, false, INSIGHT_HTTP_ERROR_FEES_MESSAGE)
|
|
168
189
|
}
|
|
169
190
|
|
|
170
191
|
async broadcastTx(rawTx) {
|
|
@@ -187,28 +208,32 @@ export default class InsightAPIClient {
|
|
|
187
208
|
console.warn('Insight Broadcast HTTP Error:')
|
|
188
209
|
console.warn(response.statusText)
|
|
189
210
|
console.warn(data)
|
|
190
|
-
|
|
211
|
+
const error = new Error(INSIGHT_HTTP_ERROR_BROADCAST_MESSAGE)
|
|
212
|
+
error.code = `${response.status}`
|
|
213
|
+
throw error
|
|
191
214
|
}
|
|
192
215
|
|
|
193
216
|
try {
|
|
194
217
|
data = JSON.parse(data)
|
|
195
218
|
} catch (err) {
|
|
196
219
|
console.warn('Insight Broadcast JSON Parse Error:', err.message, data)
|
|
197
|
-
throw new Error(
|
|
220
|
+
throw new Error(INSIGHT_JSON_ERROR_MESSAGE, { cause: err })
|
|
198
221
|
}
|
|
199
222
|
|
|
200
|
-
if (!data.txid)
|
|
223
|
+
if (!data.txid) {
|
|
224
|
+
throw new Error(INSIGHT_MISSING_TXID_MESSAGE)
|
|
225
|
+
}
|
|
201
226
|
}
|
|
202
227
|
|
|
203
228
|
async getClaimable(address) {
|
|
204
229
|
const encodedAddress = encodeURIComponent(address)
|
|
205
230
|
const url = urlJoin(this._baseURL, `/addr/${encodedAddress}/claimable`)
|
|
206
|
-
return fetchJson(url)
|
|
231
|
+
return fetchJson(url, undefined, false, INSIGHT_HTTP_ERROR_CLAIMABLE_MESSAGE)
|
|
207
232
|
}
|
|
208
233
|
|
|
209
234
|
async getUnclaimed(address) {
|
|
210
235
|
const encodedAddress = encodeURIComponent(address)
|
|
211
236
|
const url = urlJoin(this._baseURL, `/addr/${encodedAddress}/unclaimed`)
|
|
212
|
-
return fetchJson(url)
|
|
237
|
+
return fetchJson(url, undefined, false, INSIGHT_HTTP_ERROR_UNCLAIMED_MESSAGE)
|
|
213
238
|
}
|
|
214
239
|
}
|
package/src/psbt-parser.js
CHANGED
|
@@ -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
|
-
//
|
|
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,
|
package/src/tx-send/index.js
CHANGED
|
@@ -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
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}) =>
|
|
185
|
-
|
|
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
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
|
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'
|