@btc-vision/transaction 1.3.4 → 1.3.6

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 (46) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/generators/Features.d.ts +9 -4
  3. package/browser/generators/Generator.d.ts +5 -2
  4. package/browser/generators/builders/CalldataGenerator.d.ts +2 -2
  5. package/browser/generators/builders/LegacyCalldataGenerator.d.ts +2 -2
  6. package/browser/index.js +1 -1
  7. package/browser/opnet.d.ts +7 -0
  8. package/browser/transaction/TransactionFactory.d.ts +2 -0
  9. package/browser/transaction/browser/extensions/UnisatSigner.d.ts +3 -5
  10. package/browser/transaction/builders/InteractionTransaction.d.ts +1 -0
  11. package/browser/transaction/interfaces/ITransactionParameters.d.ts +4 -0
  12. package/build/_version.d.ts +1 -1
  13. package/build/_version.js +1 -1
  14. package/build/generators/Features.d.ts +9 -4
  15. package/build/generators/Features.js +1 -5
  16. package/build/generators/Generator.d.ts +5 -2
  17. package/build/generators/Generator.js +40 -5
  18. package/build/generators/builders/CalldataGenerator.d.ts +2 -2
  19. package/build/generators/builders/CalldataGenerator.js +10 -4
  20. package/build/generators/builders/DeploymentGenerator.js +13 -2
  21. package/build/generators/builders/LegacyCalldataGenerator.d.ts +2 -2
  22. package/build/generators/builders/LegacyCalldataGenerator.js +10 -4
  23. package/build/opnet.d.ts +7 -0
  24. package/build/transaction/TransactionFactory.d.ts +2 -0
  25. package/build/transaction/TransactionFactory.js +65 -18
  26. package/build/transaction/browser/extensions/UnisatSigner.d.ts +3 -5
  27. package/build/transaction/builders/DeploymentTransaction.js +12 -2
  28. package/build/transaction/builders/InteractionTransaction.d.ts +1 -0
  29. package/build/transaction/builders/InteractionTransaction.js +12 -1
  30. package/build/transaction/interfaces/ITransactionParameters.d.ts +4 -0
  31. package/build/transaction/shared/TweakedTransaction.js +3 -3
  32. package/eslint.config.js +1 -0
  33. package/package.json +1 -1
  34. package/src/_version.ts +1 -1
  35. package/src/generators/Features.ts +10 -5
  36. package/src/generators/Generator.ts +54 -5
  37. package/src/generators/builders/CalldataGenerator.ts +16 -9
  38. package/src/generators/builders/DeploymentGenerator.ts +17 -1
  39. package/src/generators/builders/LegacyCalldataGenerator.ts +14 -6
  40. package/src/opnet.ts +9 -0
  41. package/src/transaction/TransactionFactory.ts +95 -31
  42. package/src/transaction/browser/extensions/UnisatSigner.ts +4 -6
  43. package/src/transaction/builders/DeploymentTransaction.ts +12 -3
  44. package/src/transaction/builders/InteractionTransaction.ts +15 -0
  45. package/src/transaction/interfaces/ITransactionParameters.ts +7 -1
  46. package/src/transaction/shared/TweakedTransaction.ts +3 -4
@@ -1,5 +1,8 @@
1
1
  import { Network, networks, toXOnly } from '@btc-vision/bitcoin';
2
2
  import { BinaryWriter } from '../buffer/BinaryWriter.js';
3
+ import { AccessListFeature, Feature, Features } from './Features.js';
4
+ import { Address } from '../keypair/Address.js';
5
+ import { Compressor } from '../bytecode/Compressor.js';
3
6
 
4
7
  /** Bitcoin Script Generator */
5
8
  export abstract class Generator {
@@ -48,13 +51,22 @@ export abstract class Generator {
48
51
  this.xSenderPubKey = toXOnly(senderPubKey);
49
52
  }
50
53
 
51
- public get senderFirstByte(): Buffer {
52
- return Buffer.from([this.senderPubKey[0], 0, 0, 0]);
54
+ public buildHeader(features: Features[]): Buffer {
55
+ let flags: number = 0;
56
+
57
+ for (const feature of features) {
58
+ flags |= feature;
59
+ }
60
+
61
+ const bytesU24 = Buffer.alloc(3);
62
+ bytesU24.writeUIntBE(flags, 0, 3);
63
+
64
+ return Buffer.from([this.senderPubKey[0], ...bytesU24]);
53
65
  }
54
66
 
55
- public getHeader(maxPriority: bigint): Buffer {
56
- const writer = new BinaryWriter(8 + 4);
57
- writer.writeBytes(this.senderFirstByte);
67
+ public getHeader(maxPriority: bigint, features: Features[] = []): Buffer {
68
+ const writer = new BinaryWriter(12);
69
+ writer.writeBytes(this.buildHeader(features));
58
70
  writer.writeU64(maxPriority);
59
71
 
60
72
  return Buffer.from(writer.getBuffer());
@@ -92,4 +104,41 @@ export abstract class Generator {
92
104
 
93
105
  return chunks;
94
106
  }
107
+
108
+ protected encodeFeature(feature: Feature<Features>): Buffer[][] {
109
+ switch (feature.opcode) {
110
+ case Features.ACCESS_LIST:
111
+ return this.splitBufferIntoChunks(
112
+ this.encodeAccessListFeature(feature as AccessListFeature),
113
+ );
114
+ default:
115
+ throw new Error(`Unknown feature type: ${feature.opcode}`);
116
+ }
117
+ }
118
+
119
+ private encodeAccessListFeature(feature: AccessListFeature): Buffer {
120
+ const writer = new BinaryWriter();
121
+
122
+ writer.writeU16(Object.keys(feature.data).length);
123
+
124
+ for (const contract in feature.data) {
125
+ const parsedContract = Address.fromString(contract);
126
+ const data = feature.data[contract];
127
+
128
+ writer.writeAddress(parsedContract);
129
+ writer.writeU32(data.length);
130
+
131
+ for (const pointer of data) {
132
+ const pointerBuffer = Buffer.from(pointer, 'base64');
133
+
134
+ if (pointerBuffer.length !== 32) {
135
+ throw new Error(`Invalid pointer length: ${pointerBuffer.length}`);
136
+ }
137
+
138
+ writer.writeBytes(pointerBuffer);
139
+ }
140
+ }
141
+
142
+ return Compressor.compress(Buffer.from(writer.getBuffer()));
143
+ }
95
144
  }
@@ -2,9 +2,8 @@ import { crypto, Network, networks, opcodes, script } from '@btc-vision/bitcoin'
2
2
  import { ECPairInterface } from 'ecpair';
3
3
  import { Compressor } from '../../bytecode/Compressor.js';
4
4
  import { EcKeyPair } from '../../keypair/EcKeyPair.js';
5
- import { FeatureOpCodes, Features } from '../Features.js';
5
+ import { Feature, Features } from '../Features.js';
6
6
  import { Generator } from '../Generator.js';
7
- import { BinaryWriter } from '../../buffer/BinaryWriter.js';
8
7
 
9
8
  /**
10
9
  * Class to generate bitcoin script for interaction transactions
@@ -59,7 +58,7 @@ export class CalldataGenerator extends Generator {
59
58
  * @param {Buffer} contractSecret - The contract secret
60
59
  * @param preimage
61
60
  * @param maxPriority - Amount of satoshis to spend max on priority fee
62
- * @param {number[]} [features=[]] - The features to use (optional)
61
+ * @param {Feature<Features>[]} features - The features to use
63
62
  * @returns {Buffer} - The compiled script
64
63
  * @throws {Error} - If something goes wrong
65
64
  */
@@ -68,15 +67,25 @@ export class CalldataGenerator extends Generator {
68
67
  contractSecret: Buffer,
69
68
  preimage: Buffer,
70
69
  maxPriority: bigint,
71
- features: Features[] = [],
70
+ features: Feature<Features>[] = [],
72
71
  ): Buffer {
73
72
  if (!this.contractSaltPubKey) throw new Error('Contract salt public key not set');
74
73
 
75
74
  const dataChunks: Buffer[][] = this.splitBufferIntoChunks(calldata);
76
75
  if (!dataChunks.length) throw new Error('No data chunks found');
77
76
 
78
- let compiledData = [
79
- this.getHeader(maxPriority),
77
+ const featuresList: Features[] = [];
78
+ const featureData: (number | Buffer | Buffer[])[] = [];
79
+ for (let i = 0; i < features.length; i++) {
80
+ const feature = features[i];
81
+ featuresList.push(feature.opcode);
82
+
83
+ const data = this.encodeFeature(feature);
84
+ featureData.push(...data);
85
+ }
86
+
87
+ let compiledData: (number | Buffer | Buffer[])[] = [
88
+ this.getHeader(maxPriority, featuresList),
80
89
  opcodes.OP_TOALTSTACK,
81
90
 
82
91
  // CHALLENGE PREIMAGE FOR REWARD,
@@ -105,11 +114,9 @@ export class CalldataGenerator extends Generator {
105
114
  Generator.MAGIC,
106
115
  ];
107
116
 
108
- const featureOpcodes = features.map((feature) => FeatureOpCodes[feature]); // Get the opcodes for the features
109
-
110
117
  // Write calldata
111
118
  compiledData = compiledData.concat(
112
- ...featureOpcodes,
119
+ ...featureData,
113
120
  ...[opcodes.OP_1NEGATE, ...dataChunks, opcodes.OP_ELSE, opcodes.OP_1, opcodes.OP_ENDIF],
114
121
  );
115
122
 
@@ -1,5 +1,6 @@
1
1
  import { crypto, Network, networks, opcodes, script } from '@btc-vision/bitcoin';
2
2
  import { Generator } from '../Generator.js';
3
+ import { Feature, Features } from '../Features.js';
3
4
 
4
5
  export class DeploymentGenerator extends Generator {
5
6
  constructor(
@@ -46,14 +47,28 @@ export class DeploymentGenerator extends Generator {
46
47
  preimage: Buffer,
47
48
  maxPriority: bigint,
48
49
  calldata?: Buffer,
50
+ features?: Feature<Features>[],
49
51
  ): (number | Buffer)[] {
50
52
  if (!this.contractSaltPubKey) throw new Error('Contract salt public key not set');
51
53
 
52
54
  const dataChunks: Buffer[][] = this.splitBufferIntoChunks(contractBytecode);
53
55
  const calldataChunks: Buffer[][] = calldata ? this.splitBufferIntoChunks(calldata) : [];
54
56
 
57
+ const featuresList: Features[] = [];
58
+ const featureData: (number | Buffer | Buffer[])[] = [];
59
+
60
+ if (features) {
61
+ for (let i = 0; i < features.length; i++) {
62
+ const feature = features[i];
63
+ featuresList.push(feature.opcode);
64
+
65
+ const data = this.encodeFeature(feature);
66
+ featureData.push(...data);
67
+ }
68
+ }
69
+
55
70
  const compiledData = [
56
- this.getHeader(maxPriority),
71
+ this.getHeader(maxPriority, featuresList),
57
72
  opcodes.OP_TOALTSTACK,
58
73
 
59
74
  // CHALLENGE PREIMAGE FOR REWARD,
@@ -80,6 +95,7 @@ export class DeploymentGenerator extends Generator {
80
95
  opcodes.OP_IF,
81
96
 
82
97
  Generator.MAGIC,
98
+ ...featureData,
83
99
  opcodes.OP_0,
84
100
  ...calldataChunks,
85
101
  opcodes.OP_1NEGATE,
@@ -2,8 +2,8 @@ import { crypto, Network, networks, opcodes, script } from '@btc-vision/bitcoin'
2
2
  import { ECPairInterface } from 'ecpair';
3
3
  import { Compressor } from '../../bytecode/Compressor.js';
4
4
  import { EcKeyPair } from '../../keypair/EcKeyPair.js';
5
- import { FeatureOpCodes, Features } from '../Features.js';
6
5
  import { Generator } from '../Generator.js';
6
+ import { Feature, Features } from '../Features.js';
7
7
 
8
8
  /**
9
9
  * Class to generate bitcoin script for interaction transactions
@@ -63,13 +63,23 @@ export class LegacyCalldataGenerator extends Generator {
63
63
  contractSecret: Buffer,
64
64
  preimage: Buffer,
65
65
  maxPriority: bigint,
66
- features: Features[] = [],
66
+ features: Feature<Features>[] = [],
67
67
  ): Buffer {
68
68
  const dataChunks: Buffer[][] = this.splitBufferIntoChunks(calldata);
69
69
  if (!dataChunks.length) throw new Error('No data chunks found');
70
70
 
71
+ const featuresList: Features[] = [];
72
+ const featureData: (number | Buffer | Buffer[])[] = [];
73
+ for (let i = 0; i < features.length; i++) {
74
+ const feature = features[i];
75
+ featuresList.push(feature.opcode);
76
+
77
+ const data = this.encodeFeature(feature);
78
+ featureData.push(...data);
79
+ }
80
+
71
81
  let compiledData = [
72
- this.getHeader(maxPriority),
82
+ this.getHeader(maxPriority, featuresList),
73
83
  opcodes.OP_TOALTSTACK,
74
84
 
75
85
  // CHALLENGE PREIMAGE FOR REWARD,
@@ -94,11 +104,9 @@ export class LegacyCalldataGenerator extends Generator {
94
104
  Generator.MAGIC,
95
105
  ];
96
106
 
97
- const featureOpcodes = features.map((feature) => FeatureOpCodes[feature]); // Get the opcodes for the features
98
-
99
107
  // Write calldata
100
108
  compiledData = compiledData.concat(
101
- ...featureOpcodes,
109
+ ...featureData,
102
110
  ...[opcodes.OP_1NEGATE, ...dataChunks, opcodes.OP_ELSE, opcodes.OP_1, opcodes.OP_ENDIF],
103
111
  );
104
112
 
package/src/opnet.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Unisat } from './transaction/browser/types/Unisat.js';
2
+
1
3
  export { version } from './_version.js';
2
4
 
3
5
  /** Bytecode */
@@ -97,3 +99,10 @@ export * from './transaction/browser/Web3Provider.js';
97
99
 
98
100
  export * from './keypair/Secp256k1PointDeriver.js';
99
101
  export * from './transaction/ContractAddress.js';
102
+
103
+ declare global {
104
+ interface Window {
105
+ unisat?: Unisat;
106
+ opnet?: Unisat;
107
+ }
108
+ }
@@ -19,7 +19,11 @@ import {
19
19
  } from './interfaces/ITransactionParameters.js';
20
20
  import { PSBTTypes } from './psbt/PSBTTypes.js';
21
21
  import { ChallengeSolutionTransaction } from './builders/ChallengeSolutionTransaction.js';
22
- import { InteractionParametersWithoutSigner } from './browser/Web3Provider.js';
22
+ import {
23
+ IDeploymentParametersWithoutSigner,
24
+ InteractionParametersWithoutSigner,
25
+ } from './browser/Web3Provider.js';
26
+ import { WindowWithWallets } from './browser/extensions/UnisatSigner.js';
23
27
 
24
28
  export interface DeploymentResult {
25
29
  readonly transaction: [string, string];
@@ -174,26 +178,7 @@ export class TransactionFactory {
174
178
  throw new Error('Field "signer" not provided, OP_WALLET not detected.');
175
179
  }
176
180
 
177
- const inputs = (interactionParameters.optionalInputs || []).map((input) => {
178
- let nonWitness = input.nonWitnessUtxo;
179
- if (
180
- nonWitness &&
181
- !(nonWitness instanceof Uint8Array) &&
182
- typeof nonWitness === 'object'
183
- ) {
184
- nonWitness = Buffer.from(
185
- Uint8Array.from(
186
- Object.values(input.nonWitnessUtxo as unknown as Record<number, number>),
187
- ),
188
- );
189
- }
190
-
191
- return {
192
- ...input,
193
- nonWitnessUtxo: nonWitness,
194
- };
195
- });
196
-
181
+ const inputs = this.parseOptionalInputs(interactionParameters.optionalInputs);
197
182
  const preTransaction: InteractionTransaction = new InteractionTransaction({
198
183
  ...interactionParameters,
199
184
  utxos: [interactionParameters.utxos[0]], // we simulate one input here.
@@ -277,21 +262,46 @@ export class TransactionFactory {
277
262
  public async signDeployment(
278
263
  deploymentParameters: IDeploymentParameters,
279
264
  ): Promise<DeploymentResult> {
280
- const preTransaction: DeploymentTransaction = new DeploymentTransaction(
281
- deploymentParameters,
282
- );
265
+ const opWalletDeployment = await this.detectDeploymentOPWallet(deploymentParameters);
266
+ if (opWalletDeployment) {
267
+ return opWalletDeployment;
268
+ }
283
269
 
284
- // Initial generation
285
- await preTransaction.signTransaction();
270
+ if (!('signer' in deploymentParameters)) {
271
+ throw new Error('Field "signer" not provided, OP_WALLET not detected.');
272
+ }
273
+
274
+ const inputs = this.parseOptionalInputs(deploymentParameters.optionalInputs);
275
+ const preTransaction: DeploymentTransaction = new DeploymentTransaction({
276
+ ...deploymentParameters,
277
+ utxos: [deploymentParameters.utxos[0]], // we simulate one input here.
278
+ optionalInputs: inputs,
279
+ });
280
+
281
+ // we don't sign that transaction, we just need the parameters.
282
+ await preTransaction.generateTransactionMinimalSignatures();
286
283
 
287
284
  const parameters: IFundingTransactionParameters =
288
285
  await preTransaction.getFundingTransactionParameters();
289
286
 
287
+ parameters.utxos = deploymentParameters.utxos;
290
288
  parameters.amount =
291
289
  (await preTransaction.estimateTransactionFees()) +
292
290
  this.getPriorityFee(deploymentParameters) +
293
291
  preTransaction.getOptionalOutputValue();
294
292
 
293
+ const feeEstimationFundingTransaction = await this.createFundTransaction({
294
+ ...parameters,
295
+ optionalOutputs: [],
296
+ optionalInputs: [],
297
+ });
298
+
299
+ if (!feeEstimationFundingTransaction) {
300
+ throw new Error('Could not sign funding transaction.');
301
+ }
302
+
303
+ parameters.estimatedFees = feeEstimationFundingTransaction.estimatedFees;
304
+
295
305
  const fundingTransaction: FundingTransaction = new FundingTransaction({
296
306
  ...parameters,
297
307
  optionalInputs: [],
@@ -316,12 +326,12 @@ export class TransactionFactory {
316
326
 
317
327
  const newParams: IDeploymentParameters = {
318
328
  ...deploymentParameters,
319
- utxos: [newUtxo],
329
+ utxos: [newUtxo], // always 0
320
330
  randomBytes: preTransaction.getRndBytes(),
321
331
  preimage: preTransaction.getPreimage(),
322
332
  nonWitnessUtxo: signedTransaction.toBuffer(),
323
- optionalOutputs: [],
324
- optionalInputs: [],
333
+ estimatedFees: preTransaction.estimatedFees,
334
+ optionalInputs: inputs,
325
335
  };
326
336
 
327
337
  const finalTransaction: DeploymentTransaction = new DeploymentTransaction(newParams);
@@ -420,14 +430,41 @@ export class TransactionFactory {
420
430
  return utxos;
421
431
  }
422
432
 
433
+ private parseOptionalInputs(optionalInputs?: UTXO[]): UTXO[] {
434
+ return (optionalInputs || []).map((input) => {
435
+ let nonWitness = input.nonWitnessUtxo;
436
+ if (
437
+ nonWitness &&
438
+ !(nonWitness instanceof Uint8Array) &&
439
+ typeof nonWitness === 'object'
440
+ ) {
441
+ nonWitness = Buffer.from(
442
+ Uint8Array.from(
443
+ Object.values(input.nonWitnessUtxo as unknown as Record<number, number>),
444
+ ),
445
+ );
446
+ }
447
+
448
+ return {
449
+ ...input,
450
+ nonWitnessUtxo: nonWitness,
451
+ };
452
+ });
453
+ }
454
+
423
455
  private async detectInteractionOPWallet(
424
456
  interactionParameters: IInteractionParameters | InteractionParametersWithoutSigner,
425
457
  ): Promise<InteractionResponse | null> {
426
- if (typeof window === 'undefined' || !window || !window.opnet || !window.opnet.web3) {
458
+ if (typeof window === 'undefined') {
459
+ return null;
460
+ }
461
+
462
+ const _window = window as WindowWithWallets;
463
+ if (!_window || !_window.opnet || !_window.opnet.web3) {
427
464
  return null;
428
465
  }
429
466
 
430
- const opnet = window.opnet.web3;
467
+ const opnet = _window.opnet.web3;
431
468
  const interaction = await opnet.signInteraction({
432
469
  ...interactionParameters,
433
470
 
@@ -442,6 +479,33 @@ export class TransactionFactory {
442
479
  return interaction;
443
480
  }
444
481
 
482
+ private async detectDeploymentOPWallet(
483
+ deploymentParameters: IDeploymentParameters | IDeploymentParametersWithoutSigner,
484
+ ): Promise<DeploymentResult | null> {
485
+ if (typeof window === 'undefined') {
486
+ return null;
487
+ }
488
+
489
+ const _window = window as WindowWithWallets;
490
+ if (!_window || !_window.opnet || !_window.opnet.web3) {
491
+ return null;
492
+ }
493
+
494
+ const opnet = _window.opnet.web3;
495
+ const deployment = await opnet.deployContract({
496
+ ...deploymentParameters,
497
+
498
+ // @ts-expect-error no, this is ok
499
+ signer: undefined,
500
+ });
501
+
502
+ if (!deployment) {
503
+ throw new Error('Could not sign interaction transaction.');
504
+ }
505
+
506
+ return deployment;
507
+ }
508
+
445
509
  private async _createChallengeSolution(
446
510
  parameters: IChallengeSolutionTransactionParameters,
447
511
  ): Promise<ChallengeSolutionResponse> {
@@ -14,11 +14,9 @@ import { canSignNonTaprootInput, isTaprootInput } from '../../../signer/SignerUt
14
14
  import { CustomKeypair } from '../BrowserSignerBase.js';
15
15
  import { PsbtSignatureOptions, SignatureType, Unisat, UnisatNetwork } from '../types/Unisat.js';
16
16
 
17
- declare global {
18
- interface Window {
19
- unisat?: Unisat;
20
- opnet?: Unisat;
21
- }
17
+ export interface WindowWithWallets {
18
+ unisat?: Unisat;
19
+ opnet?: Unisat;
22
20
  }
23
21
 
24
22
  export class UnisatSigner extends CustomKeypair {
@@ -85,7 +83,7 @@ export class UnisatSigner extends CustomKeypair {
85
83
  public get unisat(): Unisat {
86
84
  if (!window) throw new Error('Window not found');
87
85
 
88
- const module = window.unisat;
86
+ const module = (window as WindowWithWallets).unisat;
89
87
  if (!module) {
90
88
  throw new Error('Unisat extension not found');
91
89
  }
@@ -118,7 +118,7 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
118
118
  this.verifyCalldata();
119
119
  }
120
120
 
121
- if(!parameters.preimage) throw new Error('Preimage is required');
121
+ if (!parameters.preimage) throw new Error('Preimage is required');
122
122
 
123
123
  this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
124
124
  this.preimage = parameters.preimage;
@@ -274,7 +274,11 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
274
274
  if (i === 0) {
275
275
  transaction.finalizeInput(i, this.customFinalizer);
276
276
  } else {
277
- transaction.finalizeInput(i);
277
+ try {
278
+ transaction.finalizeInput(i, this.customFinalizerP2SH);
279
+ } catch (e) {
280
+ transaction.finalizeInput(i);
281
+ }
278
282
  }
279
283
  }
280
284
  }
@@ -305,7 +309,12 @@ export class DeploymentTransaction extends TransactionBuilder<TransactionType.DE
305
309
  transaction.finalizeInput(0, this.customFinalizer);
306
310
  } else {
307
311
  transaction.signInput(i, this.getSignerKey());
308
- transaction.finalizeInput(i);
312
+
313
+ try {
314
+ transaction.finalizeInput(i, this.customFinalizerP2SH);
315
+ } catch (e) {
316
+ transaction.finalizeInput(i);
317
+ }
309
318
  }
310
319
  }
311
320
  }
@@ -3,6 +3,7 @@ import { TransactionType } from '../enums/TransactionType.js';
3
3
  import { TapLeafScript } from '../interfaces/Tap.js';
4
4
  import { IInteractionParameters } from '../interfaces/ITransactionParameters.js';
5
5
  import { SharedInteractionTransaction } from './SharedInteractionTransaction.js';
6
+ import { Feature, Features } from '../../generators/Features.js';
6
7
 
7
8
  /**
8
9
  * Class for interaction transactions
@@ -32,9 +33,23 @@ export class InteractionTransaction extends SharedInteractionTransaction<Transac
32
33
  this.contractSecret,
33
34
  this.preimage,
34
35
  this.priorityFee,
36
+ this.generateFeatures(parameters),
35
37
  );
36
38
 
37
39
  this.scriptTree = this.getScriptTree();
38
40
  this.internalInit();
39
41
  }
42
+
43
+ private generateFeatures(parameters: IInteractionParameters): Feature<Features>[] {
44
+ const features: Feature<Features>[] = [];
45
+
46
+ if (parameters.loadedStorage) {
47
+ features.push({
48
+ opcode: Features.ACCESS_LIST,
49
+ data: parameters.loadedStorage,
50
+ });
51
+ }
52
+
53
+ return features;
54
+ }
40
55
  }
@@ -3,6 +3,10 @@ import { ITweakedTransactionData } from '../shared/TweakedTransaction.js';
3
3
  import { ChainId } from '../../network/ChainId.js';
4
4
  import { PsbtOutputExtended } from '@btc-vision/bitcoin';
5
5
 
6
+ export interface LoadedStorage {
7
+ [key: string]: string[];
8
+ }
9
+
6
10
  export interface ITransactionParameters extends ITweakedTransactionData {
7
11
  readonly from?: string;
8
12
  readonly to?: string;
@@ -11,7 +15,7 @@ export interface ITransactionParameters extends ITweakedTransactionData {
11
15
 
12
16
  nonWitnessUtxo?: Buffer;
13
17
  estimatedFees?: bigint;
14
-
18
+
15
19
  optionalInputs?: UTXO[];
16
20
  optionalOutputs?: PsbtOutputExtended[];
17
21
 
@@ -40,6 +44,8 @@ export interface SharedInteractionParameters extends ITransactionParameters {
40
44
 
41
45
  readonly preimage: Buffer;
42
46
  readonly randomBytes?: Buffer;
47
+
48
+ readonly loadedStorage?: LoadedStorage;
43
49
  }
44
50
 
45
51
  export interface IInteractionParameters extends SharedInteractionParameters {
@@ -802,11 +802,10 @@ export abstract class TweakedTransaction extends Logger {
802
802
  if (this.tapLeafScript) {
803
803
  input.tapLeafScript = [this.tapLeafScript];
804
804
  }
805
- }
806
805
 
807
- // If the first input and we have a global nonWitnessUtxo not yet set
808
- if (i === 0 && this.nonWitnessUtxo) {
809
- input.nonWitnessUtxo = this.nonWitnessUtxo;
806
+ if (this.nonWitnessUtxo) {
807
+ input.nonWitnessUtxo = this.nonWitnessUtxo;
808
+ }
810
809
  }
811
810
 
812
811
  /*if (utxo.nonWitnessUtxo && extra) {