@exodus/bitcoin-api 2.12.0 → 2.13.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/bitcoin-api",
3
- "version": "2.12.0",
3
+ "version": "2.13.0",
4
4
  "description": "Exodus bitcoin-api",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -23,14 +23,13 @@
23
23
  "@exodus/bip322-js": "^1.1.0-exodus.4",
24
24
  "@exodus/bip44-constants": "^195.0.0",
25
25
  "@exodus/bitcoin-lib": "2.3.0",
26
+ "@exodus/bitcoinerlab-secp256k1": "^1.0.5-exodus.1",
26
27
  "@exodus/bitcoinjs-lib": "^6.1.5-exodus.1",
27
28
  "@exodus/currency": "^2.3.2",
28
29
  "@exodus/fetch": "^1.3.0",
29
30
  "@exodus/models": "^11.0.0",
30
- "@exodus/secp256k1": "4.0.2-exodus.0",
31
31
  "@exodus/simple-retry": "0.0.6",
32
32
  "@exodus/timer": "^1.0.0",
33
- "@noble/secp256k1": "~1.7.1",
34
33
  "bech32": "^1.1.3",
35
34
  "bip32-path": "^0.4.2",
36
35
  "bn.js": "4.12.0",
@@ -58,5 +57,5 @@
58
57
  "jest-when": "^3.5.1",
59
58
  "safe-buffer": "^5.2.1"
60
59
  },
61
- "gitHead": "49a0b40b825c556ea08538c5cedf64754dd35369"
60
+ "gitHead": "9d296fc0875749545bc95b2a976e93739d63553b"
62
61
  }
@@ -1,3 +1,9 @@
1
- import { desktopEcc } from './desktop'
1
+ import { initEccLib } from '@exodus/bitcoinjs-lib'
2
2
 
3
- export const eccFactory = () => desktopEcc
3
+ import bitcoinerlab from '@exodus/bitcoinerlab-secp256k1'
4
+
5
+ export const ecc = bitcoinerlab
6
+
7
+ initEccLib(ecc)
8
+
9
+ export const eccFactory = () => ecc
@@ -1,7 +1,13 @@
1
1
  import assert from 'minimalistic-assert'
2
2
  import { getUtxosData } from './utxo-selector'
3
3
  import { findUnconfirmedSentRbfTxs } from '../tx-utils'
4
- import { getUsableUtxos, getUtxos } from '../utxos-utils'
4
+ import {
5
+ getInscriptionIds,
6
+ getOrdinalsUtxos,
7
+ getTransferOrdinalsUtxos,
8
+ getUsableUtxos,
9
+ getUtxos,
10
+ } from '../utxos-utils'
5
11
  import { canBumpTx } from './can-bump-tx'
6
12
  import { getUnconfirmedTxAncestorMap } from '../unconfirmed-ancestor-data'
7
13
 
@@ -42,7 +48,7 @@ export class GetFeeResolver {
42
48
  assert(!nft, 'nft must not be provided when brc20 is provided!!!')
43
49
  }
44
50
 
45
- const inscriptionIds = nft?.tokenId ? [nft?.tokenId] : brc20?.inscriptionIds
51
+ const inscriptionIds = getInscriptionIds({ nft, brc20 })
46
52
 
47
53
  const { resolvedFee, extraFee } = this.#getUtxosData({
48
54
  asset,
@@ -100,6 +106,12 @@ export class GetFeeResolver {
100
106
  const utxos = getUtxos({ accountState, asset })
101
107
  const unconfirmedTxAncestor = getUnconfirmedTxAncestorMap({ accountState })
102
108
 
109
+ const ordinalsUtxos = getOrdinalsUtxos({ accountState, asset })
110
+
111
+ const transferOrdinalsUtxos = inscriptionIds
112
+ ? getTransferOrdinalsUtxos({ inscriptionIds, ordinalsUtxos })
113
+ : undefined
114
+
103
115
  const usableUtxos = getUsableUtxos({
104
116
  asset,
105
117
  utxos,
@@ -117,6 +129,7 @@ export class GetFeeResolver {
117
129
  amount,
118
130
  feeRate: feePerKB,
119
131
  receiveAddress,
132
+ transferOrdinalsUtxos,
120
133
  inscriptionIds,
121
134
  isSendAll,
122
135
  getFeeEstimator: this.#getFeeEstimator,
@@ -34,6 +34,7 @@ export const selectUtxos = ({
34
34
  allowUnconfirmedRbfEnabledUtxos,
35
35
  unconfirmedTxAncestor,
36
36
  inscriptionIds, // for each inscription transfer, we need to calculate one more input and one more output
37
+ transferOrdinalsUtxos, // to calculate the size of the input
37
38
  }) => {
38
39
  const resolvedReceiveAddresses = getBestReceiveAddresses({
39
40
  asset,
@@ -197,7 +198,9 @@ export const selectUtxos = ({
197
198
  selectedUtxosValue = selectedUtxosValue.add(newUtxo.value)
198
199
  }
199
200
 
200
- let selectedUtxos = UtxoCollection.fromArray(selectedUtxosArray, { currency })
201
+ let selectedUtxos = (transferOrdinalsUtxos || UtxoCollection.createEmpty({ currency })).union(
202
+ UtxoCollection.fromArray(selectedUtxosArray, { currency })
203
+ ) // extremelly important, orden must be kept!!! ordinals utxos go first!!!
201
204
 
202
205
  // start figuring out fees
203
206
  const outputs =
@@ -240,6 +243,7 @@ export const getUtxosData = ({
240
243
  mustSpendUtxos,
241
244
  allowUnconfirmedRbfEnabledUtxos,
242
245
  inscriptionIds,
246
+ transferOrdinalsUtxos,
243
247
  unconfirmedTxAncestor,
244
248
  utxosDescendingOrder,
245
249
  }) => {
@@ -257,6 +261,7 @@ export const getUtxosData = ({
257
261
  allowUnconfirmedRbfEnabledUtxos,
258
262
  unconfirmedTxAncestor,
259
263
  inscriptionIds,
264
+ transferOrdinalsUtxos,
260
265
  utxosDescendingOrder,
261
266
  })
262
267
 
package/src/index.js CHANGED
@@ -16,6 +16,7 @@ export * from './unconfirmed-ancestor-data'
16
16
  export * from './parse-unsigned-tx'
17
17
  export * from './insight-api-client/util'
18
18
  export * from './move-funds'
19
+ export { createEncodeMultisigContract } from './multisig-address'
19
20
  export { toAsyncSigner } from './tx-sign/taproot'
20
21
  export { toXOnly } from './bitcoinjs-lib/ecc-utils'
21
22
  export * from './ordinals-utils'
@@ -0,0 +1,81 @@
1
+ import * as defaultBitcoinjsLib from '@exodus/bitcoinjs-lib'
2
+ import { toXOnly } from './bitcoinjs-lib/ecc-utils'
3
+ import { eccFactory } from './bitcoinjs-lib/ecc'
4
+
5
+ // Key to use when key path spending is disabled https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs
6
+ const DUMMY_TAPROOT_PUBKEY = Buffer.from(
7
+ '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0',
8
+ 'hex'
9
+ )
10
+
11
+ // Leaf version for BIP342 is 0xc0 or 192 https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#specification
12
+ const LEAF_VERSION_TAPSCRIPT = 192
13
+
14
+ // Limit multisig keys to 20 for now
15
+ const MAX_PUBKEYS = 20
16
+
17
+ export const createEncodeMultisigContract =
18
+ ({
19
+ bitcoinjsLib = defaultBitcoinjsLib,
20
+ network = bitcoinjsLib.Network.bitcoin,
21
+ ecc = eccFactory(),
22
+ }) =>
23
+ (publicKeys, { threshold = publicKeys.length, version = 0 } = Object.create(null)) => {
24
+ if (
25
+ !Array.isArray(publicKeys) ||
26
+ publicKeys.some((k) => !Buffer.isBuffer(k) || !ecc.isPointCompressed(k))
27
+ ) {
28
+ throw new Error('publicKeys must be an Array of Buffers representing compressed public keys')
29
+ }
30
+
31
+ if (publicKeys.length <= 0 || publicKeys.length > MAX_PUBKEYS) {
32
+ throw new Error(`asset.encodeMultisigContract supports from 1 to ${MAX_PUBKEYS} pubKeys`)
33
+ }
34
+
35
+ if (new Set(publicKeys.map((k) => k.toString('hex'))).size !== publicKeys.length) {
36
+ throw new Error('publicKeys must not contain any duplicates')
37
+ }
38
+
39
+ if (!Number.isSafeInteger(version)) {
40
+ throw new TypeError('asset.encodeMultisigContract requires meta.version to be an integer')
41
+ }
42
+
43
+ // Only support version 0 for now
44
+ if (version !== 0) {
45
+ throw new Error(`asset.encodeMultisigContract does not support version ${version}`)
46
+ }
47
+
48
+ if (!Number.isSafeInteger(threshold) || threshold <= 0 || threshold > MAX_PUBKEYS) {
49
+ throw new Error(
50
+ `asset.encodeMultisigContract requires meta.threshold to be an integer between 1 and ${MAX_PUBKEYS}`
51
+ )
52
+ }
53
+
54
+ if (threshold > publicKeys.length) {
55
+ throw new Error('threshold must be <= publicKeys.length')
56
+ }
57
+
58
+ // Sort according to BIP67 https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki
59
+ publicKeys.sort((a, b) => Buffer.compare(a, b))
60
+
61
+ // Create multisig redeem script https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#cite_note-5
62
+ const OPS = bitcoinjsLib.script.OPS
63
+ const chunks = []
64
+ const keysIter = publicKeys[Symbol.iterator]()
65
+
66
+ chunks.push(toXOnly(keysIter.next().value), OPS.OP_CHECKSIG)
67
+ for (const key of keysIter) {
68
+ chunks.push(toXOnly(key), OPS.OP_CHECKSIGADD)
69
+ }
70
+
71
+ chunks.push(Buffer.from([threshold]), OPS.OP_NUMEQUAL)
72
+
73
+ const output = bitcoinjsLib.script.compile(chunks)
74
+
75
+ return bitcoinjsLib.payments.p2tr({
76
+ internalPubkey: DUMMY_TAPROOT_PUBKEY,
77
+ scriptTree: { output },
78
+ redeem: { output, redeemVersion: LEAF_VERSION_TAPSCRIPT },
79
+ network,
80
+ })
81
+ }
@@ -15,6 +15,7 @@ import {
15
15
  } from './dogecoin'
16
16
  import { findUnconfirmedSentRbfTxs } from '../tx-utils'
17
17
  import {
18
+ getInscriptionIds,
18
19
  getOrdinalsUtxos,
19
20
  getTransferOrdinalsUtxos,
20
21
  getUsableUtxos,
@@ -221,11 +222,9 @@ export const getPrepareSendTransaction =
221
222
  allowUnconfirmedRbfEnabledUtxos,
222
223
  utxosDescendingOrder,
223
224
  rbfEnabled: providedRbfEnabled,
225
+ assetClientInterface,
224
226
  }) =>
225
- async (
226
- { asset: maybeToken, walletAccount, address, amount: tokenAmount, options },
227
- { assetClientInterface }
228
- ) => {
227
+ async ({ asset: maybeToken, walletAccount, address, amount: tokenAmount, options }) => {
229
228
  const {
230
229
  multipleAddressesEnabled,
231
230
  feePerKB,
@@ -258,7 +257,7 @@ export const getPrepareSendTransaction =
258
257
  }
259
258
 
260
259
  const amount = isToken ? asset.currency.ZERO : tokenAmount
261
- const inscriptionIds = nft?.tokenId ? [nft?.tokenId] : brc20 ? brc20.inscriptionIds : undefined
260
+ const inscriptionIds = getInscriptionIds({ nft, brc20 })
262
261
 
263
262
  assert(
264
263
  ordinalsEnabled || !inscriptionIds,
@@ -351,6 +350,7 @@ export const getPrepareSendTransaction =
351
350
  allowUnconfirmedRbfEnabledUtxos,
352
351
  unconfirmedTxAncestor,
353
352
  inscriptionIds,
353
+ transferOrdinalsUtxos,
354
354
  utxosDescendingOrder,
355
355
  })
356
356
 
@@ -365,10 +365,6 @@ export const getPrepareSendTransaction =
365
365
  throw new Error(`Unable to bump ${bumpTxId}`)
366
366
  }
367
367
 
368
- if (transferOrdinalsUtxos) {
369
- selectedUtxos = transferOrdinalsUtxos.union(selectedUtxos)
370
- }
371
-
372
368
  if (replaceTx) {
373
369
  replaceTx = replaceTx.clone()
374
370
  replaceTx = replaceTx.update({ data: { ...replaceTx.data } })
@@ -474,11 +470,9 @@ export const createAndBroadcastTXFactory =
474
470
  allowUnconfirmedRbfEnabledUtxos,
475
471
  ordinalsEnabled = false,
476
472
  utxosDescendingOrder,
473
+ assetClientInterface,
477
474
  }) =>
478
- async (
479
- { asset: maybeToken, walletAccount, address, amount: tokenAmount, options },
480
- { assetClientInterface }
481
- ) => {
475
+ async ({ asset: maybeToken, walletAccount, address, amount: tokenAmount, options }) => {
482
476
  // Prepare transaction
483
477
  const { bumpTxId, nft, isExchange, isBip70, isRbfAllowed = true, feeOpts } = options
484
478
 
@@ -503,10 +497,8 @@ export const createAndBroadcastTXFactory =
503
497
  allowUnconfirmedRbfEnabledUtxos,
504
498
  utxosDescendingOrder,
505
499
  rbfEnabled,
506
- })(
507
- { asset: maybeToken, walletAccount, address, amount: tokenAmount, options },
508
- { assetClientInterface }
509
- )
500
+ assetClientInterface,
501
+ })({ asset: maybeToken, walletAccount, address, amount: tokenAmount, options })
510
502
  const {
511
503
  amount,
512
504
  change,
@@ -37,7 +37,9 @@ export function createSignWithWallet({
37
37
  const { key, purpose, publicKey } = getKeyAndPurpose(address)
38
38
 
39
39
  const isP2SH = purpose === 49
40
- const isTaprootAddress = purpose === 86
40
+ const hasTapLeafScript =
41
+ psbt.data.inputs[index].tapLeafScript && psbt.data.inputs[index].tapLeafScript.length > 0
42
+ const isTaprootKeySpend = purpose === 86 && !hasTapLeafScript
41
43
 
42
44
  if (isP2SH) {
43
45
  // If spending from a P2SH address, we assume the address is P2SH wrapping
@@ -56,7 +58,7 @@ export function createSignWithWallet({
56
58
  } else {
57
59
  throw new Error('Expected P2SH script to be a nested segwit input')
58
60
  }
59
- } else if (isTaprootAddress && !Buffer.isBuffer(psbt.data.inputs[index].tapInternalKey)) {
61
+ } else if (isTaprootKeySpend && !Buffer.isBuffer(psbt.data.inputs[index].tapInternalKey)) {
60
62
  // tapInternalKey is metadata for signing and not part of the hash to sign.
61
63
  // so modifying it here is fine.
62
64
  psbt.updateInput(index, {
@@ -67,7 +69,7 @@ export function createSignWithWallet({
67
69
  // desktop / BE / mobile with bip-schnorr signing
68
70
  await psbt.signInputAsync(
69
71
  index,
70
- toAsyncSigner({ keyPair: key, isTaprootAddress, network }),
72
+ toAsyncSigner({ keyPair: key, isTaprootKeySpend, network }),
71
73
  allowedSigHashTypes
72
74
  )
73
75
  }
@@ -35,7 +35,7 @@ export const signTxFactory = ({ assetName, resolvePurpose, coinInfo, network })
35
35
 
36
36
  await signWithWallet(psbt, inputsToSign)
37
37
 
38
- const skipFinalize = !!unsignedTx.txData.psbtBuffer
38
+ const skipFinalize = !!unsignedTx.txData.psbtBuffer || unsignedTx.txMeta.returnPsbt
39
39
  return extractTransaction({ psbt, skipFinalize })
40
40
  }
41
41
  }
@@ -17,17 +17,21 @@ export function createPrepareForSigning({ assetName, resolvePurpose, coinInfo })
17
17
  assert(coinInfo, 'coinInfo is required')
18
18
 
19
19
  return ({ unsignedTx }) => {
20
+ const networkInfo = coinInfo.toBitcoinJS()
21
+
20
22
  const isPsbtBufferPassed =
21
23
  unsignedTx.txData.psbtBuffer &&
22
24
  unsignedTx.txMeta.addressPathsMap &&
23
25
  unsignedTx.txMeta.inputsToSign
24
26
  if (isPsbtBufferPassed) {
25
27
  // PSBT created externally (Web3, etc..)
26
- return createPsbtFromBuffer({ psbtBuffer: unsignedTx.txData.psbtBuffer })
28
+ return createPsbtFromBuffer({
29
+ psbtBuffer: unsignedTx.txData.psbtBuffer,
30
+ networkInfo,
31
+ })
27
32
  }
28
33
 
29
34
  // Create PSBT based on internal Exodus data structure
30
- const networkInfo = coinInfo.toBitcoinJS()
31
35
  const psbt = createPsbtFromTxData({
32
36
  ...unsignedTx.txData,
33
37
  ...unsignedTx.txMeta,
@@ -41,8 +45,8 @@ export function createPrepareForSigning({ assetName, resolvePurpose, coinInfo })
41
45
  }
42
46
 
43
47
  // Creates a PSBT instance from the passed transaction buffer provided by 3rd parties (e.g. dApps).
44
- function createPsbtFromBuffer({ psbtBuffer, ecc }) {
45
- return Psbt.fromBuffer(psbtBuffer, { eccLib: ecc })
48
+ function createPsbtFromBuffer({ psbtBuffer, ecc, networkInfo }) {
49
+ return Psbt.fromBuffer(psbtBuffer, { eccLib: ecc, network: networkInfo })
46
50
  }
47
51
 
48
52
  // Creates a PSBT instance from the passed inputs, outputs etc. The wallet itself provides this data.
@@ -54,7 +58,7 @@ function createPsbtFromTxData({ inputs, outputs, rawTxs, networkInfo, resolvePur
54
58
  const psbt = new Psbt({ maximumFeeRate, network: networkInfo })
55
59
 
56
60
  // Fill tx
57
- for (const { txId, vout, address, value, script, sequence } of inputs) {
61
+ for (const { txId, vout, address, value, script, sequence, tapLeafScript } of inputs) {
58
62
  // TODO: don't use the purpose as intermediate variable
59
63
  // see internals of `resolvePurposes`, just use `isP2TR, isP2SH etc directly
60
64
  const purpose = resolvePurpose(address)
@@ -64,6 +68,10 @@ function createPsbtFromTxData({ inputs, outputs, rawTxs, networkInfo, resolvePur
64
68
 
65
69
  const txIn = { hash: txId, index: vout, sequence }
66
70
  if (isSegwitAddress || isTaprootAddress) {
71
+ if (isTaprootAddress && tapLeafScript) {
72
+ txIn.tapLeafScript = tapLeafScript
73
+ }
74
+
67
75
  // witness outputs only require the value and the script, not the full transaction
68
76
  txIn.witnessUtxo = { value, script: Buffer.from(script, 'hex') }
69
77
  } else {
@@ -40,22 +40,22 @@ function tapTweakHash(pubKey, h) {
40
40
  /**
41
41
  * Take a sync signer and make it async.
42
42
  */
43
- export function toAsyncSigner({ keyPair, isTaprootAddress, network }) {
43
+ export function toAsyncSigner({ keyPair, isTaprootKeySpend, network }) {
44
44
  assert(keyPair, 'keyPair is required')
45
45
 
46
- if (isTaprootAddress) {
46
+ if (isTaprootKeySpend) {
47
47
  keyPair = tweakSigner({ signer: keyPair, ECPair, network })
48
48
  }
49
49
 
50
50
  // eslint-disable-next-line @exodus/mutable/no-param-reassign-prop-only
51
51
  keyPair.sign = async (h) => {
52
- const sig = await ecc.signAsync(h, keyPair.privateKey)
52
+ const sig = ecc.sign(h, keyPair.privateKey)
53
53
  return Buffer.from(sig)
54
54
  }
55
55
 
56
56
  // eslint-disable-next-line @exodus/mutable/no-param-reassign-prop-only
57
57
  keyPair.signSchnorr = async (h) => {
58
- const sig = await ecc.signSchnorrAsync(h, keyPair.privateKey, getSchnorrEntropy())
58
+ const sig = ecc.signSchnorr(h, keyPair.privateKey, getSchnorrEntropy())
59
59
  return Buffer.from(sig)
60
60
  }
61
61
 
@@ -9,6 +9,10 @@ export const getInscriptionTxId = (inscriptionId) => {
9
9
  return inscriptionId.split('i')[0]
10
10
  }
11
11
 
12
+ export function getInscriptionIds({ nft, brc20 }) {
13
+ return nft?.tokenId ? [nft?.tokenId] : brc20 ? brc20.inscriptionIds : undefined
14
+ }
15
+
12
16
  export function getTransferOrdinalsUtxos({ inscriptionIds, ordinalsUtxos }) {
13
17
  const transferOrdinalsUtxos = ordinalsUtxos.filter((utxo) =>
14
18
  utxo.inscriptions?.some((i) => inscriptionIds.includes(i.inscriptionId))
@@ -1,81 +0,0 @@
1
- import secp256k1 from '@exodus/secp256k1'
2
- import { toXOnly } from '../ecc-utils'
3
-
4
- /**
5
- * Common ecc functions between mobile and desktop. Once mobile accepts @noble/secp256k1, we can unify both
6
- */
7
- export const common = {
8
- /**
9
- *
10
- * @param msg32 {Uint8Array} the message
11
- * @param seckey {Uint8Array} the private key
12
- * @param data {Uint8Array} the data to sign
13
- */
14
- sign: (msg32, seckey, data) => secp256k1.ecdsaSign(msg32, seckey, { data }).signature,
15
-
16
- /**
17
- *
18
- * @param msg32 {Uint8Array} the message to verify
19
- * @param publicKey {Uint8Array} the public key
20
- * @param signature {Uint8Array} the signature
21
- * @returns {boolean}
22
- */
23
- verify: (msg32, publicKey, signature) => secp256k1.ecdsaVerify(signature, msg32, publicKey),
24
-
25
- /**
26
- *
27
- * @param seckey {Uint8Array}
28
- * @param tweak {Uint8Array}
29
- */
30
- privateAdd: (seckey, tweak) =>
31
- // cloning input. secp256k1 modifies it and it cannot be reused/cached
32
- secp256k1.privateKeyTweakAdd(Buffer.from(seckey), tweak),
33
-
34
- /**
35
- * @param seckey {Uint8Array} the public key
36
- */
37
- privateNegate: (seckey) =>
38
- // cloning input. secp256k1 modifies it and it cannot be reused/cached
39
- secp256k1.privateKeyNegate(Buffer.from(seckey)),
40
-
41
- /**
42
- *
43
- * @param publicKey {Uint8Array}
44
- * @param tweak {Uint8Array}
45
- * @returns {{parity: (number), xOnlyPubkey}|null}
46
- */
47
- xOnlyPointAddTweak: (publicKey, tweak) => {
48
- try {
49
- const t = secp256k1.publicKeyTweakAdd(toPubKey(publicKey), tweak)
50
- return {
51
- parity: t[0] === 0x02 ? 0 : 1,
52
- xOnlyPubkey: toXOnly(t),
53
- }
54
- } catch {
55
- return null
56
- }
57
- },
58
-
59
- /**
60
- * @param seckey {Uint8Array}
61
- * @returns {boolean}
62
- */
63
- isPrivate: (seckey) => secp256k1.privateKeyVerify(seckey),
64
- /**
65
- * @param seckey {Uint8Array}
66
- * @param compressed {boolean}
67
- */
68
- pointFromScalar: (seckey, compressed) => secp256k1.publicKeyCreate(seckey, compressed),
69
- }
70
-
71
- /**
72
- *
73
- * @param xOnly {Uint8Array}
74
- * @returns {Uint8Array}
75
- */
76
- export const toPubKey = (xOnly) => {
77
- const p = new Uint8Array(33)
78
- p.set([0x02])
79
- p.set(xOnly, 1)
80
- return p
81
- }
@@ -1,83 +0,0 @@
1
- import { initEccLib } from '@exodus/bitcoinjs-lib'
2
- import { Point, schnorr, sign } from '@noble/secp256k1'
3
- import { common, toPubKey } from './common'
4
-
5
- /**
6
- * Wrapper around `secp256k1` in order to follow the bitcoinjs-lib `TinySecp256k1Interface`
7
- * Schnorr signatures are offered by @noble/secp256k1
8
- */
9
- export const desktopEcc = {
10
- ...common,
11
-
12
- /**
13
- * @param message {Uint8Array}
14
- * @param privateKey {Uint8Array}
15
- * @param extraEntropy {Uint8Array}
16
- * @returns {Promise<Uint8Array>}
17
- */
18
- signAsync: async (message, privateKey, extraEntropy) =>
19
- sign(message, privateKey, {
20
- extraEntropy,
21
- canonical: true,
22
- der: false,
23
- }),
24
-
25
- /**
26
- * @param message {Uint8Array}
27
- * @param privateKey {Uint8Array}
28
- * @param extraEntropy {Uint8Array}
29
- * @returns {Promise<Uint8Array>}
30
- */
31
- signSchnorrAsync: async (message, privateKey, extraEntropy) =>
32
- schnorr.sign(message, privateKey, extraEntropy),
33
-
34
- /**
35
- * @param message {Uint8Array}
36
- * @param publicKey {Uint8Array}
37
- * @param signature {Uint8Array}
38
- * @returns {Promise<boolean>}
39
- */
40
- verifySchnorrAsync: async (message, publicKey, signature) =>
41
- schnorr.verify(signature, message, publicKey),
42
-
43
- // The underlying library does not expose sync functions for Schnorr sign and verify.
44
- // These function are explicitly defined here as `null` for documentation purposes.
45
- // Update, latest version of https://github.com/paulmillr/noble-secp256k1 does support SYNC
46
- signSchnorr: null,
47
- verifySchnorr: null,
48
-
49
- /**
50
- * @param publicKey {Uint8Array}
51
- * @returns {boolean}
52
- */
53
- isPoint: (publicKey) => {
54
- try {
55
- Point.fromHex(publicKey).assertValidity()
56
- return true
57
- } catch {
58
- return false
59
- }
60
- },
61
-
62
- /**
63
- * @param publicKey {Uint8Array}
64
- * @returns {boolean}
65
- */
66
- isXOnlyPoint: (publicKey) => {
67
- try {
68
- Point.fromHex(toPubKey(publicKey)).assertValidity()
69
- return true
70
- } catch {
71
- return false
72
- }
73
- },
74
-
75
- /**
76
- * @param publicKey {Uint8Array}
77
- * @param compressed {boolean}
78
- * @returns {Uint8Array}
79
- */
80
- pointCompress: (publicKey, compressed) => Point.fromHex(publicKey).toRawBytes(compressed),
81
- }
82
-
83
- initEccLib(desktopEcc)