@exodus/bitcoin-api 4.0.2 → 4.0.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
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.0.4](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.0.3...@exodus/bitcoin-api@4.0.4) (2025-09-16)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
* fix: mixed input transactions incorrectly classified as receive (#6472)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## [4.0.3](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.0.2...@exodus/bitcoin-api@4.0.3) (2025-09-05)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
* fix: update bip32 to ^4.1.0 (#6406)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
6
26
|
## [4.0.2](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@4.0.1...@exodus/bitcoin-api@4.0.2) (2025-09-01)
|
|
7
27
|
|
|
8
28
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/bitcoin-api",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.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",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@exodus/asset-lib": "^5.0.0",
|
|
24
24
|
"@exodus/basic-utils": "^3.0.1",
|
|
25
|
-
"@exodus/bip32": "^
|
|
25
|
+
"@exodus/bip32": "^4.1.0",
|
|
26
26
|
"@exodus/bip322-js": "^2.1.0",
|
|
27
27
|
"@exodus/bip44-constants": "^195.0.0",
|
|
28
28
|
"@exodus/bitcoin-lib": "^2.4.3",
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"type": "git",
|
|
60
60
|
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "1e736fa10b97d17bfa72bdbac09b57dc64b34d44"
|
|
63
63
|
}
|
package/src/sign-message.js
CHANGED
|
@@ -5,7 +5,7 @@ export const signMessage = async ({ privateKey, message: _message }) => {
|
|
|
5
5
|
assert(_message?.bip322Message, `expected bip322Message`)
|
|
6
6
|
|
|
7
7
|
const { address, message } = _message.bip322Message
|
|
8
|
-
const signedMessage = Signer.
|
|
8
|
+
const signedMessage = await Signer.signAsync(Buffer.from(privateKey), address, message)
|
|
9
9
|
|
|
10
10
|
return Buffer.isBuffer(signedMessage) ? signedMessage : Buffer.from(signedMessage, 'base64')
|
|
11
11
|
}
|
|
@@ -451,6 +451,9 @@ export class BitcoinMonitorScanner {
|
|
|
451
451
|
|
|
452
452
|
// if txItem.vin has an address that matches ours, means we've spent this tx
|
|
453
453
|
let isSent = false
|
|
454
|
+
// Track whether all non-coinbase inputs belong to our wallet
|
|
455
|
+
let allInputsAreOurs = true
|
|
456
|
+
|
|
454
457
|
txItem.vin.forEach((vin) => {
|
|
455
458
|
// It's an coinbase vin
|
|
456
459
|
if (Object.keys(vin).length === 0) return
|
|
@@ -459,7 +462,12 @@ export class BitcoinMonitorScanner {
|
|
|
459
462
|
from.push(vin.addr)
|
|
460
463
|
|
|
461
464
|
const address = addrMap[vin.addr]
|
|
462
|
-
if (!address)
|
|
465
|
+
if (!address) {
|
|
466
|
+
// This input doesn't belong to our wallet
|
|
467
|
+
allInputsAreOurs = false
|
|
468
|
+
return
|
|
469
|
+
}
|
|
470
|
+
|
|
463
471
|
// it came from us...
|
|
464
472
|
txLogItem.coinAmount = txLogItem.coinAmount.sub(currency.defaultUnit(vin.value))
|
|
465
473
|
isSent = true
|
|
@@ -597,12 +605,16 @@ export class BitcoinMonitorScanner {
|
|
|
597
605
|
if (isSelfSent) {
|
|
598
606
|
txLogItem.selfSend = true
|
|
599
607
|
txLogItem.coinAmount = currency.ZERO
|
|
600
|
-
} else {
|
|
608
|
+
} else if (allInputsAreOurs) {
|
|
601
609
|
// we want coinAmount to be without the fee
|
|
602
|
-
// so far coinAmount = -vins + vouts (to us) = - (sent amount + fees)
|
|
610
|
+
// so far coinAmount = -vins (from us) + vouts (to us) = - (sent amount + fees)
|
|
603
611
|
// add fees to get only sent amount
|
|
612
|
+
// Only do this adjustment when ALL inputs belong to our wallet to avoid
|
|
613
|
+
// false positives in mixed-input transactions (collaborative transactions like coinjoin)
|
|
604
614
|
txLogItem.coinAmount = txLogItem.coinAmount.add(txLogItem.feeAmount)
|
|
605
615
|
}
|
|
616
|
+
// If not all inputs are ours, leave coinAmount as-is to avoid misclassification
|
|
617
|
+
// coinAmount = -vins (from us) + vouts (to us), which will be negative for send transactions
|
|
606
618
|
} else {
|
|
607
619
|
txLogItem.from = from
|
|
608
620
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { getOwnProperty, memoize } from '@exodus/basic-utils'
|
|
2
|
-
import { ECPair } from '@exodus/bitcoinjs'
|
|
3
2
|
import { privateKeyToPublicKey } from '@exodus/crypto/secp256k1'
|
|
4
3
|
import KeyIdentifier from '@exodus/key-identifier'
|
|
5
4
|
import BipPath from 'bip32-path'
|
|
6
5
|
import assert from 'minimalistic-assert'
|
|
6
|
+
import WIF from 'wif'
|
|
7
7
|
|
|
8
8
|
export const createGetKeyWithMetadata = ({
|
|
9
9
|
signer,
|
|
@@ -32,7 +32,7 @@ export const createGetKeyWithMetadata = ({
|
|
|
32
32
|
return getPrivateKeyFromMap(privateKeysAddressMap, networkInfo, purpose, address)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
return getPrivateKeyFromHDKeys(hdkeys, addressPathsMap,
|
|
35
|
+
return getPrivateKeyFromHDKeys(hdkeys, addressPathsMap, purpose, address)
|
|
36
36
|
},
|
|
37
37
|
({ address, derivationPath }) => address + '_' + derivationPath
|
|
38
38
|
)
|
|
@@ -41,30 +41,22 @@ function standardGetPrivateKeyFromMap(privateKeysAddressMap, networkInfo, purpos
|
|
|
41
41
|
const privateWif = getOwnProperty(privateKeysAddressMap, address, 'string')
|
|
42
42
|
assert(privateWif, `there is no private key for address ${address}`)
|
|
43
43
|
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
? networkInfo
|
|
49
|
-
: undefined
|
|
50
|
-
|
|
51
|
-
const { privateKey, compressed } = useNet
|
|
52
|
-
? ECPair.fromWIF(privateWif, useNet)
|
|
53
|
-
: ECPair.fromWIF(privateWif)
|
|
44
|
+
// networkInfo is coininfo('{something}').toBitcoinJS()
|
|
45
|
+
const useNet = networkInfo || { wif: 0x80 } // default to bitcoin
|
|
46
|
+
const { privateKey, compressed, version } = WIF.decode(privateWif)
|
|
47
|
+
if (version !== useNet.wif) throw new Error('Invalid network version')
|
|
54
48
|
|
|
55
49
|
const publicKey = privateKeyToPublicKey({ privateKey, compressed, format: 'buffer' })
|
|
56
|
-
return { privateKey, publicKey, purpose }
|
|
50
|
+
return { privateKey: Buffer.from(privateKey), publicKey, purpose }
|
|
57
51
|
}
|
|
58
52
|
|
|
59
|
-
function getPrivateKeyFromHDKeys(hdkeys, addressPathsMap,
|
|
53
|
+
function getPrivateKeyFromHDKeys(hdkeys, addressPathsMap, purpose, address) {
|
|
60
54
|
const path = getOwnProperty(addressPathsMap, address, 'string')
|
|
61
55
|
assert(hdkeys, 'hdkeys must be provided')
|
|
62
56
|
assert(purpose, `purpose for address ${address} could not be resolved`)
|
|
63
57
|
const hdkey = hdkeys[purpose]
|
|
64
58
|
assert(hdkey, `hdkey for purpose for ${purpose} and address ${address} could not be resolved`)
|
|
65
|
-
const
|
|
66
|
-
const { privateKey } = ECPair.fromPrivateKey(derivedhdkey.privateKey, { network: networkInfo })
|
|
67
|
-
const publicKey = derivedhdkey.publicKey
|
|
59
|
+
const { privateKey, publicKey } = hdkey.derive(path)
|
|
68
60
|
return { privateKey, publicKey, purpose }
|
|
69
61
|
}
|
|
70
62
|
|