@exodus/bitcoin-api 4.1.3 → 4.1.4
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 +8 -0
- package/package.json +2 -2
- package/src/tx-create/create-tx.js +15 -13
- package/src/tx-send/broadcast-tx.js +48 -0
- package/src/tx-send/index.js +34 -150
- package/src/tx-send/update-state.js +188 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
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.1.4](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.1.3...@exodus/bitcoin-api@4.1.4) (2025-10-15)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @exodus/bitcoin-api
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
6
14
|
## [4.1.3](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.1.1...@exodus/bitcoin-api@4.1.3) (2025-10-14)
|
|
7
15
|
|
|
8
16
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/bitcoin-api",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.4",
|
|
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": "0efd644cc413c308688b43c8563b172b3a80e3fa"
|
|
64
64
|
}
|
|
@@ -243,20 +243,22 @@ const transferHandler = {
|
|
|
243
243
|
})
|
|
244
244
|
|
|
245
245
|
return {
|
|
246
|
-
amount,
|
|
247
|
-
change,
|
|
248
|
-
totalAmount,
|
|
249
|
-
address: processedAddress,
|
|
250
|
-
ourAddress,
|
|
251
|
-
receiveAddress,
|
|
252
|
-
sendAmount,
|
|
253
|
-
fee,
|
|
254
|
-
usableUtxos,
|
|
255
|
-
selectedUtxos,
|
|
256
|
-
replaceTx,
|
|
257
|
-
sendOutput,
|
|
258
|
-
changeOutput,
|
|
259
246
|
unsignedTx,
|
|
247
|
+
fee,
|
|
248
|
+
metadata: {
|
|
249
|
+
amount,
|
|
250
|
+
change,
|
|
251
|
+
totalAmount,
|
|
252
|
+
address: processedAddress,
|
|
253
|
+
ourAddress,
|
|
254
|
+
receiveAddress,
|
|
255
|
+
sendAmount,
|
|
256
|
+
usableUtxos,
|
|
257
|
+
selectedUtxos,
|
|
258
|
+
replaceTx,
|
|
259
|
+
sendOutput,
|
|
260
|
+
changeOutput,
|
|
261
|
+
},
|
|
260
262
|
}
|
|
261
263
|
},
|
|
262
264
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { retry } from '@exodus/simple-retry'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Broadcast a signed transaction to the Bitcoin network with retry logic
|
|
5
|
+
* @param {Object} params
|
|
6
|
+
* @param {Object} params.asset - The asset object
|
|
7
|
+
* @param {Buffer} params.rawTx - The raw signed transaction
|
|
8
|
+
* @returns {Promise<void>}
|
|
9
|
+
* @throws {Error} With finalError=true for non-retryable errors
|
|
10
|
+
*/
|
|
11
|
+
export async function broadcastTransaction({ asset, rawTx }) {
|
|
12
|
+
const broadcastTxWithRetry = retry(
|
|
13
|
+
async (rawTxHex) => {
|
|
14
|
+
try {
|
|
15
|
+
return await asset.api.broadcastTx(rawTxHex)
|
|
16
|
+
} catch (e) {
|
|
17
|
+
// Mark certain errors as final (non-retryable)
|
|
18
|
+
if (
|
|
19
|
+
/missing inputs/i.test(e.message) ||
|
|
20
|
+
/absurdly-high-fee/.test(e.message) ||
|
|
21
|
+
/too-long-mempool-chain/.test(e.message) ||
|
|
22
|
+
/txn-mempool-conflict/.test(e.message) ||
|
|
23
|
+
/tx-size/.test(e.message) ||
|
|
24
|
+
/txn-already-in-mempool/.test(e.message)
|
|
25
|
+
) {
|
|
26
|
+
e.finalError = true
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
throw e
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{ delayTimesMs: ['10s'] }
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
const rawTxHex = rawTx.toString('hex')
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
await broadcastTxWithRetry(rawTxHex)
|
|
39
|
+
} catch (err) {
|
|
40
|
+
if (err.message.includes('txn-already-in-mempool')) {
|
|
41
|
+
// Not an error, transaction is already broadcast
|
|
42
|
+
console.log('Transaction is already in the mempool.')
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
throw err
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/tx-send/index.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import * as defaultBitcoinjsLib from '@exodus/bitcoinjs'
|
|
2
|
-
import { Address } from '@exodus/models'
|
|
3
|
-
import { retry } from '@exodus/simple-retry'
|
|
4
2
|
import assert from 'minimalistic-assert'
|
|
5
3
|
|
|
6
|
-
import { serializeCurrency } from '../fee/fee-utils.js'
|
|
7
4
|
import { createTxFactory } from '../tx-create/create-tx.js'
|
|
8
5
|
import { getBlockHeight } from '../tx-create/tx-create-utils.js'
|
|
6
|
+
import { broadcastTransaction } from './broadcast-tx.js'
|
|
7
|
+
import { updateAccountState, updateTransactionLog } from './update-state.js'
|
|
9
8
|
|
|
10
9
|
const getSize = (tx) => {
|
|
11
10
|
if (typeof tx.size === 'number') return tx.size
|
|
@@ -172,23 +171,13 @@ export const createAndBroadcastTXFactory =
|
|
|
172
171
|
utxosDescendingOrder,
|
|
173
172
|
walletAccount,
|
|
174
173
|
})
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
receiveAddress,
|
|
180
|
-
sendAmount,
|
|
181
|
-
fee,
|
|
182
|
-
usableUtxos,
|
|
183
|
-
selectedUtxos,
|
|
184
|
-
replaceTx,
|
|
185
|
-
sendOutput,
|
|
186
|
-
changeOutput,
|
|
187
|
-
unsignedTx,
|
|
188
|
-
} = transactionDescriptor
|
|
174
|
+
|
|
175
|
+
const { unsignedTx, fee, metadata } = transactionDescriptor
|
|
176
|
+
const { sendAmount, usableUtxos, replaceTx, sendOutput, changeOutput } = metadata
|
|
177
|
+
|
|
189
178
|
const outputs = unsignedTx.txData.outputs
|
|
190
179
|
|
|
191
|
-
address =
|
|
180
|
+
address = metadata.address
|
|
192
181
|
|
|
193
182
|
// Sign transaction
|
|
194
183
|
const { rawTx, txId, tx } = await signTransaction({
|
|
@@ -199,44 +188,18 @@ export const createAndBroadcastTXFactory =
|
|
|
199
188
|
})
|
|
200
189
|
|
|
201
190
|
// Broadcast transaction
|
|
202
|
-
const broadcastTxWithRetry = retry(
|
|
203
|
-
async (rawTx) => {
|
|
204
|
-
try {
|
|
205
|
-
return await asset.api.broadcastTx(rawTx)
|
|
206
|
-
} catch (e) {
|
|
207
|
-
if (
|
|
208
|
-
/missing inputs/i.test(e.message) ||
|
|
209
|
-
/absurdly-high-fee/.test(e.message) ||
|
|
210
|
-
/too-long-mempool-chain/.test(e.message) ||
|
|
211
|
-
/txn-mempool-conflict/.test(e.message) ||
|
|
212
|
-
/tx-size/.test(e.message) ||
|
|
213
|
-
/txn-already-in-mempool/.test(e.message)
|
|
214
|
-
) {
|
|
215
|
-
e.finalError = true
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
throw e
|
|
219
|
-
}
|
|
220
|
-
},
|
|
221
|
-
{ delayTimesMs: ['10s'] }
|
|
222
|
-
)
|
|
223
|
-
|
|
224
191
|
try {
|
|
225
|
-
await
|
|
192
|
+
await broadcastTransaction({ asset, rawTx })
|
|
226
193
|
} catch (err) {
|
|
227
|
-
if (err.message
|
|
228
|
-
// It's not an error, we must ignore it.
|
|
229
|
-
console.log('Transaction is already in the mempool.')
|
|
230
|
-
} else if (/insight broadcast http error.*missing inputs/i.test(err.message)) {
|
|
194
|
+
if (/insight broadcast http error.*missing inputs/i.test(err.message)) {
|
|
231
195
|
err.txInfo = JSON.stringify({
|
|
232
196
|
amount: sendAmount.toDefaultString({ unit: true }),
|
|
233
|
-
fee: ((fee && fee.toDefaultString({ unit: true })) || 0).toString(),
|
|
197
|
+
fee: ((fee && fee.toDefaultString({ unit: true })) || 0).toString(),
|
|
234
198
|
allUtxos: usableUtxos.toJSON(),
|
|
235
199
|
})
|
|
236
|
-
throw err
|
|
237
|
-
} else {
|
|
238
|
-
throw err
|
|
239
200
|
}
|
|
201
|
+
|
|
202
|
+
throw err
|
|
240
203
|
}
|
|
241
204
|
|
|
242
205
|
function findUtxoIndex(output) {
|
|
@@ -256,114 +219,35 @@ export const createAndBroadcastTXFactory =
|
|
|
256
219
|
const changeUtxoIndex = findUtxoIndex(changeOutput)
|
|
257
220
|
const sendUtxoIndex = findUtxoIndex(sendOutput)
|
|
258
221
|
|
|
259
|
-
const {
|
|
260
|
-
|
|
261
|
-
const knownBalanceUtxoIds = accountState.knownBalanceUtxoIds || []
|
|
262
|
-
let remainingUtxos = usableUtxos.difference(selectedUtxos)
|
|
263
|
-
if (changeUtxoIndex !== -1) {
|
|
264
|
-
const address = Address.create(ourAddress.address, ourAddress.meta)
|
|
265
|
-
const changeUtxo = {
|
|
266
|
-
txId,
|
|
267
|
-
address,
|
|
268
|
-
vout: changeUtxoIndex,
|
|
269
|
-
script,
|
|
270
|
-
value: change,
|
|
271
|
-
confirmations: 0,
|
|
272
|
-
rbfEnabled,
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
knownBalanceUtxoIds.push(`${changeUtxo.txId}:${changeUtxo.vout}`.toLowerCase())
|
|
276
|
-
remainingUtxos = remainingUtxos.addUtxo(changeUtxo)
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
if (replaceTx) {
|
|
280
|
-
remainingUtxos = remainingUtxos.difference(remainingUtxos.getTxIdUtxos(replaceTx.txId))
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
await assetClientInterface.updateAccountState({
|
|
284
|
-
assetName,
|
|
285
|
-
walletAccount,
|
|
286
|
-
newData: {
|
|
287
|
-
utxos: remainingUtxos,
|
|
288
|
-
knownBalanceUtxoIds,
|
|
289
|
-
},
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
const config = await assetClientInterface.getAssetConfig?.({
|
|
222
|
+
const { size } = await updateAccountState({
|
|
223
|
+
assetClientInterface,
|
|
293
224
|
assetName,
|
|
294
225
|
walletAccount,
|
|
226
|
+
accountState,
|
|
227
|
+
txId,
|
|
228
|
+
metadata,
|
|
229
|
+
tx,
|
|
230
|
+
rawTx,
|
|
231
|
+
changeUtxoIndex,
|
|
232
|
+
getSizeAndChangeScript,
|
|
233
|
+
rbfEnabled,
|
|
295
234
|
})
|
|
296
|
-
const walletAddressObjects = await assetClientInterface.getReceiveAddresses({
|
|
297
|
-
walletAccount,
|
|
298
|
-
assetName,
|
|
299
|
-
multiAddressMode: config?.multiAddressMode ?? true,
|
|
300
|
-
})
|
|
301
|
-
// There are two cases of bumping, replacing or chaining a self-send.
|
|
302
|
-
// If we have a bumpTxId, but we aren't replacing, then it is a self-send.
|
|
303
|
-
const selfSend = bumpTxId
|
|
304
|
-
? !replaceTx
|
|
305
|
-
: walletAddressObjects.some((receiveAddress) => String(receiveAddress) === String(address))
|
|
306
|
-
|
|
307
|
-
const displayReceiveAddress = asset.address.displayAddress?.(receiveAddress) || receiveAddress
|
|
308
|
-
|
|
309
|
-
const receivers = bumpTxId
|
|
310
|
-
? replaceTx
|
|
311
|
-
? replaceTx.data.sent
|
|
312
|
-
: []
|
|
313
|
-
: replaceTx
|
|
314
|
-
? [
|
|
315
|
-
...replaceTx.data.sent,
|
|
316
|
-
{ address: displayReceiveAddress, amount: serializeCurrency(amount, asset.currency) },
|
|
317
|
-
]
|
|
318
|
-
: [{ address: displayReceiveAddress, amount: serializeCurrency(amount, asset.currency) }]
|
|
319
|
-
|
|
320
|
-
const calculateCoinAmount = () => {
|
|
321
|
-
if (selfSend) {
|
|
322
|
-
return asset.currency.ZERO
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
return totalAmount.abs().negate()
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const coinAmount = calculateCoinAmount()
|
|
329
235
|
|
|
330
|
-
await
|
|
331
|
-
|
|
236
|
+
await updateTransactionLog({
|
|
237
|
+
asset,
|
|
238
|
+
assetClientInterface,
|
|
332
239
|
walletAccount,
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
data: {
|
|
343
|
-
sent: selfSend ? [] : receivers,
|
|
344
|
-
rbfEnabled,
|
|
345
|
-
feePerKB: size ? fee.div(size / 1000).toBaseNumber() : undefined,
|
|
346
|
-
changeAddress: changeOutput ? ourAddress : undefined,
|
|
347
|
-
blockHeight,
|
|
348
|
-
blocksSeen: 0,
|
|
349
|
-
inputs: selectedUtxos.toJSON(),
|
|
350
|
-
replacedTxId: replaceTx ? replaceTx.txId : undefined,
|
|
351
|
-
},
|
|
352
|
-
},
|
|
353
|
-
],
|
|
240
|
+
txId,
|
|
241
|
+
fee,
|
|
242
|
+
metadata,
|
|
243
|
+
address,
|
|
244
|
+
amount,
|
|
245
|
+
bumpTxId,
|
|
246
|
+
size,
|
|
247
|
+
blockHeight,
|
|
248
|
+
rbfEnabled,
|
|
354
249
|
})
|
|
355
250
|
|
|
356
|
-
// If we are replacing the tx, add the replacedBy info to the previous tx to update UI
|
|
357
|
-
// Also, clone the personal note and attach it to the new tx so it is not lost
|
|
358
|
-
if (replaceTx) {
|
|
359
|
-
replaceTx.data.replacedBy = txId
|
|
360
|
-
await assetClientInterface.updateTxLogAndNotify({
|
|
361
|
-
assetName,
|
|
362
|
-
walletAccount,
|
|
363
|
-
txs: [replaceTx],
|
|
364
|
-
})
|
|
365
|
-
}
|
|
366
|
-
|
|
367
251
|
return {
|
|
368
252
|
txId,
|
|
369
253
|
sendUtxoIndex,
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { Address } from '@exodus/models'
|
|
2
|
+
|
|
3
|
+
import { serializeCurrency } from '../fee/fee-utils.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Update account state after transaction is broadcast
|
|
7
|
+
* @param {Object} params
|
|
8
|
+
* @param {Object} params.assetClientInterface - Asset client interface
|
|
9
|
+
* @param {string} params.assetName - Name of the asset
|
|
10
|
+
* @param {Object} params.walletAccount - Wallet account
|
|
11
|
+
* @param {Object} params.accountState - Current account state
|
|
12
|
+
* @param {string} params.txId - Transaction ID
|
|
13
|
+
* @param {Object} params.metadata - Transaction metadata
|
|
14
|
+
* @param {Object} params.tx - Signed transaction object
|
|
15
|
+
* @param {Buffer} params.rawTx - Raw transaction
|
|
16
|
+
* @param {number} params.changeUtxoIndex - Index of change output
|
|
17
|
+
* @param {Object} params.changeOutput - Change output details
|
|
18
|
+
* @param {Object} params.getSizeAndChangeScript - Function to get size and script
|
|
19
|
+
* @param {boolean} params.rbfEnabled - Whether RBF is enabled
|
|
20
|
+
*/
|
|
21
|
+
export async function updateAccountState({
|
|
22
|
+
assetClientInterface,
|
|
23
|
+
assetName,
|
|
24
|
+
walletAccount,
|
|
25
|
+
accountState,
|
|
26
|
+
txId,
|
|
27
|
+
metadata,
|
|
28
|
+
tx,
|
|
29
|
+
rawTx,
|
|
30
|
+
changeUtxoIndex,
|
|
31
|
+
getSizeAndChangeScript,
|
|
32
|
+
rbfEnabled,
|
|
33
|
+
}) {
|
|
34
|
+
const { usableUtxos, selectedUtxos, replaceTx, change, ourAddress } = metadata
|
|
35
|
+
|
|
36
|
+
// Get change script and size
|
|
37
|
+
const { script, size } = getSizeAndChangeScript({
|
|
38
|
+
assetName,
|
|
39
|
+
tx,
|
|
40
|
+
rawTx,
|
|
41
|
+
changeUtxoIndex,
|
|
42
|
+
txId,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// Update remaining UTXOs
|
|
46
|
+
const knownBalanceUtxoIds = accountState.knownBalanceUtxoIds || []
|
|
47
|
+
let remainingUtxos = usableUtxos.difference(selectedUtxos)
|
|
48
|
+
|
|
49
|
+
// Add change UTXO if present
|
|
50
|
+
if (changeUtxoIndex !== -1 && ourAddress) {
|
|
51
|
+
const address = Address.create(ourAddress.address || ourAddress, ourAddress.meta || {})
|
|
52
|
+
const changeUtxo = {
|
|
53
|
+
txId,
|
|
54
|
+
address,
|
|
55
|
+
vout: changeUtxoIndex,
|
|
56
|
+
script,
|
|
57
|
+
value: change,
|
|
58
|
+
confirmations: 0,
|
|
59
|
+
rbfEnabled,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
knownBalanceUtxoIds.push(`${changeUtxo.txId}:${changeUtxo.vout}`.toLowerCase())
|
|
63
|
+
remainingUtxos = remainingUtxos.addUtxo(changeUtxo)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Remove replaced transaction UTXOs if present
|
|
67
|
+
if (replaceTx) {
|
|
68
|
+
remainingUtxos = remainingUtxos.difference(remainingUtxos.getTxIdUtxos(replaceTx.txId))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Update account state
|
|
72
|
+
await assetClientInterface.updateAccountState({
|
|
73
|
+
assetName,
|
|
74
|
+
walletAccount,
|
|
75
|
+
newData: {
|
|
76
|
+
utxos: remainingUtxos,
|
|
77
|
+
knownBalanceUtxoIds,
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
return { size }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Update transaction log with new transaction
|
|
86
|
+
* @param {Object} params
|
|
87
|
+
* @param {Object} params.asset - Asset object
|
|
88
|
+
* @param {Object} params.assetClientInterface - Asset client interface
|
|
89
|
+
* @param {Object} params.walletAccount - Wallet account
|
|
90
|
+
* @param {string} params.txId - Transaction ID
|
|
91
|
+
* @param {Object} params.fee - Transaction fee
|
|
92
|
+
* @param {Object} params.metadata - Transaction metadata
|
|
93
|
+
* @param {string} params.address - Recipient address
|
|
94
|
+
* @param {Object} params.amount - Transaction amount (for regular sends)
|
|
95
|
+
* @param {string} params.bumpTxId - ID of transaction being bumped (if applicable)
|
|
96
|
+
* @param {number} params.size - Transaction size
|
|
97
|
+
* @param {number} params.blockHeight - Block height
|
|
98
|
+
* @param {boolean} params.rbfEnabled - Whether RBF is enabled
|
|
99
|
+
*/
|
|
100
|
+
export async function updateTransactionLog({
|
|
101
|
+
asset,
|
|
102
|
+
assetClientInterface,
|
|
103
|
+
walletAccount,
|
|
104
|
+
txId,
|
|
105
|
+
fee,
|
|
106
|
+
metadata,
|
|
107
|
+
address,
|
|
108
|
+
amount,
|
|
109
|
+
bumpTxId,
|
|
110
|
+
size,
|
|
111
|
+
blockHeight,
|
|
112
|
+
rbfEnabled,
|
|
113
|
+
}) {
|
|
114
|
+
const { totalAmount, selectedUtxos, replaceTx, changeOutput, ourAddress } = metadata
|
|
115
|
+
const assetName = asset.name
|
|
116
|
+
|
|
117
|
+
// Check if this is a self-send
|
|
118
|
+
const config = await assetClientInterface.getAssetConfig?.({
|
|
119
|
+
assetName,
|
|
120
|
+
walletAccount,
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
const walletAddressObjects = await assetClientInterface.getReceiveAddresses({
|
|
124
|
+
walletAccount,
|
|
125
|
+
assetName,
|
|
126
|
+
multiAddressMode: config?.multiAddressMode ?? true,
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
// There are two cases of bumping, replacing or chaining a self-send.
|
|
130
|
+
// If we have a bumpTxId, but we aren't replacing, then it is a self-send.
|
|
131
|
+
const selfSend = bumpTxId
|
|
132
|
+
? !replaceTx
|
|
133
|
+
: walletAddressObjects.some((receiveAddress) => String(receiveAddress) === String(address))
|
|
134
|
+
|
|
135
|
+
const displayReceiveAddress = asset.address.displayAddress?.(address) || address
|
|
136
|
+
|
|
137
|
+
// Build receivers list
|
|
138
|
+
const receivers = bumpTxId
|
|
139
|
+
? replaceTx
|
|
140
|
+
? replaceTx.data.sent
|
|
141
|
+
: []
|
|
142
|
+
: replaceTx
|
|
143
|
+
? [
|
|
144
|
+
...replaceTx.data.sent,
|
|
145
|
+
{ address: displayReceiveAddress, amount: serializeCurrency(amount, asset.currency) },
|
|
146
|
+
]
|
|
147
|
+
: [{ address: displayReceiveAddress, amount: serializeCurrency(amount, asset.currency) }]
|
|
148
|
+
|
|
149
|
+
// Calculate coin amount
|
|
150
|
+
const coinAmount = selfSend ? asset.currency.ZERO : totalAmount.abs().negate()
|
|
151
|
+
|
|
152
|
+
// Update transaction log
|
|
153
|
+
await assetClientInterface.updateTxLogAndNotify({
|
|
154
|
+
assetName,
|
|
155
|
+
walletAccount,
|
|
156
|
+
txs: [
|
|
157
|
+
{
|
|
158
|
+
txId,
|
|
159
|
+
confirmations: 0,
|
|
160
|
+
coinAmount,
|
|
161
|
+
coinName: asset.name,
|
|
162
|
+
feeAmount: fee,
|
|
163
|
+
feeCoinName: assetName,
|
|
164
|
+
selfSend,
|
|
165
|
+
data: {
|
|
166
|
+
sent: selfSend ? [] : receivers,
|
|
167
|
+
rbfEnabled,
|
|
168
|
+
feePerKB: size ? fee.div(size / 1000).toBaseNumber() : undefined,
|
|
169
|
+
changeAddress: changeOutput ? ourAddress : undefined,
|
|
170
|
+
blockHeight,
|
|
171
|
+
blocksSeen: 0,
|
|
172
|
+
inputs: selectedUtxos.toJSON(),
|
|
173
|
+
replacedTxId: replaceTx ? replaceTx.txId : undefined,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
// If replacing a transaction, update the old one
|
|
180
|
+
if (replaceTx) {
|
|
181
|
+
replaceTx.data.replacedBy = txId
|
|
182
|
+
await assetClientInterface.updateTxLogAndNotify({
|
|
183
|
+
assetName,
|
|
184
|
+
walletAccount,
|
|
185
|
+
txs: [replaceTx],
|
|
186
|
+
})
|
|
187
|
+
}
|
|
188
|
+
}
|