@btc-vision/transaction 1.3.5 → 1.3.8

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 (36) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/buffer/BinaryReader.d.ts +2 -0
  3. package/browser/generators/Features.d.ts +9 -4
  4. package/browser/generators/Generator.d.ts +5 -2
  5. package/browser/generators/builders/CalldataGenerator.d.ts +2 -2
  6. package/browser/generators/builders/LegacyCalldataGenerator.d.ts +2 -2
  7. package/browser/index.js +1 -1
  8. package/browser/transaction/builders/InteractionTransaction.d.ts +1 -0
  9. package/browser/transaction/interfaces/ITransactionParameters.d.ts +4 -0
  10. package/build/_version.d.ts +1 -1
  11. package/build/_version.js +1 -1
  12. package/build/buffer/BinaryReader.d.ts +2 -0
  13. package/build/buffer/BinaryReader.js +6 -0
  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/transaction/builders/InteractionTransaction.d.ts +1 -0
  24. package/build/transaction/builders/InteractionTransaction.js +12 -1
  25. package/build/transaction/interfaces/ITransactionParameters.d.ts +4 -0
  26. package/eslint.config.js +1 -0
  27. package/package.json +1 -1
  28. package/src/_version.ts +1 -1
  29. package/src/buffer/BinaryReader.ts +8 -0
  30. package/src/generators/Features.ts +10 -5
  31. package/src/generators/Generator.ts +54 -5
  32. package/src/generators/builders/CalldataGenerator.ts +16 -9
  33. package/src/generators/builders/DeploymentGenerator.ts +17 -1
  34. package/src/generators/builders/LegacyCalldataGenerator.ts +14 -6
  35. package/src/transaction/builders/InteractionTransaction.ts +15 -0
  36. package/src/transaction/interfaces/ITransactionParameters.ts +7 -1
@@ -10,4 +10,5 @@ export declare class InteractionTransaction extends SharedInteractionTransaction
10
10
  protected tapLeafScript: TapLeafScript | null;
11
11
  protected readonly contractSecret: Buffer;
12
12
  constructor(parameters: IInteractionParameters);
13
+ private generateFeatures;
13
14
  }
@@ -2,6 +2,9 @@ import { UTXO } from '../../utxo/interfaces/IUTXO.js';
2
2
  import { ITweakedTransactionData } from '../shared/TweakedTransaction.js';
3
3
  import { ChainId } from '../../network/ChainId.js';
4
4
  import { PsbtOutputExtended } from '@btc-vision/bitcoin';
5
+ export interface LoadedStorage {
6
+ [key: string]: string[];
7
+ }
5
8
  export interface ITransactionParameters extends ITweakedTransactionData {
6
9
  readonly from?: string;
7
10
  readonly to?: string;
@@ -28,6 +31,7 @@ export interface SharedInteractionParameters extends ITransactionParameters {
28
31
  disableAutoRefund?: boolean;
29
32
  readonly preimage: Buffer;
30
33
  readonly randomBytes?: Buffer;
34
+ readonly loadedStorage?: LoadedStorage;
31
35
  }
32
36
  export interface IInteractionParameters extends SharedInteractionParameters {
33
37
  readonly calldata: Buffer;
@@ -1 +1 @@
1
- export declare const version = "1.3.5";
1
+ export declare const version = "1.3.8";
package/build/_version.js CHANGED
@@ -1 +1 @@
1
- export const version = '1.3.5';
1
+ export const version = '1.3.8';
@@ -9,6 +9,8 @@ export declare class BinaryReader {
9
9
  static bigintCompare(a: bigint, b: bigint): number;
10
10
  static numberCompare(a: number, b: number): number;
11
11
  setBuffer(bytes: BufferLike): void;
12
+ length(): number;
13
+ bytesLeft(): number;
12
14
  readU8(): u8;
13
15
  readU16(be?: boolean): u16;
14
16
  readU32(be?: boolean): u32;
@@ -27,6 +27,12 @@ export class BinaryReader {
27
27
  this.buffer = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
28
28
  this.currentOffset = 0;
29
29
  }
30
+ length() {
31
+ return this.buffer.byteLength;
32
+ }
33
+ bytesLeft() {
34
+ return this.buffer.byteLength - this.currentOffset;
35
+ }
30
36
  readU8() {
31
37
  this.verifyEnd(this.currentOffset + U8_BYTE_LENGTH);
32
38
  const value = this.buffer.getUint8(this.currentOffset);
@@ -1,6 +1,11 @@
1
+ import { LoadedStorage } from '../transaction/interfaces/ITransactionParameters.js';
1
2
  export declare enum Features {
2
- UNWRAP = 0
3
+ ACCESS_LIST = 1
4
+ }
5
+ export interface Feature<T extends Features> {
6
+ opcode: T;
7
+ data: unknown;
8
+ }
9
+ export interface AccessListFeature extends Feature<Features.ACCESS_LIST> {
10
+ data: LoadedStorage;
3
11
  }
4
- export declare const FeatureOpCodes: {
5
- [key: number]: number;
6
- };
@@ -1,8 +1,4 @@
1
- import { opcodes } from '@btc-vision/bitcoin';
2
1
  export var Features;
3
2
  (function (Features) {
4
- Features[Features["UNWRAP"] = 0] = "UNWRAP";
3
+ Features[Features["ACCESS_LIST"] = 1] = "ACCESS_LIST";
5
4
  })(Features || (Features = {}));
6
- export const FeatureOpCodes = {
7
- [Features.UNWRAP]: opcodes.OP_16,
8
- };
@@ -1,4 +1,5 @@
1
1
  import { Network } from '@btc-vision/bitcoin';
2
+ import { Feature, Features } from './Features.js';
2
3
  export declare abstract class Generator {
3
4
  static readonly DATA_CHUNK_SIZE: number;
4
5
  static readonly MAGIC: Buffer;
@@ -7,8 +8,10 @@ export declare abstract class Generator {
7
8
  protected readonly contractSaltPubKey?: Buffer;
8
9
  protected readonly network: Network;
9
10
  protected constructor(senderPubKey: Buffer, contractSaltPubKey?: Buffer, network?: Network);
10
- get senderFirstByte(): Buffer;
11
- getHeader(maxPriority: bigint): Buffer;
11
+ buildHeader(features: Features[]): Buffer;
12
+ getHeader(maxPriority: bigint, features?: Features[]): Buffer;
12
13
  abstract compile(...args: unknown[]): Buffer;
13
14
  protected splitBufferIntoChunks(buffer: Buffer, chunkSize?: number): Array<Buffer[]>;
15
+ protected encodeFeature(feature: Feature<Features>): Buffer[][];
16
+ private encodeAccessListFeature;
14
17
  }
@@ -1,5 +1,8 @@
1
1
  import { networks, toXOnly } from '@btc-vision/bitcoin';
2
2
  import { BinaryWriter } from '../buffer/BinaryWriter.js';
3
+ import { Features } from './Features.js';
4
+ import { Address } from '../keypair/Address.js';
5
+ import { Compressor } from '../bytecode/Compressor.js';
3
6
  export class Generator {
4
7
  constructor(senderPubKey, contractSaltPubKey, network = networks.bitcoin) {
5
8
  this.network = networks.bitcoin;
@@ -8,12 +11,18 @@ export class Generator {
8
11
  this.network = network;
9
12
  this.xSenderPubKey = toXOnly(senderPubKey);
10
13
  }
11
- get senderFirstByte() {
12
- return Buffer.from([this.senderPubKey[0], 0, 0, 0]);
14
+ buildHeader(features) {
15
+ let flags = 0;
16
+ for (const feature of features) {
17
+ flags |= feature;
18
+ }
19
+ const bytesU24 = Buffer.alloc(3);
20
+ bytesU24.writeUIntBE(flags, 0, 3);
21
+ return Buffer.from([this.senderPubKey[0], ...bytesU24]);
13
22
  }
14
- getHeader(maxPriority) {
15
- const writer = new BinaryWriter(8 + 4);
16
- writer.writeBytes(this.senderFirstByte);
23
+ getHeader(maxPriority, features = []) {
24
+ const writer = new BinaryWriter(12);
25
+ writer.writeBytes(this.buildHeader(features));
17
26
  writer.writeU64(maxPriority);
18
27
  return Buffer.from(writer.getBuffer());
19
28
  }
@@ -29,6 +38,32 @@ export class Generator {
29
38
  }
30
39
  return chunks;
31
40
  }
41
+ encodeFeature(feature) {
42
+ switch (feature.opcode) {
43
+ case Features.ACCESS_LIST:
44
+ return this.splitBufferIntoChunks(this.encodeAccessListFeature(feature));
45
+ default:
46
+ throw new Error(`Unknown feature type: ${feature.opcode}`);
47
+ }
48
+ }
49
+ encodeAccessListFeature(feature) {
50
+ const writer = new BinaryWriter();
51
+ writer.writeU16(Object.keys(feature.data).length);
52
+ for (const contract in feature.data) {
53
+ const parsedContract = Address.fromString(contract);
54
+ const data = feature.data[contract];
55
+ writer.writeAddress(parsedContract);
56
+ writer.writeU32(data.length);
57
+ for (const pointer of data) {
58
+ const pointerBuffer = Buffer.from(pointer, 'base64');
59
+ if (pointerBuffer.length !== 32) {
60
+ throw new Error(`Invalid pointer length: ${pointerBuffer.length}`);
61
+ }
62
+ writer.writeBytes(pointerBuffer);
63
+ }
64
+ }
65
+ return Compressor.compress(Buffer.from(writer.getBuffer()));
66
+ }
32
67
  }
33
68
  Generator.DATA_CHUNK_SIZE = 512;
34
69
  Generator.MAGIC = Buffer.from('op', 'utf-8');
@@ -1,8 +1,8 @@
1
1
  import { Network } from '@btc-vision/bitcoin';
2
- import { Features } from '../Features.js';
2
+ import { Feature, Features } from '../Features.js';
3
3
  import { Generator } from '../Generator.js';
4
4
  export declare class CalldataGenerator extends Generator {
5
5
  constructor(senderPubKey: Buffer, contractSaltPubKey: Buffer, network?: Network);
6
6
  static getPubKeyAsBuffer(witnessKeys: Buffer[], network: Network): Buffer;
7
- compile(calldata: Buffer, contractSecret: Buffer, preimage: Buffer, maxPriority: bigint, features?: Features[]): Buffer;
7
+ compile(calldata: Buffer, contractSecret: Buffer, preimage: Buffer, maxPriority: bigint, features?: Feature<Features>[]): Buffer;
8
8
  }
@@ -1,7 +1,6 @@
1
1
  import { crypto, networks, opcodes, script } from '@btc-vision/bitcoin';
2
2
  import { Compressor } from '../../bytecode/Compressor.js';
3
3
  import { EcKeyPair } from '../../keypair/EcKeyPair.js';
4
- import { FeatureOpCodes } from '../Features.js';
5
4
  import { Generator } from '../Generator.js';
6
5
  export class CalldataGenerator extends Generator {
7
6
  constructor(senderPubKey, contractSaltPubKey, network = networks.bitcoin) {
@@ -31,8 +30,16 @@ export class CalldataGenerator extends Generator {
31
30
  const dataChunks = this.splitBufferIntoChunks(calldata);
32
31
  if (!dataChunks.length)
33
32
  throw new Error('No data chunks found');
33
+ const featuresList = [];
34
+ const featureData = [];
35
+ for (let i = 0; i < features.length; i++) {
36
+ const feature = features[i];
37
+ featuresList.push(feature.opcode);
38
+ const data = this.encodeFeature(feature);
39
+ featureData.push(...data);
40
+ }
34
41
  let compiledData = [
35
- this.getHeader(maxPriority),
42
+ this.getHeader(maxPriority, featuresList),
36
43
  opcodes.OP_TOALTSTACK,
37
44
  preimage,
38
45
  opcodes.OP_TOALTSTACK,
@@ -53,8 +60,7 @@ export class CalldataGenerator extends Generator {
53
60
  opcodes.OP_IF,
54
61
  Generator.MAGIC,
55
62
  ];
56
- const featureOpcodes = features.map((feature) => FeatureOpCodes[feature]);
57
- compiledData = compiledData.concat(...featureOpcodes, ...[opcodes.OP_1NEGATE, ...dataChunks, opcodes.OP_ELSE, opcodes.OP_1, opcodes.OP_ENDIF]);
63
+ compiledData = compiledData.concat(...featureData, ...[opcodes.OP_1NEGATE, ...dataChunks, opcodes.OP_ELSE, opcodes.OP_1, opcodes.OP_ENDIF]);
58
64
  const asm = compiledData.flat();
59
65
  const compiled = script.compile(asm);
60
66
  const decompiled = script.decompile(compiled);
@@ -13,13 +13,23 @@ export class DeploymentGenerator extends Generator {
13
13
  }
14
14
  return compiled;
15
15
  }
16
- getAsm(contractBytecode, contractSalt, preimage, maxPriority, calldata) {
16
+ getAsm(contractBytecode, contractSalt, preimage, maxPriority, calldata, features) {
17
17
  if (!this.contractSaltPubKey)
18
18
  throw new Error('Contract salt public key not set');
19
19
  const dataChunks = this.splitBufferIntoChunks(contractBytecode);
20
20
  const calldataChunks = calldata ? this.splitBufferIntoChunks(calldata) : [];
21
+ const featuresList = [];
22
+ const featureData = [];
23
+ if (features) {
24
+ for (let i = 0; i < features.length; i++) {
25
+ const feature = features[i];
26
+ featuresList.push(feature.opcode);
27
+ const data = this.encodeFeature(feature);
28
+ featureData.push(...data);
29
+ }
30
+ }
21
31
  const compiledData = [
22
- this.getHeader(maxPriority),
32
+ this.getHeader(maxPriority, featuresList),
23
33
  opcodes.OP_TOALTSTACK,
24
34
  preimage,
25
35
  opcodes.OP_TOALTSTACK,
@@ -39,6 +49,7 @@ export class DeploymentGenerator extends Generator {
39
49
  opcodes.OP_NUMEQUAL,
40
50
  opcodes.OP_IF,
41
51
  Generator.MAGIC,
52
+ ...featureData,
42
53
  opcodes.OP_0,
43
54
  ...calldataChunks,
44
55
  opcodes.OP_1NEGATE,
@@ -1,8 +1,8 @@
1
1
  import { Network } from '@btc-vision/bitcoin';
2
- import { Features } from '../Features.js';
3
2
  import { Generator } from '../Generator.js';
3
+ import { Feature, Features } from '../Features.js';
4
4
  export declare class LegacyCalldataGenerator extends Generator {
5
5
  constructor(senderPubKey: Buffer, network?: Network);
6
6
  static getPubKeyAsBuffer(witnessKeys: Buffer[], network: Network): Buffer;
7
- compile(calldata: Buffer, contractSecret: Buffer, preimage: Buffer, maxPriority: bigint, features?: Features[]): Buffer;
7
+ compile(calldata: Buffer, contractSecret: Buffer, preimage: Buffer, maxPriority: bigint, features?: Feature<Features>[]): Buffer;
8
8
  }
@@ -1,7 +1,6 @@
1
1
  import { crypto, networks, opcodes, script } from '@btc-vision/bitcoin';
2
2
  import { Compressor } from '../../bytecode/Compressor.js';
3
3
  import { EcKeyPair } from '../../keypair/EcKeyPair.js';
4
- import { FeatureOpCodes } from '../Features.js';
5
4
  import { Generator } from '../Generator.js';
6
5
  export class LegacyCalldataGenerator extends Generator {
7
6
  constructor(senderPubKey, network = networks.bitcoin) {
@@ -29,8 +28,16 @@ export class LegacyCalldataGenerator extends Generator {
29
28
  const dataChunks = this.splitBufferIntoChunks(calldata);
30
29
  if (!dataChunks.length)
31
30
  throw new Error('No data chunks found');
31
+ const featuresList = [];
32
+ const featureData = [];
33
+ for (let i = 0; i < features.length; i++) {
34
+ const feature = features[i];
35
+ featuresList.push(feature.opcode);
36
+ const data = this.encodeFeature(feature);
37
+ featureData.push(...data);
38
+ }
32
39
  let compiledData = [
33
- this.getHeader(maxPriority),
40
+ this.getHeader(maxPriority, featuresList),
34
41
  opcodes.OP_TOALTSTACK,
35
42
  preimage,
36
43
  opcodes.OP_TOALTSTACK,
@@ -48,8 +55,7 @@ export class LegacyCalldataGenerator extends Generator {
48
55
  opcodes.OP_IF,
49
56
  Generator.MAGIC,
50
57
  ];
51
- const featureOpcodes = features.map((feature) => FeatureOpCodes[feature]);
52
- compiledData = compiledData.concat(...featureOpcodes, ...[opcodes.OP_1NEGATE, ...dataChunks, opcodes.OP_ELSE, opcodes.OP_1, opcodes.OP_ENDIF]);
58
+ compiledData = compiledData.concat(...featureData, ...[opcodes.OP_1NEGATE, ...dataChunks, opcodes.OP_ELSE, opcodes.OP_1, opcodes.OP_ENDIF]);
53
59
  const asm = compiledData.flat();
54
60
  const compiled = script.compile(asm);
55
61
  const decompiled = script.decompile(compiled);
@@ -10,4 +10,5 @@ export declare class InteractionTransaction extends SharedInteractionTransaction
10
10
  protected tapLeafScript: TapLeafScript | null;
11
11
  protected readonly contractSecret: Buffer;
12
12
  constructor(parameters: IInteractionParameters);
13
+ private generateFeatures;
13
14
  }
@@ -1,13 +1,24 @@
1
1
  import { TransactionType } from '../enums/TransactionType.js';
2
2
  import { SharedInteractionTransaction } from './SharedInteractionTransaction.js';
3
+ import { Features } from '../../generators/Features.js';
3
4
  export class InteractionTransaction extends SharedInteractionTransaction {
4
5
  constructor(parameters) {
5
6
  super(parameters);
6
7
  this.type = TransactionType.INTERACTION;
7
8
  this.tapLeafScript = null;
8
9
  this.contractSecret = this.generateSecret();
9
- this.compiledTargetScript = this.calldataGenerator.compile(this.calldata, this.contractSecret, this.preimage, this.priorityFee);
10
+ this.compiledTargetScript = this.calldataGenerator.compile(this.calldata, this.contractSecret, this.preimage, this.priorityFee, this.generateFeatures(parameters));
10
11
  this.scriptTree = this.getScriptTree();
11
12
  this.internalInit();
12
13
  }
14
+ generateFeatures(parameters) {
15
+ const features = [];
16
+ if (parameters.loadedStorage) {
17
+ features.push({
18
+ opcode: Features.ACCESS_LIST,
19
+ data: parameters.loadedStorage,
20
+ });
21
+ }
22
+ return features;
23
+ }
13
24
  }
@@ -2,6 +2,9 @@ import { UTXO } from '../../utxo/interfaces/IUTXO.js';
2
2
  import { ITweakedTransactionData } from '../shared/TweakedTransaction.js';
3
3
  import { ChainId } from '../../network/ChainId.js';
4
4
  import { PsbtOutputExtended } from '@btc-vision/bitcoin';
5
+ export interface LoadedStorage {
6
+ [key: string]: string[];
7
+ }
5
8
  export interface ITransactionParameters extends ITweakedTransactionData {
6
9
  readonly from?: string;
7
10
  readonly to?: string;
@@ -28,6 +31,7 @@ export interface SharedInteractionParameters extends ITransactionParameters {
28
31
  disableAutoRefund?: boolean;
29
32
  readonly preimage: Buffer;
30
33
  readonly randomBytes?: Buffer;
34
+ readonly loadedStorage?: LoadedStorage;
31
35
  }
32
36
  export interface IInteractionParameters extends SharedInteractionParameters {
33
37
  readonly calldata: Buffer;
package/eslint.config.js CHANGED
@@ -29,6 +29,7 @@ export default tseslint.config(
29
29
  '@typescript-eslint/no-duplicate-enum-values': 'off',
30
30
  'prefer-spread': 'off',
31
31
  '@typescript-eslint/no-empty-object-type': 'off',
32
+ '@typescript-eslint/prefer-literal-enum-member': 'off'
32
33
  },
33
34
  },
34
35
  {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@btc-vision/transaction",
3
3
  "type": "module",
4
- "version": "1.3.5",
4
+ "version": "1.3.8",
5
5
  "author": "BlobMaster41",
6
6
  "description": "OPNet transaction library allows you to create and sign transactions for the OPNet network.",
7
7
  "engines": {
package/src/_version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '1.3.5';
1
+ export const version = '1.3.8';
@@ -42,6 +42,14 @@ export class BinaryReader {
42
42
  this.currentOffset = 0;
43
43
  }
44
44
 
45
+ public length(): number {
46
+ return this.buffer.byteLength;
47
+ }
48
+
49
+ public bytesLeft(): number {
50
+ return this.buffer.byteLength - this.currentOffset;
51
+ }
52
+
45
53
  /**
46
54
  * Reads a single unsigned byte (u8).
47
55
  */
@@ -1,9 +1,14 @@
1
- import { opcodes } from '@btc-vision/bitcoin';
1
+ import { LoadedStorage } from '../transaction/interfaces/ITransactionParameters.js';
2
2
 
3
3
  export enum Features {
4
- UNWRAP = 0, // random number just to set the first value
4
+ ACCESS_LIST = 1,
5
5
  }
6
6
 
7
- export const FeatureOpCodes: { [key: number]: number } = {
8
- [Features.UNWRAP]: opcodes.OP_16,
9
- };
7
+ export interface Feature<T extends Features> {
8
+ opcode: T;
9
+ data: unknown;
10
+ }
11
+
12
+ export interface AccessListFeature extends Feature<Features.ACCESS_LIST> {
13
+ data: LoadedStorage;
14
+ }
@@ -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