@exodus/bitcoin-api 2.2.1 → 2.3.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/package.json +2 -3
- package/src/balances.js +26 -0
- package/src/fee/get-fee-resolver.js +6 -8
- package/src/fee/utxo-selector.js +6 -3
- package/src/index.js +1 -0
- package/src/move-funds.js +31 -33
- package/src/tx-send/index.js +1 -1
- package/src/tx-sign/default-create-tx.js +2 -1
- package/src/utxos-utils.js +0 -36
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/bitcoin-api",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Exodus bitcoin-api",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
"access": "restricted"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"build": "babel --root-mode upward --delete-dir-on-start --ignore '**/__tests__/**' src --out-dir lib",
|
|
17
16
|
"test": "jest",
|
|
18
17
|
"lint": "eslint ./src",
|
|
19
18
|
"lint:fix": "yarn lint --fix"
|
|
@@ -41,5 +40,5 @@
|
|
|
41
40
|
"@exodus/bitcoin-meta": "^1.0.0",
|
|
42
41
|
"@noble/secp256k1": "~1.5.3"
|
|
43
42
|
},
|
|
44
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "e2dd97caae4a23757f38f2156450f17d86fdce34"
|
|
45
44
|
}
|
package/src/balances.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import assert from 'minimalistic-assert'
|
|
2
|
+
import { getUtxos } from './utxos-utils'
|
|
3
|
+
|
|
4
|
+
// known issue!! fee data is static here!
|
|
5
|
+
// Hydra's balances's module does not provide it when calling asset.api.getBalances
|
|
6
|
+
// https://github.com/ExodusMovement/exodus-hydra/blob/f9110f8e9e76b8b199bc4d40461cb1bed3a5be1e/modules/balances/module/index.js#L130
|
|
7
|
+
// feeData is required to know if an unconfirmed tx can or cannot be RBFed?
|
|
8
|
+
// This could be fixed once we allow all unconfirmed utxos to be RBFed or if we change the balance module to provide the feeData when calling asset.api.getBalances
|
|
9
|
+
export const getBalancesFactory = ({ feeData, getSpendableBalance }) => {
|
|
10
|
+
assert(feeData, 'feeData is required')
|
|
11
|
+
assert(getSpendableBalance, 'getSpendableBalance is required')
|
|
12
|
+
return ({ asset, accountState, txLog }) => {
|
|
13
|
+
assert(asset, 'asset is required')
|
|
14
|
+
assert(accountState, 'accountState is required')
|
|
15
|
+
assert(txLog, 'txLog is required')
|
|
16
|
+
const utxos = getUtxos({ asset, accountState })
|
|
17
|
+
const balance = utxos.value
|
|
18
|
+
const spendableBalance = getSpendableBalance({
|
|
19
|
+
asset,
|
|
20
|
+
accountState,
|
|
21
|
+
txSet: txLog,
|
|
22
|
+
feeData,
|
|
23
|
+
})
|
|
24
|
+
return { balance, spendableBalance }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'minimalistic-assert'
|
|
2
2
|
import { getUtxosData } from './utxo-selector'
|
|
3
3
|
import { findUnconfirmedSentRbfTxs } from '../tx-utils'
|
|
4
|
-
import {
|
|
4
|
+
import { getUsableUtxos, getUtxos } from '../utxos-utils'
|
|
5
5
|
import { canBumpTx } from './can-bump-tx'
|
|
6
6
|
|
|
7
7
|
export class GetFeeResolver {
|
|
@@ -40,15 +40,13 @@ export class GetFeeResolver {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
getSpendableBalance = ({ asset, accountState, txSet, feeData }) => {
|
|
43
|
-
|
|
44
|
-
const spendableUtxos = getSpendableUtxos({
|
|
43
|
+
return this.#getUtxosData({
|
|
45
44
|
asset,
|
|
46
|
-
|
|
47
|
-
feeData,
|
|
45
|
+
accountState,
|
|
48
46
|
txSet,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
feeData,
|
|
48
|
+
isSendAll: true,
|
|
49
|
+
}).spendableBalance
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
#getUtxosData = ({ asset, accountState, txSet, feeData, amount, customFee, isSendAll }) => {
|
package/src/fee/utxo-selector.js
CHANGED
|
@@ -209,12 +209,15 @@ export const getUtxosData = ({
|
|
|
209
209
|
})
|
|
210
210
|
|
|
211
211
|
const resolvedFee = replaceTx ? fee.sub(replaceTx.feeAmount) : fee
|
|
212
|
-
|
|
213
|
-
const
|
|
212
|
+
const empty = UtxoCollection.createEmpty({ currency: asset.currency })
|
|
213
|
+
const spendableUtxos = getConfirmedOrRfbDisabledUtxos({
|
|
214
214
|
asset,
|
|
215
215
|
utxos: usableUtxos,
|
|
216
216
|
allowUnconfirmedRbfEnabledUtxos,
|
|
217
|
-
}).
|
|
217
|
+
}).union(replaceTx ? usableUtxos.getTxIdUtxos(replaceTx.txId) : empty)
|
|
218
|
+
// .union(selectedUtxos || empty)
|
|
219
|
+
|
|
220
|
+
const spendableBalance = spendableUtxos.value
|
|
218
221
|
|
|
219
222
|
const extraFee = selectedUtxos
|
|
220
223
|
? asset.currency.baseUnit(getExtraFee({ asset, inputs: selectedUtxos, feePerKB: feeRate }))
|
package/src/index.js
CHANGED
package/src/move-funds.js
CHANGED
|
@@ -2,21 +2,45 @@ import wif from 'wif'
|
|
|
2
2
|
import { UtxoCollection, Address } from '@exodus/models'
|
|
3
3
|
import { createInputs, createOutput, getNonWitnessTxs } from './tx-send'
|
|
4
4
|
import assert from 'minimalistic-assert'
|
|
5
|
+
import secp256k1 from 'secp256k1'
|
|
6
|
+
|
|
7
|
+
function wifToPublicKey({ coinInfo, privateKeyWIF }) {
|
|
8
|
+
assert(coinInfo, 'coinInfo is required')
|
|
9
|
+
assert(privateKeyWIF, 'privateKeyWIF is required')
|
|
10
|
+
const { versions } = coinInfo
|
|
11
|
+
|
|
12
|
+
const { privateKey, compressed } = wif.decode(privateKeyWIF, versions.private)
|
|
13
|
+
return secp256k1.publicKeyCreate(privateKey, compressed)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const getAddressesFromPrivateKeyFactory = ({ purposes, keys, coinInfo }) => {
|
|
17
|
+
assert(purposes, 'purposes is required')
|
|
18
|
+
assert(keys, 'keys is required')
|
|
19
|
+
assert(coinInfo, 'coinInfo is required')
|
|
20
|
+
return ({ privateKey }) => {
|
|
21
|
+
const publicKey = wifToPublicKey({ coinInfo, privateKeyWIF: privateKey })
|
|
22
|
+
return purposes.map((purpose) =>
|
|
23
|
+
keys.encodePublic(purpose === 49 ? secp256k1.publicKeyConvert(publicKey, true) : publicKey, {
|
|
24
|
+
purpose,
|
|
25
|
+
})
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
5
29
|
|
|
6
30
|
export const moveFundsFactory = ({
|
|
7
31
|
asset,
|
|
8
32
|
insightClient,
|
|
9
33
|
getFeeEstimator,
|
|
10
|
-
keys,
|
|
11
34
|
signTx,
|
|
12
35
|
address,
|
|
36
|
+
getAddressesFromPrivateKey,
|
|
13
37
|
}) => {
|
|
14
38
|
assert(asset, 'asset is required')
|
|
15
39
|
assert(insightClient, 'insightClient is required')
|
|
16
40
|
assert(getFeeEstimator, 'getFeeEstimator is required')
|
|
17
41
|
assert(address, 'address is required')
|
|
18
|
-
assert(keys, 'keys is required')
|
|
19
42
|
assert(signTx, 'signTx is required')
|
|
43
|
+
assert(getAddressesFromPrivateKey, 'getAddressesFromPrivateKey is required')
|
|
20
44
|
async function prepareFunds(assetName, input, options = {}) {
|
|
21
45
|
const { toAddress, assetClientInterface, MoveFundsError, walletAccount } = options
|
|
22
46
|
assert(MoveFundsError, 'MoveFundsError is required') // should we move MoveFundsError to asset libs?
|
|
@@ -29,6 +53,7 @@ export const moveFundsFactory = ({
|
|
|
29
53
|
asset,
|
|
30
54
|
input,
|
|
31
55
|
}
|
|
56
|
+
// WIF format private key
|
|
32
57
|
const privateKey = input
|
|
33
58
|
|
|
34
59
|
if (!isValidPrivateKey(privateKey)) {
|
|
@@ -36,7 +61,7 @@ export const moveFundsFactory = ({
|
|
|
36
61
|
}
|
|
37
62
|
|
|
38
63
|
const { compressed } = wif.decode(privateKey)
|
|
39
|
-
const addresses =
|
|
64
|
+
const addresses = getAddressesFromPrivateKey({ privateKey })
|
|
40
65
|
|
|
41
66
|
const receiveAddresses = await assetClientInterface.getReceiveAddresses({
|
|
42
67
|
walletAccount,
|
|
@@ -61,7 +86,9 @@ export const moveFundsFactory = ({
|
|
|
61
86
|
}
|
|
62
87
|
}
|
|
63
88
|
if (!found) {
|
|
64
|
-
formatProps.fromAddress = addresses.join(' or
|
|
89
|
+
formatProps.fromAddress = `${addresses.slice(0, -1).join(', ')}, or ${
|
|
90
|
+
addresses[addresses.length - 1]
|
|
91
|
+
}`
|
|
65
92
|
throw new MoveFundsError('balance-zero', formatProps)
|
|
66
93
|
}
|
|
67
94
|
const fromAddress = address
|
|
@@ -114,35 +141,6 @@ export const moveFundsFactory = ({
|
|
|
114
141
|
return { txId, fromAddress, toAddress, amount, fee }
|
|
115
142
|
}
|
|
116
143
|
|
|
117
|
-
function getAllAddressesFromWIF(privateKeyWIF) {
|
|
118
|
-
const { compressed } = wif.decode(privateKeyWIF)
|
|
119
|
-
const legacyAddress = keys.encodePublicFromWIF(privateKeyWIF)
|
|
120
|
-
// TODO: support nested segwit address, right now we don't support send
|
|
121
|
-
// const nestedSegwitAddress = encodeNestedSegwitFromWIF(privateKeyWIF, { asset })
|
|
122
|
-
const nativeSegwitAddress = keys.encodePublicBech32FromWIF(privateKeyWIF)
|
|
123
|
-
|
|
124
|
-
if (compressed) {
|
|
125
|
-
return [nativeSegwitAddress, legacyAddress]
|
|
126
|
-
} else {
|
|
127
|
-
return [legacyAddress]
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/*
|
|
132
|
-
function getPublicFromWIF(privateKeyWIF, { asset }) {
|
|
133
|
-
const { versions } = asset.coinInfo
|
|
134
|
-
const { privateKey, compressed } = wif.decode(privateKeyWIF, versions.private)
|
|
135
|
-
return secp256k1.pointFromScalar(privateKey, compressed)
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function encodeNestedSegwitFromWIF(privateKeyWIF, { asset }) {
|
|
139
|
-
const publicKey = getPublicFromWIF(privateKeyWIF, { asset })
|
|
140
|
-
const witnessProgram = bitcoinjs.payments.p2wpkh({ pubkey: publicKey }).output
|
|
141
|
-
const witnessProgramHash = bitcoinjs.crypto.hash160(witnessProgram)
|
|
142
|
-
return bitcoinjs.address.toBase58Check(witnessProgramHash, asset.coinInfo.versions.scripthash)
|
|
143
|
-
}
|
|
144
|
-
*/
|
|
145
|
-
|
|
146
144
|
async function getUtxos({ asset, address }) {
|
|
147
145
|
const rawUtxos = await insightClient.fetchUTXOs([address])
|
|
148
146
|
return UtxoCollection.fromArray(
|
package/src/tx-send/index.js
CHANGED
|
@@ -363,7 +363,7 @@ export const createAndBroadcastTXFactory = ({
|
|
|
363
363
|
sent: selfSend ? [] : receivers,
|
|
364
364
|
rbfEnabled,
|
|
365
365
|
feePerKB: ['bitcoin', 'bitcoinregtest', 'bitcointestnet'].includes(assetName)
|
|
366
|
-
? fee.div(tx.virtualSize
|
|
366
|
+
? fee.div(tx.virtualSize / 1000).toBaseNumber()
|
|
367
367
|
: undefined,
|
|
368
368
|
changeAddress: changeOutput ? ourAddress : undefined,
|
|
369
369
|
blockHeight,
|
|
@@ -115,6 +115,7 @@ export const signTxFactory = ({ assetName, resolvePurpose, keys, coinInfo, netwo
|
|
|
115
115
|
const rawTx = tx.toBuffer()
|
|
116
116
|
const txId = tx.getId()
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
// tx needs to be serializable for desktop RPC send => sign communication
|
|
119
|
+
return { rawTx, txId, tx: { ...tx, virtualSize: tx.virtualSize?.() } }
|
|
119
120
|
}
|
|
120
121
|
}
|
package/src/utxos-utils.js
CHANGED
|
@@ -12,26 +12,6 @@ export function getUtxos({ accountState, asset }) {
|
|
|
12
12
|
)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export const getBalancesFactory = ({ feeData, allowUnconfirmedRbfEnabledUtxos }) => {
|
|
16
|
-
assert(feeData, 'feeData is required')
|
|
17
|
-
return ({ asset, accountState, txLog }) => {
|
|
18
|
-
assert(asset, 'asset is required')
|
|
19
|
-
assert(accountState, 'accountState is required')
|
|
20
|
-
assert(txLog, 'txLog is required')
|
|
21
|
-
const utxos = getUtxos({ asset, accountState })
|
|
22
|
-
const balance = utxos.value
|
|
23
|
-
const spendableBalance = getSpendableUtxos({
|
|
24
|
-
asset,
|
|
25
|
-
utxos,
|
|
26
|
-
txSet: txLog,
|
|
27
|
-
feeData,
|
|
28
|
-
|
|
29
|
-
allowUnconfirmedRbfEnabledUtxos,
|
|
30
|
-
}).value
|
|
31
|
-
return { balance, spendableBalance }
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
15
|
export function getConfirmedUtxos({ asset, utxos }) {
|
|
36
16
|
assert(asset, 'asset is required')
|
|
37
17
|
assert(utxos, 'utxos is required')
|
|
@@ -83,19 +63,3 @@ export function getUsableUtxos({ asset, utxos, feeData, txSet }) {
|
|
|
83
63
|
{ currency: asset.currency }
|
|
84
64
|
)
|
|
85
65
|
}
|
|
86
|
-
|
|
87
|
-
export function getSpendableUtxos({
|
|
88
|
-
asset,
|
|
89
|
-
utxos,
|
|
90
|
-
feeData,
|
|
91
|
-
txSet,
|
|
92
|
-
|
|
93
|
-
allowUnconfirmedRbfEnabledUtxos,
|
|
94
|
-
}) {
|
|
95
|
-
const usableUtxos = getUsableUtxos({ asset, utxos, feeData, txSet })
|
|
96
|
-
return getConfirmedOrRfbDisabledUtxos({
|
|
97
|
-
asset,
|
|
98
|
-
utxos: usableUtxos,
|
|
99
|
-
allowUnconfirmedRbfEnabledUtxos,
|
|
100
|
-
})
|
|
101
|
-
}
|