@btc-vision/transaction 1.0.112 → 1.0.113

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.
Files changed (47) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/index.js +1 -1
  3. package/browser/keypair/AddressVerificator.d.ts +15 -3
  4. package/browser/keypair/EcKeyPair.d.ts +7 -4
  5. package/browser/keypair/Wallet.d.ts +1 -0
  6. package/browser/signer/TweakedSigner.d.ts +2 -2
  7. package/browser/transaction/builders/FundingTransaction.d.ts +2 -1
  8. package/browser/transaction/builders/MultiSignTransaction.d.ts +2 -1
  9. package/browser/transaction/builders/SharedInteractionTransaction.d.ts +1 -1
  10. package/browser/transaction/builders/TransactionBuilder.d.ts +2 -2
  11. package/browser/transaction/shared/TweakedTransaction.d.ts +8 -7
  12. package/build/_version.d.ts +1 -1
  13. package/build/_version.js +1 -1
  14. package/build/keypair/AddressVerificator.d.ts +15 -3
  15. package/build/keypair/AddressVerificator.js +74 -3
  16. package/build/keypair/EcKeyPair.d.ts +7 -4
  17. package/build/keypair/EcKeyPair.js +54 -9
  18. package/build/keypair/Wallet.d.ts +1 -0
  19. package/build/keypair/Wallet.js +5 -4
  20. package/build/signer/TweakedSigner.d.ts +2 -2
  21. package/build/signer/TweakedSigner.js +1 -1
  22. package/build/transaction/builders/CustomScriptTransaction.js +3 -3
  23. package/build/transaction/builders/DeploymentTransaction.js +4 -4
  24. package/build/transaction/builders/FundingTransaction.d.ts +2 -1
  25. package/build/transaction/builders/MultiSignTransaction.d.ts +2 -1
  26. package/build/transaction/builders/SharedInteractionTransaction.d.ts +1 -1
  27. package/build/transaction/builders/SharedInteractionTransaction.js +4 -4
  28. package/build/transaction/builders/TransactionBuilder.d.ts +2 -2
  29. package/build/transaction/builders/UnwrapTransaction.js +2 -2
  30. package/build/transaction/shared/TweakedTransaction.d.ts +8 -7
  31. package/build/transaction/shared/TweakedTransaction.js +1 -1
  32. package/package.json +2 -1
  33. package/src/_version.ts +1 -1
  34. package/src/keypair/AddressVerificator.ts +144 -5
  35. package/src/keypair/EcKeyPair.ts +111 -14
  36. package/src/keypair/Wallet.ts +11 -3
  37. package/src/signer/TweakedSigner.ts +3 -2
  38. package/src/transaction/builders/CustomScriptTransaction.ts +6 -5
  39. package/src/transaction/builders/DeploymentTransaction.ts +6 -5
  40. package/src/transaction/builders/FundingTransaction.ts +2 -1
  41. package/src/transaction/builders/MultiSignTransaction.ts +6 -2
  42. package/src/transaction/builders/SharedInteractionTransaction.ts +5 -5
  43. package/src/transaction/builders/TransactionBuilder.ts +18 -8
  44. package/src/transaction/builders/UnwrapSegwitTransaction.ts +7 -3
  45. package/src/transaction/builders/UnwrapTransaction.ts +2 -2
  46. package/src/transaction/builders/WrapTransaction.ts +1 -6
  47. package/src/transaction/shared/TweakedTransaction.ts +12 -12
@@ -32,7 +32,7 @@ export class SharedInteractionTransaction extends TransactionBuilder {
32
32
  this.calldata = Compressor.compress(parameters.calldata);
33
33
  this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
34
34
  this.scriptSigner = this.generateKeyPairFromSeed();
35
- this.calldataGenerator = new CalldataGenerator(this.signer.publicKey, this.scriptSignerXOnlyPubKey(), this.network);
35
+ this.calldataGenerator = new CalldataGenerator(Buffer.from(this.signer.publicKey), this.scriptSignerXOnlyPubKey(), this.network);
36
36
  }
37
37
  getContractSecret() {
38
38
  return this.contractSecret;
@@ -46,7 +46,7 @@ export class SharedInteractionTransaction extends TransactionBuilder {
46
46
  return address.fromBech32(this.to).data;
47
47
  }
48
48
  scriptSignerXOnlyPubKey() {
49
- return toXOnly(this.scriptSigner.publicKey);
49
+ return toXOnly(Buffer.from(this.scriptSigner.publicKey));
50
50
  }
51
51
  generateKeyPairFromSeed() {
52
52
  return EcKeyPair.fromSeedKeyPair(this.randomBytes, this.network);
@@ -188,9 +188,9 @@ export class SharedInteractionTransaction extends TransactionBuilder {
188
188
  throw new Error('Failed to sign input');
189
189
  }
190
190
  getPubKeys() {
191
- const pubkeys = [this.signer.publicKey];
191
+ const pubkeys = [Buffer.from(this.signer.publicKey)];
192
192
  if (this.scriptSigner) {
193
- pubkeys.push(this.scriptSigner.publicKey);
193
+ pubkeys.push(Buffer.from(this.scriptSigner.publicKey));
194
194
  }
195
195
  return pubkeys;
196
196
  }
@@ -20,7 +20,7 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
20
20
  protected readonly outputs: PsbtOutputExtended[];
21
21
  protected feeOutput: PsbtOutputExtended | null;
22
22
  protected totalInputAmount: bigint;
23
- protected readonly signer: Signer;
23
+ protected readonly signer: Signer | ECPairInterface;
24
24
  protected readonly network: Network;
25
25
  protected readonly feeRate: number;
26
26
  protected priorityFee: bigint;
@@ -30,7 +30,7 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
30
30
  protected _maximumFeeRate: number;
31
31
  protected isPubKeyDestination: boolean;
32
32
  protected constructor(parameters: ITransactionParameters);
33
- static getFrom(from: string | undefined, keypair: ECPairInterface, network: Network): Address;
33
+ static getFrom(from: string | undefined, keypair: ECPairInterface | Signer, network: Network): Address;
34
34
  static witnessStackToScriptWitness(witness: Buffer[]): Buffer;
35
35
  getFundingTransactionParameters(): Promise<IFundingTransactionParameters>;
36
36
  setDestinationAddress(address: Address): void;
@@ -33,7 +33,7 @@ export class UnwrapTransaction extends SharedInteractionTransaction {
33
33
  this.estimatedFeeLoss = UnwrapTransaction.preEstimateTaprootTransactionFees(BigInt(this.feeRate), this.calculateNumInputs(this.vaultUTXOs), 2n, this.calculateNumSignatures(this.vaultUTXOs), 65n, this.calculateNumEmptyWitnesses(this.vaultUTXOs));
34
34
  this.amount = parameters.amount;
35
35
  this.contractSecret = this.generateSecret();
36
- this.calldataGenerator = new CalldataGenerator(this.signer.publicKey, this.scriptSignerXOnlyPubKey(), this.network);
36
+ this.calldataGenerator = new CalldataGenerator(Buffer.from(this.signer.publicKey), this.scriptSignerXOnlyPubKey(), this.network);
37
37
  this.compiledTargetScript = this.calldataGenerator.compile(this.calldata, this.contractSecret, [Features.UNWRAP]);
38
38
  this.scriptTree = this.getScriptTree();
39
39
  this.internalInit();
@@ -173,7 +173,7 @@ export class UnwrapTransaction extends SharedInteractionTransaction {
173
173
  }
174
174
  return [
175
175
  this.contractSecret,
176
- toXOnly(this.signer.publicKey),
176
+ toXOnly(Buffer.from(this.signer.publicKey)),
177
177
  input.tapScriptSig[0].signature,
178
178
  input.tapScriptSig[1].signature,
179
179
  ];
@@ -1,11 +1,12 @@
1
1
  import { Logger } from '@btc-vision/logger';
2
2
  import { Network, Payment, Psbt, Signer, Transaction } from 'bitcoinjs-lib';
3
+ import { ECPairInterface } from 'ecpair';
3
4
  import { PsbtInput } from 'bip174/src/lib/interfaces.js';
4
5
  import { UTXO } from '../../utxo/interfaces/IUTXO.js';
5
6
  import { PsbtInputExtended, TapLeafScript } from '../interfaces/Tap.js';
6
7
  import { ChainId } from '../../network/ChainId.js';
7
8
  export interface ITweakedTransactionData {
8
- readonly signer: Signer;
9
+ readonly signer: Signer | ECPairInterface;
9
10
  readonly network: Network;
10
11
  readonly chainId?: ChainId;
11
12
  readonly nonWitnessUtxo?: Buffer;
@@ -17,8 +18,8 @@ export declare enum TransactionSequence {
17
18
  export declare abstract class TweakedTransaction extends Logger {
18
19
  readonly logColor: string;
19
20
  finalized: boolean;
20
- protected signer: Signer;
21
- protected tweakedSigner?: Signer;
21
+ protected signer: Signer | ECPairInterface;
22
+ protected tweakedSigner?: ECPairInterface;
22
23
  protected network: Network;
23
24
  protected signed: boolean;
24
25
  protected abstract readonly transaction: Psbt;
@@ -35,7 +36,7 @@ export declare abstract class TweakedTransaction extends Logger {
35
36
  protected constructor(data: ITweakedTransactionData);
36
37
  static readScriptWitnessToWitnessStack(Buffer: Buffer): Buffer[];
37
38
  static preEstimateTaprootTransactionFees(feeRate: bigint, numInputs: bigint, numOutputs: bigint, numWitnessElements: bigint, witnessElementSize: bigint, emptyWitness: bigint, taprootControlWitnessSize?: bigint, taprootScriptSize?: bigint): bigint;
38
- protected static signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer, sighashTypes: number[]): void;
39
+ protected static signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface, sighashTypes: number[]): void;
39
40
  protected static calculateSignHash(sighashTypes: number[]): number;
40
41
  ignoreSignatureError(): void;
41
42
  getScriptAddress(): string;
@@ -46,13 +47,13 @@ export declare abstract class TweakedTransaction extends Logger {
46
47
  preEstimateTransactionFees(feeRate: bigint, numInputs: bigint, numOutputs: bigint, numSignatures: bigint, numPubkeys: bigint): bigint;
47
48
  protected generateTapData(): Payment;
48
49
  protected generateScriptAddress(): Payment;
49
- protected getSignerKey(): Signer;
50
- protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer?: Signer): Promise<void>;
50
+ protected getSignerKey(): Signer | ECPairInterface;
51
+ protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer?: Signer | ECPairInterface): Promise<void>;
51
52
  protected splitArray<T>(arr: T[], chunkSize: number): T[][];
52
53
  protected signInputs(transaction: Psbt): Promise<void>;
53
54
  protected internalPubKeyToXOnly(): Buffer;
54
55
  protected internalInit(): void;
55
56
  protected tweakSigner(): void;
56
- protected getTweakedSigner(useTweakedHash?: boolean, signer?: Signer): Signer | undefined;
57
+ protected getTweakedSigner(useTweakedHash?: boolean, signer?: Signer | ECPairInterface): ECPairInterface | undefined;
57
58
  protected generatePsbtInputExtended(utxo: UTXO, i: number): PsbtInputExtended;
58
59
  }
@@ -232,7 +232,7 @@ export class TweakedTransaction extends Logger {
232
232
  }
233
233
  }
234
234
  internalPubKeyToXOnly() {
235
- return toXOnly(this.signer.publicKey);
235
+ return toXOnly(Buffer.from(this.signer.publicKey));
236
236
  }
237
237
  internalInit() {
238
238
  this.scriptData = payments.p2tr(this.generateScriptAddress());
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@btc-vision/transaction",
3
3
  "type": "module",
4
- "version": "1.0.112",
4
+ "version": "1.0.113",
5
5
  "author": "BlobMaster41",
6
6
  "description": "OPNet transaction library allows you to create and sign transactions for the OPNet network.",
7
7
  "engines": {
@@ -95,6 +95,7 @@
95
95
  "@btc-vision/bsi-bitcoin-rpc": "^1.0.29",
96
96
  "@btc-vision/logger": "^1.0.6",
97
97
  "@eslint/js": "^9.10.0",
98
+ "@noble/secp256k1": "^1.7.1",
98
99
  "assert": "^2.1.0",
99
100
  "babel-loader": "^9.1.3",
100
101
  "babel-plugin-transform-import-meta": "^2.2.1",
package/src/_version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '1.0.111';
1
+ export const version = '1.0.113';
@@ -1,11 +1,29 @@
1
- import { address, initEccLib, networks } from 'bitcoinjs-lib';
1
+ import { address, initEccLib, Network } from 'bitcoinjs-lib';
2
2
  import * as ecc from '@bitcoinerlab/secp256k1';
3
3
  import { Address } from '@btc-vision/bsi-binary';
4
+ import { EcKeyPair } from './EcKeyPair.js';
4
5
 
5
6
  initEccLib(ecc);
6
7
 
8
+ export enum AddressTypes {
9
+ P2PKH = 'P2PKH',
10
+ P2SH = 'P2SH',
11
+ P2SH_P2WPKH = 'P2SH-P2WPKH',
12
+ P2SH_OR_P2SH_P2WPKH = 'P2SH_OR_P2SH-P2WPKH',
13
+ P2PK = 'P2PK',
14
+ P2TR = 'P2TR',
15
+ P2WPKH = 'P2WPKH',
16
+ }
17
+
7
18
  export class AddressVerificator {
8
- public static isValidP2TRAddress(inAddress: Address, network: networks.Network): boolean {
19
+ /**
20
+ * Checks if the given address is a valid P2PKH address.
21
+ * @param inAddress - The address to check.
22
+ * @param network - The network to validate against.
23
+ * @returns - True if the address is a valid P2PKH address, false otherwise.
24
+ * @remarks This method is useful for validating legacy addresses (P2PKH) without
25
+ */
26
+ public static isValidP2TRAddress(inAddress: Address, network: Network): boolean {
9
27
  if (!inAddress || inAddress.length < 50) return false;
10
28
 
11
29
  let isValidTapRootAddress: boolean = false;
@@ -14,12 +32,19 @@ export class AddressVerificator {
14
32
 
15
33
  const decodedAddress = address.fromBech32(inAddress);
16
34
  isValidTapRootAddress = decodedAddress.version === 1;
17
- } catch (e) {}
35
+ } catch {}
18
36
 
19
37
  return isValidTapRootAddress;
20
38
  }
21
39
 
22
- public static validatePKHAddress(inAddress: string, network: networks.Network): boolean {
40
+ /**
41
+ * Checks if the given address is a valid P2PKH address.
42
+ * @param inAddress - The address to check.
43
+ * @param network - The network to validate against.
44
+ * @returns - True if the address is a valid P2PKH address, false otherwise.
45
+ * @remarks This method is useful for validating legacy addresses (P2PKH) without
46
+ */
47
+ public static isP2WPKHAddress(inAddress: string, network: Network): boolean {
23
48
  if (!inAddress || inAddress.length < 20 || inAddress.length > 50) return false;
24
49
 
25
50
  let isValidSegWitAddress: boolean = false;
@@ -33,8 +58,122 @@ export class AddressVerificator {
33
58
  // Check if the address is P2WPKH (version 0)
34
59
  isValidSegWitAddress =
35
60
  decodedAddress.version === 0 && decodedAddress.data.length === 20;
36
- } catch (e) {}
61
+ } catch {}
37
62
 
38
63
  return isValidSegWitAddress;
39
64
  }
65
+
66
+ /**
67
+ * Checks if the given address is a valid P2PKH or P2SH address.
68
+ * @param addy - The address to check.
69
+ * @param network - The network to validate against.
70
+ * @returns - True if the address is a valid P2PKH or P2SH address, false otherwise.
71
+ * @remarks This method is useful for validating legacy addresses (P2PKH or P2SH) without
72
+ */
73
+ public static isP2PKHOrP2SH(addy: string, network: Network): boolean {
74
+ try {
75
+ // First, try to decode as a Base58Check address (P2PKH, P2SH, or P2SH-P2WPKH)
76
+ const decodedBase58 = address.fromBase58Check(addy);
77
+
78
+ if (decodedBase58.version === network.pubKeyHash) {
79
+ // P2PKH: Legacy address (starting with '1' for mainnet, 'm/n' for testnet)
80
+ return true;
81
+ }
82
+
83
+ return decodedBase58.version === network.scriptHash;
84
+ } catch (error) {
85
+ // If decoding fails or version is not valid, it's not a valid legacy address
86
+ return false;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Checks if the input is a valid hexadecimal public key (P2PK).
92
+ * Public keys can be compressed (66 characters) or uncompressed (130 characters).
93
+ *
94
+ * @param input - The input string to check.
95
+ * @param network - The Bitcoin network to validate against (mainnet, testnet, etc.).
96
+ * @returns - True if the input is a valid public key, false otherwise.
97
+ */
98
+ public static isValidPublicKey(input: string, network: Network): boolean {
99
+ try {
100
+ if (input.startsWith('0x')) {
101
+ input = input.slice(2);
102
+ }
103
+
104
+ // Compressed public keys are 66 characters long (0x02 or 0x03 prefix + 32 bytes)
105
+ // Uncompressed public keys are 130 characters long (0x04 prefix + 64 bytes)
106
+ const hexRegex = /^[0-9a-fA-F]+$/;
107
+
108
+ if ((input.length === 66 || input.length === 130) && hexRegex.test(input)) {
109
+ // Check if the input can be parsed as a valid public key
110
+ const pubKeyBuffer = Buffer.from(input, 'hex');
111
+ EcKeyPair.fromPublicKey(pubKeyBuffer, network);
112
+
113
+ return true;
114
+ }
115
+ } catch (error) {
116
+ console.log(error);
117
+
118
+ // If any error occurs (invalid public key, etc.), return false
119
+ return false;
120
+ }
121
+
122
+ return false; // Not a valid public key
123
+ }
124
+
125
+ /**
126
+ * Validates if a given Bitcoin address is of the specified type and network.
127
+ * - P2PKH (Legacy address starting with '1' for mainnet or 'm/n' for testnet)
128
+ * - P2SH (Legacy address starting with '3' for mainnet or '2' for testnet)
129
+ * - P2SH-P2WPKH (Wrapped SegWit)
130
+ * - P2PK (Pay to PubKey, technically treated similarly to P2PKH)
131
+ * - P2WPKH (SegWit address starting with 'bc1q' for mainnet or 'tb1q' for testnet)
132
+ * - P2TR (Taproot address starting with 'bc1p' for mainnet or 'tb1p' for testnet)
133
+ *
134
+ * @param addy - The Bitcoin address to validate.
135
+ * @param network - The Bitcoin network to validate against (mainnet, testnet, etc.).
136
+ * @returns - The type of the valid Bitcoin address, or null if invalid.
137
+ */
138
+ public static validateBitcoinAddress(addy: string, network: Network): AddressTypes | null {
139
+ if (AddressVerificator.isValidPublicKey(addy, network)) {
140
+ return AddressTypes.P2PK;
141
+ }
142
+
143
+ try {
144
+ // First, try to decode as a Base58Check address (P2PKH, P2SH, or P2SH-P2WPKH)
145
+ const decodedBase58 = address.fromBase58Check(addy);
146
+
147
+ if (decodedBase58.version === network.pubKeyHash) {
148
+ // P2PKH: Legacy address (starting with '1' for mainnet, 'm/n' for testnet)
149
+ return AddressTypes.P2PKH;
150
+ }
151
+ if (decodedBase58.version === network.scriptHash) {
152
+ // P2SH: Could be P2SH (general) or P2SH-P2WPKH (wrapped SegWit)
153
+ return AddressTypes.P2SH_OR_P2SH_P2WPKH;
154
+ }
155
+ } catch (error) {
156
+ // Ignore errors from Base58 decoding, it could be a Bech32 address
157
+ }
158
+
159
+ try {
160
+ // Try to decode as a Bech32 or Bech32m address (P2WPKH or P2TR)
161
+ const decodedBech32 = address.fromBech32(addy);
162
+
163
+ if (decodedBech32.prefix === network.bech32) {
164
+ // P2WPKH: SegWit address (starting with 'bc1q' for mainnet, 'tb1q' for testnet)
165
+ if (decodedBech32.version === 0 && decodedBech32.data.length === 20) {
166
+ return AddressTypes.P2WPKH;
167
+ }
168
+ // P2TR: Taproot address (starting with 'bc1p' for mainnet, 'tb1p' for testnet)
169
+ if (decodedBech32.version === 1 && decodedBech32.data.length === 32) {
170
+ return AddressTypes.P2TR;
171
+ }
172
+ }
173
+ } catch (error) {
174
+ // Ignore errors from Bech32/Bech32m decoding
175
+ }
176
+
177
+ return null; // Not a valid or recognized Bitcoin address type
178
+ }
40
179
  }
@@ -1,10 +1,12 @@
1
1
  import * as ecc from '@bitcoinerlab/secp256k1';
2
2
  import { Address } from '@btc-vision/bsi-binary';
3
3
  import bip32, { BIP32API, BIP32Factory, BIP32Interface } from 'bip32';
4
- import { address, initEccLib, Network, networks, payments } from 'bitcoinjs-lib';
4
+ import { address, initEccLib, Network, networks, payments, Signer } from 'bitcoinjs-lib';
5
5
  import { toXOnly } from 'bitcoinjs-lib/src/psbt/bip371.js';
6
6
  import { ECPairAPI, ECPairFactory, ECPairInterface } from 'ecpair';
7
7
  import { IWallet } from './interfaces/IWallet.js';
8
+ import { CURVE, Point, utils } from '@noble/secp256k1';
9
+ import { taggedHash } from 'bitcoinjs-lib/src/crypto.js';
8
10
 
9
11
  initEccLib(ecc);
10
12
 
@@ -42,23 +44,28 @@ export class EcKeyPair {
42
44
  * @returns {ECPairInterface} - The generated keypair
43
45
  */
44
46
  public static fromPrivateKey(
45
- privateKey: Buffer,
47
+ privateKey: Buffer | Uint8Array,
46
48
  network: Network = networks.bitcoin,
47
49
  ): ECPairInterface {
48
- return this.ECPair.fromPrivateKey(privateKey, { network });
50
+ return this.ECPair.fromPrivateKey(
51
+ !Buffer.isBuffer(privateKey) ? Buffer.from(privateKey) : privateKey,
52
+ { network },
53
+ );
49
54
  }
50
55
 
51
56
  /**
52
57
  * Generate a keypair from a public key
53
- * @param {Buffer} publicKey - The public key to use
58
+ * @param {Buffer | Uint8Array} publicKey - The public key to use
54
59
  * @param {Network} network - The network to use
55
60
  * @returns {ECPairInterface} - The generated keypair
56
61
  */
57
62
  public static fromPublicKey(
58
- publicKey: Buffer,
63
+ publicKey: Buffer | Uint8Array,
59
64
  network: Network = networks.bitcoin,
60
65
  ): ECPairInterface {
61
- return this.ECPair.fromPublicKey(publicKey, { network });
66
+ const buf = !Buffer.isBuffer(publicKey) ? Buffer.from(publicKey) : publicKey;
67
+
68
+ return this.ECPair.fromPublicKey(buf, { network });
62
69
  }
63
70
 
64
71
  /**
@@ -122,7 +129,7 @@ export class EcKeyPair {
122
129
  throw new Error('Failed to regenerate key');
123
130
  }
124
131
 
125
- return key.publicKey;
132
+ return Buffer.from(key.publicKey);
126
133
  });
127
134
  }
128
135
 
@@ -136,7 +143,7 @@ export class EcKeyPair {
136
143
  keyPair: ECPairInterface,
137
144
  network: Network = networks.bitcoin,
138
145
  ): Address {
139
- const res = payments.p2wpkh({ pubkey: keyPair.publicKey, network: network });
146
+ const res = payments.p2wpkh({ pubkey: Buffer.from(keyPair.publicKey), network: network });
140
147
 
141
148
  if (!res.address) {
142
149
  throw new Error('Failed to generate wallet');
@@ -145,6 +152,96 @@ export class EcKeyPair {
145
152
  return res.address;
146
153
  }
147
154
 
155
+ /**
156
+ * Get the address of a tweaked public key
157
+ * @param {string} tweakedPubKeyHex - The tweaked public key hex string
158
+ * @param {Network} network - The network to use
159
+ * @returns {Address} - The address
160
+ * @throws {Error} - If the address cannot be generated
161
+ */
162
+ public static tweakedPubKeyToAddress(tweakedPubKeyHex: string, network: Network): string {
163
+ if (tweakedPubKeyHex.startsWith('0x')) {
164
+ tweakedPubKeyHex = tweakedPubKeyHex.slice(2);
165
+ }
166
+
167
+ // Convert the tweaked public key hex string to a Buffer
168
+ const tweakedPubKeyBuffer = Buffer.from(tweakedPubKeyHex, 'hex');
169
+
170
+ // Generate the Taproot address using the p2tr payment method
171
+ const { address } = payments.p2tr({
172
+ pubkey: toXOnly(tweakedPubKeyBuffer),
173
+ network: network,
174
+ });
175
+
176
+ if (!address) {
177
+ throw new Error('Failed to generate Taproot address');
178
+ }
179
+
180
+ return address;
181
+ }
182
+
183
+ /**
184
+ * Get the address of a xOnly tweaked public key
185
+ * @param {string} tweakedPubKeyHex - The xOnly tweaked public key hex string
186
+ * @param {Network} network - The network to use
187
+ * @returns {Address} - The address
188
+ * @throws {Error} - If the address cannot be generated
189
+ */
190
+ public static xOnlyTweakedPubKeyToAddress(tweakedPubKeyHex: string, network: Network): string {
191
+ if (tweakedPubKeyHex.startsWith('0x')) {
192
+ tweakedPubKeyHex = tweakedPubKeyHex.slice(2);
193
+ }
194
+
195
+ // Convert the tweaked public key hex string to a Buffer
196
+ const tweakedPubKeyBuffer = Buffer.from(tweakedPubKeyHex, 'hex');
197
+
198
+ // Generate the Taproot address using the p2tr payment method
199
+ const { address } = payments.p2tr({
200
+ pubkey: tweakedPubKeyBuffer,
201
+ network: network,
202
+ });
203
+
204
+ if (!address) {
205
+ throw new Error('Failed to generate Taproot address');
206
+ }
207
+
208
+ return address;
209
+ }
210
+
211
+ /**
212
+ * Tweak a public key
213
+ * @param {string} compressedPubKeyHex - The compressed public key hex string
214
+ * @returns {string} - The tweaked public key hex string
215
+ * @throws {Error} - If the public key cannot be tweaked
216
+ */
217
+ public static tweakPublicKey(compressedPubKeyHex: string): string {
218
+ if (compressedPubKeyHex.startsWith('0x')) {
219
+ compressedPubKeyHex = compressedPubKeyHex.slice(2);
220
+ }
221
+
222
+ // Convert the compressed public key hex string to a Point on the curve
223
+ let P = Point.fromHex(compressedPubKeyHex);
224
+
225
+ // Ensure the point has an even y-coordinate
226
+ if (!P.hasEvenY()) {
227
+ // Negate the point to get an even y-coordinate
228
+ P = P.negate();
229
+ }
230
+
231
+ // Get the x-coordinate (32 bytes) of the point
232
+ const x = P.toRawBytes(true).slice(1); // Remove the prefix byte
233
+
234
+ // Compute the tweak t = H_tapTweak(x)
235
+ const tHash = taggedHash('TapTweak', Buffer.from(x));
236
+ const t = utils.mod(BigInt('0x' + Buffer.from(tHash).toString('hex')), CURVE.n);
237
+
238
+ // Compute Q = P + t*G (where G is the generator point)
239
+ const Q = P.add(Point.BASE.multiply(t));
240
+
241
+ // Return the tweaked public key in compressed form (hex string)
242
+ return Q.toHex(true);
243
+ }
244
+
148
245
  /**
149
246
  * Generate a random wallet
150
247
  * @param {Network} network - The network to use
@@ -164,7 +261,7 @@ export class EcKeyPair {
164
261
  return {
165
262
  address: wallet,
166
263
  privateKey: keyPair.toWIF(),
167
- publicKey: keyPair.publicKey.toString('hex'),
264
+ publicKey: Buffer.from(keyPair.publicKey).toString('hex'),
168
265
  };
169
266
  }
170
267
 
@@ -192,7 +289,7 @@ export class EcKeyPair {
192
289
  network: Network = networks.bitcoin,
193
290
  ): Address {
194
291
  const wallet = payments.p2sh({
195
- redeem: payments.p2wpkh({ pubkey: keyPair.publicKey, network: network }),
292
+ redeem: payments.p2wpkh({ pubkey: Buffer.from(keyPair.publicKey), network: network }),
196
293
  network: network,
197
294
  });
198
295
 
@@ -213,7 +310,7 @@ export class EcKeyPair {
213
310
  keyPair: ECPairInterface,
214
311
  network: Network = networks.bitcoin,
215
312
  ): Address {
216
- const wallet = payments.p2pkh({ pubkey: keyPair.publicKey, network: network });
313
+ const wallet = payments.p2pkh({ pubkey: Buffer.from(keyPair.publicKey), network: network });
217
314
  if (!wallet.address) {
218
315
  throw new Error('Failed to generate wallet');
219
316
  }
@@ -231,7 +328,7 @@ export class EcKeyPair {
231
328
  keyPair: ECPairInterface,
232
329
  network: Network = networks.bitcoin,
233
330
  ): Address {
234
- const wallet = payments.p2pk({ pubkey: keyPair.publicKey, network: network });
331
+ const wallet = payments.p2pk({ pubkey: Buffer.from(keyPair.publicKey), network: network });
235
332
  if (!wallet.output) {
236
333
  throw new Error('Failed to generate wallet');
237
334
  }
@@ -267,11 +364,11 @@ export class EcKeyPair {
267
364
  * @returns {Address} - The taproot address
268
365
  */
269
366
  public static getTaprootAddress(
270
- keyPair: ECPairInterface,
367
+ keyPair: ECPairInterface | Signer,
271
368
  network: Network = networks.bitcoin,
272
369
  ): Address {
273
370
  const { address } = payments.p2tr({
274
- internalPubkey: toXOnly(keyPair.publicKey),
371
+ internalPubkey: toXOnly(Buffer.from(keyPair.publicKey)),
275
372
  network: network,
276
373
  });
277
374
 
@@ -33,6 +33,12 @@ export class Wallet {
33
33
  */
34
34
  private readonly _legacy: Address;
35
35
 
36
+ /**
37
+ * Buffer public key
38
+ * @private
39
+ */
40
+ private readonly _bufferPubKey: Buffer;
41
+
36
42
  constructor(
37
43
  wallet: IWallet,
38
44
  public readonly network: Network = networks.bitcoin,
@@ -42,6 +48,8 @@ export class Wallet {
42
48
  this._p2wpkh = EcKeyPair.getP2WPKHAddress(this._keypair, this.network);
43
49
  this._p2tr = EcKeyPair.getTaprootAddress(this._keypair, this.network);
44
50
  this._legacy = EcKeyPair.getLegacyAddress(this._keypair, this.network);
51
+
52
+ this._bufferPubKey = Buffer.from(wallet.publicKey, 'hex');
45
53
  }
46
54
 
47
55
  /**
@@ -92,9 +100,9 @@ export class Wallet {
92
100
  * @returns {Buffer}
93
101
  */
94
102
  public get publicKey(): Buffer {
95
- if (!this.keypair) throw new Error('Keypair not set');
103
+ if (!this._bufferPubKey) throw new Error('Public key not set');
96
104
 
97
- return this.keypair.publicKey;
105
+ return this._bufferPubKey;
98
106
  }
99
107
 
100
108
  /**
@@ -105,7 +113,7 @@ export class Wallet {
105
113
  public get xOnly(): Buffer {
106
114
  if (!this.keypair) throw new Error('Keypair not set');
107
115
 
108
- return toXOnly(this.keypair.publicKey);
116
+ return toXOnly(this._bufferPubKey);
109
117
  }
110
118
 
111
119
  /**
@@ -31,8 +31,9 @@ export class TweakedSigner {
31
31
  * Tweak a signer
32
32
  * @param {Signer} signer - The signer to tweak
33
33
  * @param {TweakSettings} opts - The tweak settings
34
+ * @returns {ECPairInterface} - The tweaked signer
34
35
  */
35
- public static tweakSigner(signer: ECPairInterface, opts: TweakSettings = {}): Signer {
36
+ public static tweakSigner(signer: ECPairInterface, opts: TweakSettings = {}): ECPairInterface {
36
37
  let privateKey: Uint8Array | undefined = signer.privateKey;
37
38
  if (!privateKey) {
38
39
  throw new Error('Private key is required for tweaking signer!');
@@ -44,7 +45,7 @@ export class TweakedSigner {
44
45
 
45
46
  const tweakedPrivateKey = ecc.privateAdd(
46
47
  privateKey,
47
- tapTweakHash(toXOnly(signer.publicKey), opts.tweakHash),
48
+ tapTweakHash(toXOnly(Buffer.from(signer.publicKey)), opts.tweakHash),
48
49
  );
49
50
 
50
51
  if (!tweakedPrivateKey) {
@@ -11,6 +11,7 @@ import { EcKeyPair } from '../../keypair/EcKeyPair.js';
11
11
  import { AddressGenerator } from '../../generators/AddressGenerator.js';
12
12
  import { toXOnly } from 'bitcoinjs-lib/src/psbt/bip371.js';
13
13
  import { PsbtInput } from 'bip174/src/lib/interfaces.js';
14
+ import { ECPairInterface } from 'ecpair';
14
15
 
15
16
  export interface ICustomTransactionParameters extends SharedInteractionParameters {
16
17
  readonly script: (Buffer | Stack)[];
@@ -72,7 +73,7 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
72
73
  * The contract signer
73
74
  * @private
74
75
  */
75
- private readonly contractSigner: Signer;
76
+ private readonly contractSigner: Signer | ECPairInterface;
76
77
 
77
78
  /**
78
79
  * The contract salt random bytes
@@ -136,7 +137,7 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
136
137
  * @protected
137
138
  */
138
139
  protected contractSignerXOnlyPubKey(): Buffer {
139
- return toXOnly(this.contractSigner.publicKey);
140
+ return toXOnly(Buffer.from(this.contractSigner.publicKey));
140
141
  }
141
142
 
142
143
  /**
@@ -261,7 +262,7 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
261
262
  if (!this.tapLeafScript) {
262
263
  throw new Error('Tap leaf script is required');
263
264
  }
264
-
265
+
265
266
  const scriptSolution = this.witnesses;
266
267
  const witness = scriptSolution
267
268
  .concat(this.tapLeafScript.script)
@@ -277,10 +278,10 @@ export class CustomScriptTransaction extends TransactionBuilder<TransactionType.
277
278
  * @private
278
279
  */
279
280
  private getPubKeys(): Buffer[] {
280
- const pubkeys = [this.signer.publicKey];
281
+ const pubkeys = [Buffer.from(this.signer.publicKey)];
281
282
 
282
283
  if (this.contractSigner) {
283
- pubkeys.push(this.contractSigner.publicKey);
284
+ pubkeys.push(Buffer.from(this.contractSigner.publicKey));
284
285
  }
285
286
 
286
287
  return pubkeys;