@btc-vision/transaction 1.4.0 → 1.5.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.
Files changed (40) hide show
  1. package/browser/buffer/BinaryReader.d.ts +1 -1
  2. package/browser/index.js +1 -1
  3. package/browser/index.js.LICENSE.txt +1 -3
  4. package/browser/keypair/Address.d.ts +1 -0
  5. package/browser/keypair/AddressVerificator.d.ts +1 -0
  6. package/browser/keypair/EcKeyPair.d.ts +4 -1
  7. package/browser/transaction/TransactionFactory.d.ts +0 -1
  8. package/browser/transaction/browser/Web3Provider.d.ts +2 -0
  9. package/browser/transaction/builders/CustomScriptTransaction.d.ts +7 -4
  10. package/browser/transaction/builders/DeploymentTransaction.d.ts +3 -0
  11. package/build/buffer/BinaryReader.d.ts +1 -1
  12. package/build/buffer/BinaryReader.js +1 -1
  13. package/build/buffer/BinaryWriter.js +2 -2
  14. package/build/keypair/Address.d.ts +1 -0
  15. package/build/keypair/Address.js +15 -2
  16. package/build/keypair/AddressVerificator.d.ts +1 -0
  17. package/build/keypair/AddressVerificator.js +4 -0
  18. package/build/keypair/EcKeyPair.d.ts +4 -1
  19. package/build/keypair/EcKeyPair.js +48 -21
  20. package/build/transaction/TransactionFactory.d.ts +0 -1
  21. package/build/transaction/TransactionFactory.js +18 -4
  22. package/build/transaction/browser/Web3Provider.d.ts +2 -0
  23. package/build/transaction/builders/CustomScriptTransaction.d.ts +7 -4
  24. package/build/transaction/builders/CustomScriptTransaction.js +24 -3
  25. package/build/transaction/builders/DeploymentTransaction.d.ts +3 -0
  26. package/build/transaction/builders/DeploymentTransaction.js +9 -1
  27. package/build/transaction/builders/TransactionBuilder.js +3 -2
  28. package/build/transaction/shared/TweakedTransaction.js +2 -2
  29. package/package.json +18 -18
  30. package/src/buffer/BinaryReader.ts +2 -2
  31. package/src/buffer/BinaryWriter.ts +2 -2
  32. package/src/keypair/Address.ts +21 -0
  33. package/src/keypair/AddressVerificator.ts +6 -1
  34. package/src/keypair/EcKeyPair.ts +91 -34
  35. package/src/transaction/TransactionFactory.ts +21 -7
  36. package/src/transaction/browser/Web3Provider.ts +6 -0
  37. package/src/transaction/builders/CustomScriptTransaction.ts +38 -7
  38. package/src/transaction/builders/DeploymentTransaction.ts +17 -3
  39. package/src/transaction/builders/TransactionBuilder.ts +4 -3
  40. package/src/transaction/shared/TweakedTransaction.ts +2 -2
@@ -20,9 +20,7 @@
20
20
 
21
21
  /*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
22
22
 
23
- /*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
24
-
25
- /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
23
+ /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
26
24
 
27
25
  /*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
28
26
 
@@ -28,6 +28,7 @@ export declare class Address extends Uint8Array {
28
28
  toString(): string;
29
29
  toJSON(): string;
30
30
  p2tr(network: Network): string;
31
+ p2op(network: Network): string;
31
32
  toTweakedHybridPublicKeyHex(): string;
32
33
  toTweakedHybridPublicKeyBuffer(): Buffer;
33
34
  private autoFormat;
@@ -1,6 +1,7 @@
1
1
  import { Network } from '@btc-vision/bitcoin';
2
2
  export declare enum AddressTypes {
3
3
  P2PKH = "P2PKH",
4
+ P2OP = "P2OP",
4
5
  P2SH_OR_P2SH_P2WPKH = "P2SH_OR_P2SH-P2WPKH",
5
6
  P2PK = "P2PK",
6
7
  P2TR = "P2TR",
@@ -13,12 +13,15 @@ export declare class EcKeyPair {
13
13
  static getP2WPKHAddress(keyPair: ECPairInterface, network?: Network): string;
14
14
  static tweakedPubKeyToAddress(tweakedPubKeyHex: string, network: Network): string;
15
15
  static tweakedPubKeyBufferToAddress(tweakedPubKeyBuffer: Buffer | Uint8Array, network: Network): string;
16
+ static p2op(bytes: Buffer | Uint8Array, network?: Network, deploymentVersion?: number): string;
16
17
  static xOnlyTweakedPubKeyToAddress(tweakedPubKeyHex: string, network: Network): string;
17
- static tweakPublicKey(compressedPubKeyHex: string | Buffer): Buffer;
18
+ static tweakPublicKey(pub: Uint8Array | Buffer | string): Buffer;
19
+ static tweakBatchSharedT(pubkeys: readonly Uint8Array[], tweakScalar: bigint): Uint8Array[];
18
20
  static generateWallet(network?: Network): IWallet;
19
21
  static verifyContractAddress(contractAddress: string, network?: Network): boolean;
20
22
  static getLegacySegwitAddress(keyPair: ECPairInterface, network?: Network): string;
21
23
  static getLegacyAddress(keyPair: ECPairInterface, network?: Network): string;
24
+ static getP2PKH(publicKey: Buffer | Uint8Array, network?: Network): string;
22
25
  static getP2PKAddress(keyPair: ECPairInterface, network?: Network): string;
23
26
  static generateRandomKeyPair(network?: Network): ECPairInterface;
24
27
  static fromSeed(seed: Buffer, network?: Network): BIP32Interface;
@@ -11,7 +11,6 @@ export interface DeploymentResult {
11
11
  readonly transaction: [string, string];
12
12
  readonly contractAddress: string;
13
13
  readonly contractPubKey: string;
14
- readonly p2trAddress: string;
15
14
  readonly preimage: string;
16
15
  readonly utxos: UTXO[];
17
16
  }
@@ -1,8 +1,10 @@
1
1
  import { IDeploymentParameters, IInteractionParameters } from '../interfaces/ITransactionParameters.js';
2
2
  import { UTXO } from '../../utxo/interfaces/IUTXO.js';
3
3
  import { DeploymentResult, InteractionResponse } from '../TransactionFactory';
4
+ import { ICustomTransactionParameters } from '../builders/CustomScriptTransaction.js';
4
5
  export type InteractionParametersWithoutSigner = Omit<IInteractionParameters, 'signer' | 'preimage'>;
5
6
  export type IDeploymentParametersWithoutSigner = Omit<IDeploymentParameters, 'signer' | 'network' | 'preimage'>;
7
+ export type CustomTransactionWithoutSigner = Omit<ICustomTransactionParameters, 'signer' | 'preimage'>;
6
8
  export interface BroadcastTransactionOptions {
7
9
  raw: string;
8
10
  psbt: boolean;
@@ -1,12 +1,13 @@
1
- import { Payment, Psbt, Stack } from '@btc-vision/bitcoin';
1
+ import { Payment, Psbt, PsbtInput, Stack } from '@btc-vision/bitcoin';
2
2
  import { TransactionType } from '../enums/TransactionType.js';
3
3
  import { TapLeafScript } from '../interfaces/Tap.js';
4
4
  import { SharedInteractionParameters } from '../interfaces/ITransactionParameters.js';
5
5
  import { TransactionBuilder } from './TransactionBuilder.js';
6
6
  export interface ICustomTransactionParameters extends SharedInteractionParameters {
7
- readonly script: (Buffer | Stack)[];
8
- readonly witnesses: Buffer[];
9
- readonly to: string;
7
+ script: (Buffer | Stack)[];
8
+ witnesses: Buffer[];
9
+ annex?: Buffer;
10
+ to: string;
10
11
  }
11
12
  export declare class CustomScriptTransaction extends TransactionBuilder<TransactionType.CUSTOM_CODE> {
12
13
  type: TransactionType.CUSTOM_CODE;
@@ -21,6 +22,7 @@ export declare class CustomScriptTransaction extends TransactionBuilder<Transact
21
22
  private readonly contractSigner;
22
23
  private readonly randomBytes;
23
24
  private readonly witnesses;
25
+ private readonly annexData?;
24
26
  constructor(parameters: ICustomTransactionParameters);
25
27
  get scriptAddress(): string;
26
28
  get p2trAddress(): string;
@@ -30,6 +32,7 @@ export declare class CustomScriptTransaction extends TransactionBuilder<Transact
30
32
  protected signInputs(transaction: Psbt): Promise<void>;
31
33
  protected generateScriptAddress(): Payment;
32
34
  protected generateTapData(): Payment;
35
+ protected getScriptSolution(input: PsbtInput): Buffer[];
33
36
  private getContractSeed;
34
37
  private customFinalizer;
35
38
  private getPubKeys;
@@ -12,6 +12,7 @@ export declare class DeploymentTransaction extends TransactionBuilder<Transactio
12
12
  protected readonly rewardChallenge: IMineableReward;
13
13
  protected readonly _contractAddress: Address;
14
14
  protected tapLeafScript: TapLeafScript | null;
15
+ private readonly deploymentVersion;
15
16
  private targetScriptRedeem;
16
17
  private leftOverFundsScriptRedeem;
17
18
  private readonly compiledTargetScript;
@@ -23,12 +24,14 @@ export declare class DeploymentTransaction extends TransactionBuilder<Transactio
23
24
  private readonly contractSigner;
24
25
  private readonly _contractPubKey;
25
26
  private readonly randomBytes;
27
+ private _computedAddress;
26
28
  constructor(parameters: IDeploymentParameters);
27
29
  get contractPubKey(): string;
28
30
  get contractAddress(): Address;
29
31
  get p2trAddress(): string;
30
32
  getRndBytes(): Buffer;
31
33
  getPreimage(): Buffer;
34
+ getContractAddress(): string;
32
35
  protected contractSignerXOnlyPubKey(): Buffer;
33
36
  protected buildTransaction(): Promise<void>;
34
37
  protected signInputsWalletBased(transaction: Psbt): Promise<void>;
@@ -21,7 +21,7 @@ export declare class BinaryReader {
21
21
  readBoolean(): boolean;
22
22
  readSelector(): Selector;
23
23
  readBytes(length: u32, zeroStop?: boolean): Uint8Array;
24
- readString(length: u16): string;
24
+ readString(length: u32): string;
25
25
  readStringWithLength(be?: boolean): string;
26
26
  readAddress(): Address;
27
27
  readBytesWithLength(maxLength?: number, be?: boolean): Uint8Array;
@@ -112,7 +112,7 @@ export class BinaryReader {
112
112
  return textDecoder.decode(bytes);
113
113
  }
114
114
  readStringWithLength(be = true) {
115
- const length = this.readU16(be);
115
+ const length = this.readU32(be);
116
116
  return this.readString(length);
117
117
  }
118
118
  readAddress() {
@@ -119,8 +119,8 @@ export class BinaryWriter {
119
119
  this.writeBytes(value);
120
120
  }
121
121
  writeStringWithLength(value) {
122
- this.allocSafe(U16_BYTE_LENGTH + value.length);
123
- this.writeU16(value.length);
122
+ this.allocSafe(U32_BYTE_LENGTH + value.length);
123
+ this.writeU32(value.length);
124
124
  this.writeString(value);
125
125
  }
126
126
  getBuffer(clear = true) {
@@ -28,6 +28,7 @@ export declare class Address extends Uint8Array {
28
28
  toString(): string;
29
29
  toJSON(): string;
30
30
  p2tr(network: Network): string;
31
+ p2op(network: Network): string;
31
32
  toTweakedHybridPublicKeyHex(): string;
32
33
  toTweakedHybridPublicKeyBuffer(): Buffer;
33
34
  private autoFormat;
@@ -9,7 +9,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
10
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
11
  };
12
- var _Address_p2tr, _Address_network, _Address_originalPublicKey, _Address_keyPair, _Address_uncompressed, _Address_tweakedUncompressed;
12
+ var _Address_p2tr, _Address_p2op, _Address_network, _Address_originalPublicKey, _Address_keyPair, _Address_uncompressed, _Address_tweakedUncompressed;
13
13
  import { decompressPublicKey, toXOnly } from '@btc-vision/bitcoin';
14
14
  import { ADDRESS_BYTE_LENGTH } from '../utils/lengths.js';
15
15
  import { AddressVerificator } from './AddressVerificator.js';
@@ -20,6 +20,7 @@ export class Address extends Uint8Array {
20
20
  constructor(bytes) {
21
21
  super(ADDRESS_BYTE_LENGTH);
22
22
  _Address_p2tr.set(this, void 0);
23
+ _Address_p2op.set(this, void 0);
23
24
  _Address_network.set(this, void 0);
24
25
  _Address_originalPublicKey.set(this, void 0);
25
26
  _Address_keyPair.set(this, void 0);
@@ -193,6 +194,18 @@ export class Address extends Uint8Array {
193
194
  }
194
195
  throw new Error('Public key not set');
195
196
  }
197
+ p2op(network) {
198
+ if (__classPrivateFieldGet(this, _Address_p2op, "f") && __classPrivateFieldGet(this, _Address_network, "f") === network) {
199
+ return __classPrivateFieldGet(this, _Address_p2op, "f");
200
+ }
201
+ const p2opAddy = EcKeyPair.p2op(this, network);
202
+ if (p2opAddy) {
203
+ __classPrivateFieldSet(this, _Address_network, network, "f");
204
+ __classPrivateFieldSet(this, _Address_p2op, p2opAddy, "f");
205
+ return p2opAddy;
206
+ }
207
+ throw new Error('Public key not set');
208
+ }
196
209
  toTweakedHybridPublicKeyHex() {
197
210
  if (!__classPrivateFieldGet(this, _Address_tweakedUncompressed, "f")) {
198
211
  throw new Error('Public key not set');
@@ -220,4 +233,4 @@ export class Address extends Uint8Array {
220
233
  super.set(tweakedBytes);
221
234
  }
222
235
  }
223
- _Address_p2tr = new WeakMap(), _Address_network = new WeakMap(), _Address_originalPublicKey = new WeakMap(), _Address_keyPair = new WeakMap(), _Address_uncompressed = new WeakMap(), _Address_tweakedUncompressed = new WeakMap();
236
+ _Address_p2tr = new WeakMap(), _Address_p2op = new WeakMap(), _Address_network = new WeakMap(), _Address_originalPublicKey = new WeakMap(), _Address_keyPair = new WeakMap(), _Address_uncompressed = new WeakMap(), _Address_tweakedUncompressed = new WeakMap();
@@ -1,6 +1,7 @@
1
1
  import { Network } from '@btc-vision/bitcoin';
2
2
  export declare enum AddressTypes {
3
3
  P2PKH = "P2PKH",
4
+ P2OP = "P2OP",
4
5
  P2SH_OR_P2SH_P2WPKH = "P2SH_OR_P2SH-P2WPKH",
5
6
  P2PK = "P2PK",
6
7
  P2TR = "P2TR",
@@ -6,6 +6,7 @@ initEccLib(ecc);
6
6
  export var AddressTypes;
7
7
  (function (AddressTypes) {
8
8
  AddressTypes["P2PKH"] = "P2PKH";
9
+ AddressTypes["P2OP"] = "P2OP";
9
10
  AddressTypes["P2SH_OR_P2SH_P2WPKH"] = "P2SH_OR_P2SH-P2WPKH";
10
11
  AddressTypes["P2PK"] = "P2PK";
11
12
  AddressTypes["P2TR"] = "P2TR";
@@ -102,6 +103,9 @@ export class AddressVerificator {
102
103
  catch { }
103
104
  try {
104
105
  const decodedBech32 = address.fromBech32(addy);
106
+ if (decodedBech32.prefix === network.bech32Opnet && decodedBech32.version === 16) {
107
+ return AddressTypes.P2OP;
108
+ }
105
109
  if (decodedBech32.prefix === network.bech32) {
106
110
  if (decodedBech32.version === 0 && decodedBech32.data.length === 20) {
107
111
  return AddressTypes.P2WPKH;
@@ -13,12 +13,15 @@ export declare class EcKeyPair {
13
13
  static getP2WPKHAddress(keyPair: ECPairInterface, network?: Network): string;
14
14
  static tweakedPubKeyToAddress(tweakedPubKeyHex: string, network: Network): string;
15
15
  static tweakedPubKeyBufferToAddress(tweakedPubKeyBuffer: Buffer | Uint8Array, network: Network): string;
16
+ static p2op(bytes: Buffer | Uint8Array, network?: Network, deploymentVersion?: number): string;
16
17
  static xOnlyTweakedPubKeyToAddress(tweakedPubKeyHex: string, network: Network): string;
17
- static tweakPublicKey(compressedPubKeyHex: string | Buffer): Buffer;
18
+ static tweakPublicKey(pub: Uint8Array | Buffer | string): Buffer;
19
+ static tweakBatchSharedT(pubkeys: readonly Uint8Array[], tweakScalar: bigint): Uint8Array[];
18
20
  static generateWallet(network?: Network): IWallet;
19
21
  static verifyContractAddress(contractAddress: string, network?: Network): boolean;
20
22
  static getLegacySegwitAddress(keyPair: ECPairInterface, network?: Network): string;
21
23
  static getLegacyAddress(keyPair: ECPairInterface, network?: Network): string;
24
+ static getP2PKH(publicKey: Buffer | Uint8Array, network?: Network): string;
22
25
  static getP2PKAddress(keyPair: ECPairInterface, network?: Network): string;
23
26
  static generateRandomKeyPair(network?: Network): ECPairInterface;
24
27
  static fromSeed(seed: Buffer, network?: Network): BIP32Interface;
@@ -1,17 +1,23 @@
1
1
  import * as ecc from '@bitcoinerlab/secp256k1';
2
2
  import bip32, { BIP32Factory } from 'bip32';
3
- import { address, initEccLib, networks, payments, taggedHash, toXOnly, } from '@btc-vision/bitcoin';
3
+ import bitcoin, { address, fromOutputScript, initEccLib, networks, opcodes, payments, script, toXOnly, } from '@btc-vision/bitcoin';
4
4
  import { ECPairFactory } from 'ecpair';
5
- import { CURVE, ProjectivePoint as Point } from '@noble/secp256k1';
5
+ import { secp256k1 } from '@noble/curves/secp256k1';
6
+ import { bytesToNumberBE, concatBytes, utf8ToBytes } from '@noble/curves/abstract/utils';
7
+ import { mod } from '@noble/curves/abstract/modular';
8
+ import { sha256 } from '@noble/hashes/sha2';
6
9
  initEccLib(ecc);
7
10
  const BIP32factory = typeof bip32 === 'function' ? bip32 : BIP32Factory;
8
11
  if (!BIP32factory) {
9
12
  throw new Error('Failed to load BIP32 library');
10
13
  }
11
- const mod = (a, b) => {
12
- const result = a % b;
13
- return result >= 0n ? result : result + b;
14
- };
14
+ secp256k1.utils.precompute(8);
15
+ const { ProjectivePoint: Point, CURVE } = secp256k1;
16
+ const TAP_TAG = utf8ToBytes('TapTweak');
17
+ const TAP_TAG_HASH = sha256(TAP_TAG);
18
+ function tapTweakHash(x) {
19
+ return sha256(concatBytes(TAP_TAG_HASH, TAP_TAG_HASH, x));
20
+ }
15
21
  export class EcKeyPair {
16
22
  static fromWIF(wif, network = networks.bitcoin) {
17
23
  return this.ECPair.fromWIF(wif, network);
@@ -77,6 +83,17 @@ export class EcKeyPair {
77
83
  }
78
84
  return address;
79
85
  }
86
+ static p2op(bytes, network = networks.bitcoin, deploymentVersion = 0) {
87
+ const witnessProgram = Buffer.concat([
88
+ Buffer.from([deploymentVersion]),
89
+ bitcoin.crypto.hash160(Buffer.from(bytes)),
90
+ ]);
91
+ if (witnessProgram.length < 2 || witnessProgram.length > 40) {
92
+ throw new Error('Witness program must be 2-40 bytes.');
93
+ }
94
+ const scriptData = script.compile([opcodes.OP_16, witnessProgram]);
95
+ return fromOutputScript(scriptData, network);
96
+ }
80
97
  static xOnlyTweakedPubKeyToAddress(tweakedPubKeyHex, network) {
81
98
  if (tweakedPubKeyHex.startsWith('0x')) {
82
99
  tweakedPubKeyHex = tweakedPubKeyHex.slice(2);
@@ -91,23 +108,26 @@ export class EcKeyPair {
91
108
  }
92
109
  return address;
93
110
  }
94
- static tweakPublicKey(compressedPubKeyHex) {
95
- if (typeof compressedPubKeyHex === 'string' && compressedPubKeyHex.startsWith('0x')) {
96
- compressedPubKeyHex = compressedPubKeyHex.slice(2);
97
- }
98
- if (typeof compressedPubKeyHex !== 'string') {
99
- compressedPubKeyHex = compressedPubKeyHex.toString('hex');
100
- }
101
- let P = Point.fromHex(compressedPubKeyHex);
102
- if ((P.y & 1n) !== 0n) {
103
- P = P.negate();
104
- }
105
- const x = P.toRawBytes(true).slice(1);
106
- const tHash = taggedHash('TapTweak', Buffer.from(x));
107
- const t = mod(BigInt('0x' + Buffer.from(tHash).toString('hex')), CURVE.n);
108
- const Q = P.add(Point.BASE.mul(t));
111
+ static tweakPublicKey(pub) {
112
+ if (typeof pub === 'string' && pub.startsWith('0x'))
113
+ pub = pub.slice(2);
114
+ const P = Point.fromHex(pub);
115
+ const Peven = (P.y & 1n) === 0n ? P : P.negate();
116
+ const xBytes = Peven.toRawBytes(true).subarray(1);
117
+ const tBytes = tapTweakHash(xBytes);
118
+ const t = mod(bytesToNumberBE(tBytes), CURVE.n);
119
+ const Q = Peven.add(Point.BASE.multiply(t));
109
120
  return Buffer.from(Q.toRawBytes(true));
110
121
  }
122
+ static tweakBatchSharedT(pubkeys, tweakScalar) {
123
+ const T = Point.BASE.multiply(tweakScalar);
124
+ return pubkeys.map((bytes) => {
125
+ const P = Point.fromHex(bytes);
126
+ const P_even = P.hasEvenY() ? P : P.negate();
127
+ const Q = P_even.add(T);
128
+ return Q.toRawBytes(true);
129
+ });
130
+ }
111
131
  static generateWallet(network = networks.bitcoin) {
112
132
  const keyPair = this.ECPair.makeRandom({
113
133
  network: network,
@@ -142,6 +162,13 @@ export class EcKeyPair {
142
162
  }
143
163
  return wallet.address;
144
164
  }
165
+ static getP2PKH(publicKey, network = networks.bitcoin) {
166
+ const wallet = payments.p2pkh({ pubkey: Buffer.from(publicKey), network: network });
167
+ if (!wallet.address) {
168
+ throw new Error('Failed to generate wallet');
169
+ }
170
+ return wallet.address;
171
+ }
145
172
  static getP2PKAddress(keyPair, network = networks.bitcoin) {
146
173
  const wallet = payments.p2pk({ pubkey: Buffer.from(keyPair.publicKey), network: network });
147
174
  if (!wallet.output) {
@@ -11,7 +11,6 @@ export interface DeploymentResult {
11
11
  readonly transaction: [string, string];
12
12
  readonly contractAddress: string;
13
13
  readonly contractPubKey: string;
14
- readonly p2trAddress: string;
15
14
  readonly preimage: string;
16
15
  readonly utxos: UTXO[];
17
16
  }
@@ -13,9 +13,17 @@ export class TransactionFactory {
13
13
  if (!interactionParameters.from) {
14
14
  throw new Error('Field "from" not provided.');
15
15
  }
16
+ if (!interactionParameters.utxos[0]) {
17
+ throw new Error('Missing at least one UTXO.');
18
+ }
19
+ if (!('signer' in interactionParameters)) {
20
+ throw new Error('Field "signer" not provided, OP_WALLET not detected.');
21
+ }
22
+ const inputs = this.parseOptionalInputs(interactionParameters.optionalInputs);
16
23
  const preTransaction = new CustomScriptTransaction({
17
24
  ...interactionParameters,
18
25
  utxos: [interactionParameters.utxos[0]],
26
+ optionalInputs: inputs,
19
27
  });
20
28
  await preTransaction.generateTransactionMinimalSignatures();
21
29
  const parameters = await preTransaction.getFundingTransactionParameters();
@@ -33,17 +41,24 @@ export class TransactionFactory {
33
41
  throw new Error('Could not sign funding transaction.');
34
42
  }
35
43
  parameters.estimatedFees = feeEstimationFundingTransaction.estimatedFees;
36
- const signedTransaction = await this.createFundTransaction(parameters);
44
+ const signedTransaction = await this.createFundTransaction({
45
+ ...parameters,
46
+ optionalOutputs: [],
47
+ optionalInputs: [],
48
+ });
37
49
  if (!signedTransaction) {
38
50
  throw new Error('Could not sign funding transaction.');
39
51
  }
40
52
  interactionParameters.utxos = this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0);
41
53
  const newParams = {
42
54
  ...interactionParameters,
43
- utxos: this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0),
55
+ utxos: [
56
+ ...this.getUTXOAsTransaction(signedTransaction.tx, interactionParameters.to, 0),
57
+ ],
44
58
  randomBytes: preTransaction.getRndBytes(),
45
59
  nonWitnessUtxo: signedTransaction.tx.toBuffer(),
46
60
  estimatedFees: preTransaction.estimatedFees,
61
+ optionalInputs: inputs,
47
62
  };
48
63
  const finalTransaction = new CustomScriptTransaction(newParams);
49
64
  const outTx = await finalTransaction.signTransaction();
@@ -194,9 +209,8 @@ export class TransactionFactory {
194
209
  };
195
210
  return {
196
211
  transaction: [signedTransaction.toHex(), outTx.toHex()],
197
- contractAddress: finalTransaction.contractAddress.p2tr(deploymentParameters.network),
212
+ contractAddress: finalTransaction.getContractAddress(),
198
213
  contractPubKey: finalTransaction.contractPubKey,
199
- p2trAddress: finalTransaction.p2trAddress,
200
214
  utxos: [refundUTXO],
201
215
  preimage: preTransaction.getPreimage().toString('hex'),
202
216
  };
@@ -1,8 +1,10 @@
1
1
  import { IDeploymentParameters, IInteractionParameters } from '../interfaces/ITransactionParameters.js';
2
2
  import { UTXO } from '../../utxo/interfaces/IUTXO.js';
3
3
  import { DeploymentResult, InteractionResponse } from '../TransactionFactory';
4
+ import { ICustomTransactionParameters } from '../builders/CustomScriptTransaction.js';
4
5
  export type InteractionParametersWithoutSigner = Omit<IInteractionParameters, 'signer' | 'preimage'>;
5
6
  export type IDeploymentParametersWithoutSigner = Omit<IDeploymentParameters, 'signer' | 'network' | 'preimage'>;
7
+ export type CustomTransactionWithoutSigner = Omit<ICustomTransactionParameters, 'signer' | 'preimage'>;
6
8
  export interface BroadcastTransactionOptions {
7
9
  raw: string;
8
10
  psbt: boolean;
@@ -1,12 +1,13 @@
1
- import { Payment, Psbt, Stack } from '@btc-vision/bitcoin';
1
+ import { Payment, Psbt, PsbtInput, Stack } from '@btc-vision/bitcoin';
2
2
  import { TransactionType } from '../enums/TransactionType.js';
3
3
  import { TapLeafScript } from '../interfaces/Tap.js';
4
4
  import { SharedInteractionParameters } from '../interfaces/ITransactionParameters.js';
5
5
  import { TransactionBuilder } from './TransactionBuilder.js';
6
6
  export interface ICustomTransactionParameters extends SharedInteractionParameters {
7
- readonly script: (Buffer | Stack)[];
8
- readonly witnesses: Buffer[];
9
- readonly to: string;
7
+ script: (Buffer | Stack)[];
8
+ witnesses: Buffer[];
9
+ annex?: Buffer;
10
+ to: string;
10
11
  }
11
12
  export declare class CustomScriptTransaction extends TransactionBuilder<TransactionType.CUSTOM_CODE> {
12
13
  type: TransactionType.CUSTOM_CODE;
@@ -21,6 +22,7 @@ export declare class CustomScriptTransaction extends TransactionBuilder<Transact
21
22
  private readonly contractSigner;
22
23
  private readonly randomBytes;
23
24
  private readonly witnesses;
25
+ private readonly annexData?;
24
26
  constructor(parameters: ICustomTransactionParameters);
25
27
  get scriptAddress(): string;
26
28
  get p2trAddress(): string;
@@ -30,6 +32,7 @@ export declare class CustomScriptTransaction extends TransactionBuilder<Transact
30
32
  protected signInputs(transaction: Psbt): Promise<void>;
31
33
  protected generateScriptAddress(): Payment;
32
34
  protected generateTapData(): Payment;
35
+ protected getScriptSolution(input: PsbtInput): Buffer[];
33
36
  private getContractSeed;
34
37
  private customFinalizer;
35
38
  private getPubKeys;
@@ -12,14 +12,20 @@ export class CustomScriptTransaction extends TransactionBuilder {
12
12
  this.tapLeafScript = null;
13
13
  this.targetScriptRedeem = null;
14
14
  this.leftOverFundsScriptRedeem = null;
15
- this.customFinalizer = (_inputIndex, _input) => {
15
+ this.customFinalizer = (_inputIndex, input) => {
16
16
  if (!this.tapLeafScript) {
17
17
  throw new Error('Tap leaf script is required');
18
18
  }
19
- const scriptSolution = this.witnesses;
19
+ const scriptSolution = this.getScriptSolution(input);
20
20
  const witness = scriptSolution
21
21
  .concat(this.tapLeafScript.script)
22
22
  .concat(this.tapLeafScript.controlBlock);
23
+ if (this.annexData && this.annexData.length > 0) {
24
+ const annex = this.annexData[0] === 0x50
25
+ ? this.annexData
26
+ : Buffer.concat([Buffer.from([0x50]), this.annexData]);
27
+ witness.push(annex);
28
+ }
23
29
  return {
24
30
  finalScriptWitness: TransactionBuilder.witnessStackToScriptWitness(witness),
25
31
  };
@@ -86,7 +92,10 @@ export class CustomScriptTransaction extends TransactionBuilder {
86
92
  }
87
93
  for (let i = 0; i < transaction.data.inputs.length; i++) {
88
94
  if (i === 0) {
89
- transaction.signInput(0, this.contractSigner);
95
+ try {
96
+ transaction.signInput(0, this.contractSigner);
97
+ }
98
+ catch (e) { }
90
99
  transaction.signInput(0, this.getSignerKey());
91
100
  transaction.finalizeInput(0, this.customFinalizer);
92
101
  }
@@ -120,6 +129,18 @@ export class CustomScriptTransaction extends TransactionBuilder {
120
129
  redeem: selectedRedeem,
121
130
  };
122
131
  }
132
+ getScriptSolution(input) {
133
+ if (!input.tapScriptSig) {
134
+ throw new Error('Tap script signature is required');
135
+ }
136
+ const witnesses = [...this.witnesses];
137
+ if (input.tapScriptSig) {
138
+ for (const sig of input.tapScriptSig) {
139
+ witnesses.push(sig.signature);
140
+ }
141
+ }
142
+ return witnesses;
143
+ }
123
144
  getContractSeed() {
124
145
  return bitCrypto.hash256(this.randomBytes);
125
146
  }
@@ -12,6 +12,7 @@ export declare class DeploymentTransaction extends TransactionBuilder<Transactio
12
12
  protected readonly rewardChallenge: IMineableReward;
13
13
  protected readonly _contractAddress: Address;
14
14
  protected tapLeafScript: TapLeafScript | null;
15
+ private readonly deploymentVersion;
15
16
  private targetScriptRedeem;
16
17
  private leftOverFundsScriptRedeem;
17
18
  private readonly compiledTargetScript;
@@ -23,12 +24,14 @@ export declare class DeploymentTransaction extends TransactionBuilder<Transactio
23
24
  private readonly contractSigner;
24
25
  private readonly _contractPubKey;
25
26
  private readonly randomBytes;
27
+ private _computedAddress;
26
28
  constructor(parameters: IDeploymentParameters);
27
29
  get contractPubKey(): string;
28
30
  get contractAddress(): Address;
29
31
  get p2trAddress(): string;
30
32
  getRndBytes(): Buffer;
31
33
  getPreimage(): Buffer;
34
+ getContractAddress(): string;
32
35
  protected contractSignerXOnlyPubKey(): Buffer;
33
36
  protected buildTransaction(): Promise<void>;
34
37
  protected signInputsWalletBased(transaction: Psbt): Promise<void>;
@@ -14,6 +14,7 @@ export class DeploymentTransaction extends TransactionBuilder {
14
14
  super(parameters);
15
15
  this.type = TransactionType.DEPLOYMENT;
16
16
  this.tapLeafScript = null;
17
+ this.deploymentVersion = 0x00;
17
18
  this.targetScriptRedeem = null;
18
19
  this.leftOverFundsScriptRedeem = null;
19
20
  this.customFinalizer = (_inputIndex, input) => {
@@ -70,6 +71,13 @@ export class DeploymentTransaction extends TransactionBuilder {
70
71
  getPreimage() {
71
72
  return this.preimage;
72
73
  }
74
+ getContractAddress() {
75
+ if (this._computedAddress) {
76
+ return this._computedAddress;
77
+ }
78
+ this._computedAddress = EcKeyPair.p2op(this.contractSeed, this.network, this.deploymentVersion);
79
+ return this._computedAddress;
80
+ }
73
81
  contractSignerXOnlyPubKey() {
74
82
  return toXOnly(Buffer.from(this.contractSigner.publicKey));
75
83
  }
@@ -105,7 +113,7 @@ export class DeploymentTransaction extends TransactionBuilder {
105
113
  }
106
114
  this.addOutput({
107
115
  value: Number(amountToCA),
108
- address: this.contractAddress.p2tr(this.network),
116
+ address: this.getContractAddress(),
109
117
  });
110
118
  if (amountToCA === MINIMUM_AMOUNT_CA &&
111
119
  amountSpent - MINIMUM_AMOUNT_CA > MINIMUM_AMOUNT_REWARD) {
@@ -5,7 +5,7 @@ import { AddressVerificator } from '../../keypair/AddressVerificator.js';
5
5
  import { TweakedTransaction } from '../shared/TweakedTransaction.js';
6
6
  initEccLib(ecc);
7
7
  export const MINIMUM_AMOUNT_REWARD = 540n;
8
- export const MINIMUM_AMOUNT_CA = 330n;
8
+ export const MINIMUM_AMOUNT_CA = 297n;
9
9
  export class TransactionBuilder extends TweakedTransaction {
10
10
  constructor(parameters) {
11
11
  super(parameters);
@@ -386,6 +386,7 @@ export class TransactionBuilder extends TweakedTransaction {
386
386
  }
387
387
  }
388
388
  TransactionBuilder.LOCK_LEAF_SCRIPT = script.compile([
389
- opcodes.OP_0,
389
+ opcodes.OP_FALSE,
390
+ opcodes.OP_VERIFY,
390
391
  ]);
391
392
  TransactionBuilder.MINIMUM_DUST = 50n;
@@ -47,8 +47,8 @@ export class TweakedTransaction extends Logger {
47
47
  }
48
48
  function readVarInt() {
49
49
  const varint = varuint.decode(Buffer, offset);
50
- offset += varuint.decode.bytes;
51
- return varint;
50
+ offset += varint.bytes;
51
+ return varint.numberValue || 0;
52
52
  }
53
53
  function readVarSlice() {
54
54
  const len = readVarInt();