@exodus/bitcoin-api 2.3.3 → 2.3.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/bitcoin-api",
3
- "version": "2.3.3",
3
+ "version": "2.3.5",
4
4
  "description": "Exodus bitcoin-api",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -19,6 +19,7 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "@exodus/asset-lib": "^3.7.2",
22
+ "@exodus/basic-utils": "^2.0.1",
22
23
  "@exodus/bip44-constants": "^195.0.0",
23
24
  "@exodus/bitcoinjs-lib": "6.0.2-beta.5",
24
25
  "@exodus/models": "^8.10.4",
@@ -35,10 +36,11 @@
35
36
  "url-join": "4.0.0"
36
37
  },
37
38
  "devDependencies": {
39
+ "@exodus/asset-lib": "^3.7.1",
38
40
  "@exodus/bcash-meta": "^1.0.0",
39
41
  "@exodus/bip-schnorr": "0.6.6-fork-1",
40
42
  "@exodus/bitcoin-meta": "^1.0.1",
41
43
  "@noble/secp256k1": "~1.5.3"
42
44
  },
43
- "gitHead": "87922c8e20b6e152449dd6e35185587fa78bba25"
45
+ "gitHead": "3dd9a1846d6f34252c12e966e1ee1f851cca4173"
44
46
  }
@@ -16,6 +16,8 @@ import {
16
16
  import { findUnconfirmedSentRbfTxs } from '../tx-utils'
17
17
  import { getUsableUtxos, getUtxos } from '../utxos-utils'
18
18
 
19
+ import * as defaultBitcoinjsLib from '@exodus/bitcoinjs-lib'
20
+
19
21
  const ASSETS_SUPPORTED_BIP_174 = [
20
22
  'bitcoin',
21
23
  'bitcoinregtest',
@@ -79,12 +81,55 @@ export async function getNonWitnessTxs(asset, utxos, insightClient) {
79
81
  return rawTxs
80
82
  }
81
83
 
84
+ export const getSizeAndChangeScriptFactory = ({ bitcoinJsLib = defaultBitcoinjsLib } = {}) => ({
85
+ assetName,
86
+ tx,
87
+ rawTx,
88
+ changeUtxoIndex,
89
+ txId,
90
+ }) => {
91
+ assert(assetName, 'assetName is required')
92
+ assert(rawTx, 'rawTx is required')
93
+ assert(typeof changeUtxoIndex === 'number', 'changeUtxoIndex must be a number')
94
+
95
+ const getSize = (tx) => {
96
+ if (typeof tx.size === 'number') return tx.size
97
+ if (typeof tx.virtualSize === 'function') {
98
+ return tx.virtualSize()
99
+ }
100
+ if (typeof tx.virtualSize === 'number') {
101
+ return tx.virtualSize
102
+ }
103
+ return undefined
104
+ }
105
+
106
+ if (tx) {
107
+ return {
108
+ script: tx.outs?.[changeUtxoIndex]?.script.toString('hex'),
109
+ size: getSize(tx),
110
+ }
111
+ }
112
+ // Trezor doesn't return tx!! we need to reparse it!
113
+ const parsedTx = bitcoinJsLib.Transaction.fromBuffer(Buffer.from(rawTx, 'hex'))
114
+ try {
115
+ return {
116
+ script: parsedTx.outs?.[changeUtxoIndex]?.script.toString('hex'),
117
+ size: getSize(parsedTx),
118
+ }
119
+ } catch (e) {
120
+ console.warn(
121
+ `tx-send warning: ${assetName} cannot extract script and size from tx ${txId}. ${e}`
122
+ )
123
+ return {}
124
+ }
125
+ }
126
+
82
127
  // not ported from Exodus; but this demos signing / broadcasting
83
128
  // NOTE: this will be ripped out in the coming weeks
84
129
 
85
130
  export const createAndBroadcastTXFactory = ({
86
131
  getFeeEstimator,
87
-
132
+ getSizeAndChangeScript = getSizeAndChangeScriptFactory(), // for decred customizations
88
133
  allowUnconfirmedRbfEnabledUtxos,
89
134
  }) => async ({ asset, walletAccount, address, amount, options }, { assetClientInterface }) => {
90
135
  const {
@@ -308,6 +353,8 @@ export const createAndBroadcastTXFactory = ({
308
353
  }
309
354
  }
310
355
 
356
+ const { script, size } = getSizeAndChangeScript({ assetName, tx, rawTx, changeUtxoIndex, txId })
357
+
311
358
  let remainingUtxos = usableUtxos.difference(selectedUtxos)
312
359
  if (changeUtxoIndex !== -1) {
313
360
  const address = Address.create(ourAddress.address, ourAddress.meta)
@@ -315,7 +362,7 @@ export const createAndBroadcastTXFactory = ({
315
362
  txId,
316
363
  address,
317
364
  vout: changeUtxoIndex,
318
- script: tx.outs[changeUtxoIndex].script.toString('hex'),
365
+ script,
319
366
  value: change,
320
367
  confirmations: 0,
321
368
  rbfEnabled,
@@ -364,9 +411,7 @@ export const createAndBroadcastTXFactory = ({
364
411
  data: {
365
412
  sent: selfSend ? [] : receivers,
366
413
  rbfEnabled,
367
- feePerKB: ['bitcoin', 'bitcoinregtest', 'bitcointestnet'].includes(assetName)
368
- ? fee.div(tx.virtualSize / 1000).toBaseNumber()
369
- : undefined,
414
+ feePerKB: size ? fee.div(size / 1000).toBaseNumber() : undefined,
370
415
  changeAddress: changeOutput ? ourAddress : undefined,
371
416
  blockHeight,
372
417
  blocksSeen: 0,
@@ -1,7 +1,10 @@
1
1
  import assert from 'minimalistic-assert'
2
2
  import lodash from 'lodash'
3
3
  import ECPairFactory from 'ecpair'
4
- import { Psbt, Transaction } from '@exodus/bitcoinjs-lib'
4
+ import { payments, Psbt, Transaction } from '@exodus/bitcoinjs-lib'
5
+ import { getOwnProperty } from '@exodus/basic-utils'
6
+
7
+ import secp256k1 from 'secp256k1'
5
8
 
6
9
  import { toAsyncSigner, tweakSigner } from './taproot'
7
10
 
@@ -45,10 +48,11 @@ export const signTxFactory = ({ assetName, resolvePurpose, keys, coinInfo, netwo
45
48
  ECPair = ECPair || ECPairFactory(ecc)
46
49
 
47
50
  const getKeyAndPurpose = lodash.memoize((address) => {
48
- // TODO: Consider using privateKeysAddressMap for other assets
49
51
  const purpose = resolvePurpose(address)
50
52
  if (privateKeysAddressMap) {
51
- const key = ECPair.fromWIF(privateKeysAddressMap[address], networkInfo)
53
+ const privateKey = getOwnProperty(privateKeysAddressMap, address, 'string')
54
+ assert(privateKey, `there is no private key for address ${address}`)
55
+ const key = ECPair.fromWIF(privateKey, networkInfo)
52
56
  return { key, purpose }
53
57
  }
54
58
  const path = addressPathsMap[address]
@@ -96,6 +100,20 @@ export const signTxFactory = ({ assetName, resolvePurpose, keys, coinInfo, netwo
96
100
  for (let index = 0; index < inputs.length; index++) {
97
101
  const { address } = inputs[index]
98
102
  const { key, purpose } = getKeyAndPurpose(address)
103
+
104
+ if (purpose === 49) {
105
+ // If spending from a P2SH address, we assume the address is P2SH wrapping
106
+ // P2WPKH. Exodus doesn't use P2SH addresses so we should only ever be
107
+ // signing a P2SH input if we are importing a private key
108
+ // BIP143: As a default policy, only compressed public keys are accepted in P2WPKH and P2WSH
109
+ const publicKey = secp256k1.publicKeyCreate(key.privateKey, true)
110
+ const p2wpkh = payments.p2wpkh({ pubkey: publicKey })
111
+ const p2sh = payments.p2sh({ redeem: p2wpkh })
112
+ psbt.updateInput(index, {
113
+ redeemScript: p2sh.redeem.output,
114
+ })
115
+ }
116
+
99
117
  if (ecc.signSchnorrAsync) {
100
118
  // desktop / BE / mobile with bip-schnorr signing
101
119
  const isTaprootAddress = purpose === 86