@exodus/bitcoin-api 2.29.0 → 2.29.2

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,21 @@
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
+ ## [2.29.2](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@2.29.1...@exodus/bitcoin-api@2.29.2) (2024-11-19)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+
12
+ * fix: bitcoin use utxo.derivationPath path to resolve purpose (#4541)
13
+
14
+
15
+
16
+ ## [2.29.1](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@2.29.0...@exodus/bitcoin-api@2.29.1) (2024-11-13)
17
+
18
+ **Note:** Update exports in fee module of package @exodus/bitcoin-api
19
+
20
+
6
21
  ## [2.29.0](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@2.28.0...@exodus/bitcoin-api@2.29.0) (2024-11-10)
7
22
 
8
23
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/bitcoin-api",
3
- "version": "2.29.0",
3
+ "version": "2.29.2",
4
4
  "description": "Exodus bitcoin-api",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -28,7 +28,7 @@
28
28
  "@exodus/bitcoin-lib": "^2.4.2",
29
29
  "@exodus/bitcoinjs": "^1.1.0",
30
30
  "@exodus/crypto": "^1.0.0-rc.13",
31
- "@exodus/currency": "^5.0.2",
31
+ "@exodus/currency": "^6.0.1",
32
32
  "@exodus/key-identifier": "^1.3.0",
33
33
  "@exodus/models": "^12.0.1",
34
34
  "@exodus/simple-retry": "^0.0.6",
@@ -56,5 +56,5 @@
56
56
  "type": "git",
57
57
  "url": "git+https://github.com/ExodusMovement/assets.git"
58
58
  },
59
- "gitHead": "500277d2c77cd37f7d7a93eceec09e5b8c49f253"
59
+ "gitHead": "ebe9c86c8ecb6e6810613402e27ee62e8c308753"
60
60
  }
@@ -79,49 +79,17 @@ export const getSizeFactory = ({ defaultOutputType, addressApi }) => {
79
79
  4 + // n_locktime
80
80
  varuint.encodingLength(inputs.length) + // inputs_len
81
81
  // input[]
82
- inputs.reduce((t, script) => {
83
- if (script === null) script = '76a914000000000000000000000000000000000000000088ac' // P2PKH
84
- assert(isHex(script), 'script must be hex string')
85
-
86
- const scriptType = scriptClassifier.classifyScriptHex({ assetName, script })
87
-
88
- const supportedTypes = supportedInputTypes[assetName] || supportedInputTypes.default
89
- assert(
90
- supportedTypes.includes(scriptType),
91
- `Only ${supportedTypes.join(', ')} inputs supported right now`
92
- )
93
-
94
- const scriptSigLengths = compressed
95
- ? scriptSigCompressedLengths
96
- : scriptSigUncompressedLengths
97
- const scriptSigLength = scriptSigLengths[scriptType]
98
- return t + 32 + 4 + varuint.encodingLength(scriptSigLength) + scriptSigLength + 4
99
- }, 0) +
82
+ inputs.reduce(
83
+ (t, script) => t + getInputSize({ script, scriptClassifier, assetName, compressed }),
84
+ 0
85
+ ) +
100
86
  varuint.encodingLength(outputs.length) + // outputs_len
101
87
  // output[]
102
- outputs.reduce((t, output) => {
103
- // if (output === null) output = get(asset, 'address.versions.bech32') ? 'P2WSH' : 'P2PKH'
104
-
105
- if (output === null) output = defaultOutputType
106
-
107
- let scriptType = scriptClassify.types[output]
108
- const supportedTypes = supportedOutputTypes[assetName] || supportedOutputTypes.default
109
-
110
- if (!supportedTypes.includes(scriptType)) {
111
- scriptType = scriptClassifier.classifyAddress({
112
- assetName,
113
- address: output,
114
- })
115
- }
116
-
117
- assert(
118
- supportedTypes.includes(scriptType),
119
- `Only ${supportedTypes.join(', ')} outputs supported right now`
120
- )
121
-
122
- const scriptPubKeyLength = scriptPubKeyLengths[scriptType]
123
- return t + 8 + varuint.encodingLength(scriptPubKeyLength) + scriptPubKeyLength
124
- }, 0)
88
+ outputs.reduce(
89
+ (t, output) =>
90
+ t + getOutputSize({ output, scriptClassifier, assetName, defaultOutputType }),
91
+ 0
92
+ )
125
93
 
126
94
  const witnessSize =
127
95
  1 + // marker
@@ -169,4 +137,45 @@ const getFeeEstimatorFactory = ({ defaultOutputType, addressApi }) => {
169
137
  return createDefaultFeeEstimator(getSize)
170
138
  }
171
139
 
140
+ export const getInputSize = ({ script, scriptClassifier, assetName, compressed }) => {
141
+ if (script === null) script = '76a914000000000000000000000000000000000000000088ac' // P2PKH
142
+ assert(isHex(script), 'script must be hex string')
143
+
144
+ const scriptType = scriptClassifier.classifyScriptHex({ assetName, script })
145
+
146
+ const supportedTypes = supportedInputTypes[assetName] || supportedInputTypes.default
147
+ assert(
148
+ supportedTypes.includes(scriptType),
149
+ `Only ${supportedTypes.join(', ')} inputs supported right now`
150
+ )
151
+
152
+ const scriptSigLengths = compressed ? scriptSigCompressedLengths : scriptSigUncompressedLengths
153
+ const scriptSigLength = scriptSigLengths[scriptType]
154
+ return 32 + 4 + varuint.encodingLength(scriptSigLength) + scriptSigLength + 4
155
+ }
156
+
157
+ export const getOutputSize = ({ output, scriptClassifier, assetName, defaultOutputType }) => {
158
+ // if (output === null) output = get(asset, 'address.versions.bech32') ? 'P2WSH' : 'P2PKH'
159
+
160
+ if (output === null) output = defaultOutputType
161
+
162
+ let scriptType = scriptClassify.types[output]
163
+ const supportedTypes = supportedOutputTypes[assetName] || supportedOutputTypes.default
164
+
165
+ if (!supportedTypes.includes(scriptType)) {
166
+ scriptType = scriptClassifier.classifyAddress({
167
+ assetName,
168
+ address: output,
169
+ })
170
+ }
171
+
172
+ assert(
173
+ supportedTypes.includes(scriptType),
174
+ `Only ${supportedTypes.join(', ')} outputs supported right now`
175
+ )
176
+
177
+ const scriptPubKeyLength = scriptPubKeyLengths[scriptType]
178
+ return 8 + varuint.encodingLength(scriptPubKeyLength) + scriptPubKeyLength
179
+ }
180
+
172
181
  export default getFeeEstimatorFactory
package/src/fee/index.js CHANGED
@@ -1,2 +1,9 @@
1
1
  export * from './get-fee-resolver.js'
2
- export { default as getFeeEstimatorFactory } from './fee-estimator.js'
2
+ export {
3
+ default as getFeeEstimatorFactory,
4
+ getInputSize,
5
+ getOutputSize,
6
+ getSizeFactory,
7
+ } from './fee-estimator.js'
8
+ export { default as createDefaultFeeEstimator } from './fee-utils.js'
9
+ export { scriptClassifierFactory } from './script-classifier.js'
@@ -545,6 +545,7 @@ export class BitcoinMonitorScanner {
545
545
  script: vout.scriptPubKey.hex,
546
546
  value: val,
547
547
  rbfEnabled: txItem.rbf,
548
+ derivationPath: address.meta.keyIdentifier?.derivationPath,
548
549
  ...(txItem.vin.length === 0 ? { isCoinbase: true } : undefined),
549
550
  }
550
551
 
@@ -8,6 +8,7 @@ export function createInputs(utxos) {
8
8
  value: utxo.value.toBaseBufferLE(8),
9
9
  script: utxo.script,
10
10
  sequence: getTxSequence(),
11
+ derivationPath: utxo.derivationPath,
11
12
  }))
12
13
  }
13
14
 
@@ -765,6 +765,7 @@ function defaultCreateInputs(utxos, rbfEnabled) {
765
765
  script: utxo.script,
766
766
  sequence: getTxSequence(rbfEnabled),
767
767
  inscriptionId: utxo.inscriptionId,
768
+ derivationPath: utxo.derivationPath,
768
769
  }))
769
770
  }
770
771
 
@@ -14,20 +14,27 @@ export const createGetKeyWithMetadata = ({
14
14
  coinInfo,
15
15
  getKeyIdentifier,
16
16
  }) =>
17
- memoize((address) => {
18
- const purpose = resolvePurpose(address)
19
- const networkInfo = coinInfo.toBitcoinJS()
17
+ memoize(
18
+ ({ address, derivationPath }) => {
19
+ const purpose = derivationPath
20
+ ? parseInt(derivationPath.split('/')[1].replace(/'/g, ''))
21
+ : resolvePurpose(address)
20
22
 
21
- if (signer) {
22
- return getPublicKeyFromSigner(signer, addressPathsMap, purpose, address, getKeyIdentifier)
23
- }
23
+ assert(typeof purpose === 'number' && purpose, 'purpose must be a number')
24
+ const networkInfo = coinInfo.toBitcoinJS()
24
25
 
25
- if (privateKeysAddressMap) {
26
- return getPrivateKeyFromMap(privateKeysAddressMap, networkInfo, purpose, address)
27
- }
26
+ if (signer) {
27
+ return getPublicKeyFromSigner(signer, addressPathsMap, purpose, address, getKeyIdentifier)
28
+ }
28
29
 
29
- return getPrivateKeyFromHDKeys(hdkeys, addressPathsMap, networkInfo, purpose, address)
30
- })
30
+ if (privateKeysAddressMap) {
31
+ return getPrivateKeyFromMap(privateKeysAddressMap, networkInfo, purpose, address)
32
+ }
33
+
34
+ return getPrivateKeyFromHDKeys(hdkeys, addressPathsMap, networkInfo, purpose, address)
35
+ },
36
+ ({ address, derivationPath }) => address + '_' + derivationPath
37
+ )
31
38
 
32
39
  function getPrivateKeyFromMap(privateKeysAddressMap, networkInfo, purpose, address) {
33
40
  const privateWif = getOwnProperty(privateKeysAddressMap, address, 'string')
@@ -34,14 +34,17 @@ export function createSignWithWallet({
34
34
  if (!inputInfo) continue
35
35
 
36
36
  const input = psbt.data.inputs[index]
37
- const { address, sigHash } = inputInfo
37
+ const { address, sigHash, derivationPath } = inputInfo
38
38
  // The sighash value from the PSBT input itself will be used.
39
39
  // This list just represents possible sighash values the inputs can have.
40
40
  const allowedSigHashTypes =
41
41
  sigHash === undefined
42
42
  ? undefined // `SIGHASH_DEFAULT` is a default safe sig hash, always allow it.
43
43
  : [sigHash, Transaction.SIGHASH_ALL]
44
- const { keyId, privateKey, publicKey, purpose } = await getKeyWithMetadata(address)
44
+ const { keyId, privateKey, publicKey, purpose } = await getKeyWithMetadata({
45
+ address,
46
+ derivationPath,
47
+ })
45
48
 
46
49
  const isTaprootInput = bip371.isTaprootInput(input)
47
50
  const isTapLeafScriptSpend = input.tapLeafScript && input.tapLeafScript.length > 0