@5ive-tech/sdk 1.1.7 → 1.1.9

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.
@@ -3,6 +3,75 @@ import { validator, Validators } from "../validation/index.js";
3
3
  import { calculateDeployFee } from "./fees.js";
4
4
  import { pollForConfirmation } from "../utils/transaction.js";
5
5
  import { ProgramIdResolver } from "../config/ProgramIdResolver.js";
6
+ const DEFAULT_FEE_VAULT_SHARD_COUNT = 10;
7
+ const FEE_VAULT_NAMESPACE_SEED = Buffer.from([
8
+ 0xff, 0x66, 0x69, 0x76, 0x65, 0x5f, 0x76, 0x6d, 0x5f, 0x66, 0x65, 0x65,
9
+ 0x5f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x31,
10
+ ]);
11
+ async function readVMStateFeeConfig(connection, vmStateAddress) {
12
+ if (!connection) {
13
+ return { shardCount: DEFAULT_FEE_VAULT_SHARD_COUNT };
14
+ }
15
+ try {
16
+ const { PublicKey } = await import("@solana/web3.js");
17
+ const info = await connection.getAccountInfo(new PublicKey(vmStateAddress), "confirmed");
18
+ if (!info) {
19
+ return { shardCount: DEFAULT_FEE_VAULT_SHARD_COUNT };
20
+ }
21
+ const data = new Uint8Array(info.data);
22
+ if (data.length < 56) {
23
+ return { shardCount: DEFAULT_FEE_VAULT_SHARD_COUNT };
24
+ }
25
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
26
+ const deployFeeLamports = view.getUint32(40, true);
27
+ const shardCountRaw = data.length > 50 ? data[50] : 0;
28
+ const shardCount = shardCountRaw > 0 ? shardCountRaw : DEFAULT_FEE_VAULT_SHARD_COUNT;
29
+ return { deployFeeLamports, shardCount };
30
+ }
31
+ catch {
32
+ return { shardCount: DEFAULT_FEE_VAULT_SHARD_COUNT };
33
+ }
34
+ }
35
+ async function deriveProgramFeeVault(programId, shardIndex) {
36
+ const { PublicKey } = await import("@solana/web3.js");
37
+ const [pda, bump] = PublicKey.findProgramAddressSync([FEE_VAULT_NAMESPACE_SEED, Buffer.from([shardIndex])], new PublicKey(programId));
38
+ return { address: pda.toBase58(), bump };
39
+ }
40
+ function createInitFeeVaultInstructionData(shardIndex, bump) {
41
+ return Uint8Array.from([11, shardIndex & 0xff, bump & 0xff]);
42
+ }
43
+ async function initProgramFeeVaultShards(connection, programId, vmStateAccount, shardCount, payer, options = {}) {
44
+ const { PublicKey, Transaction, TransactionInstruction, SystemProgram } = await import("@solana/web3.js");
45
+ const signatures = [];
46
+ for (let shardIndex = 0; shardIndex < shardCount; shardIndex++) {
47
+ const vault = await deriveProgramFeeVault(programId, shardIndex);
48
+ const tx = new Transaction().add(new TransactionInstruction({
49
+ programId: new PublicKey(programId),
50
+ keys: [
51
+ { pubkey: new PublicKey(vmStateAccount), isSigner: false, isWritable: false },
52
+ { pubkey: payer.publicKey, isSigner: true, isWritable: true },
53
+ { pubkey: new PublicKey(vault.address), isSigner: false, isWritable: true },
54
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
55
+ ],
56
+ data: Buffer.from(createInitFeeVaultInstructionData(shardIndex, vault.bump)),
57
+ }));
58
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
59
+ tx.recentBlockhash = blockhash;
60
+ tx.feePayer = payer.publicKey;
61
+ tx.partialSign(payer);
62
+ const sig = await connection.sendRawTransaction(tx.serialize(), {
63
+ skipPreflight: true,
64
+ preflightCommitment: "confirmed",
65
+ maxRetries: options.maxRetries || 3,
66
+ });
67
+ await connection.confirmTransaction(sig, "confirmed");
68
+ signatures.push(sig);
69
+ if (options.debug) {
70
+ console.log(`[FiveSDK] Initialized fee vault shard ${shardIndex}: ${vault.address}`);
71
+ }
72
+ }
73
+ return signatures;
74
+ }
6
75
  export async function generateDeployInstruction(bytecode, deployer, options = {}, connection, fiveVMProgramId) {
7
76
  Validators.bytecode(bytecode);
8
77
  validator.validateBase58Address(deployer, "deployer");
@@ -29,24 +98,21 @@ export async function generateDeployInstruction(bytecode, deployer, options = {}
29
98
  const SCRIPT_HEADER_SIZE = 64; // ScriptAccountHeader size from Rust program
30
99
  const totalAccountSize = SCRIPT_HEADER_SIZE + exportMetadata.length + bytecode.length;
31
100
  const rentLamports = await RentCalculator.calculateRentExemption(totalAccountSize);
101
+ const { deployFeeLamports } = await readVMStateFeeConfig(connection, vmStatePDA);
102
+ const deployShardIndex = 0;
103
+ const deployVault = await deriveProgramFeeVault(programId, deployShardIndex);
32
104
  const deployAccounts = [
33
105
  { pubkey: scriptAccount, isSigner: false, isWritable: true },
34
106
  { pubkey: vmStatePDA, isSigner: false, isWritable: true },
35
107
  { pubkey: deployer, isSigner: true, isWritable: true },
36
- {
37
- pubkey: "11111111111111111111111111111112",
38
- isSigner: false,
39
- isWritable: false,
40
- },
108
+ { pubkey: deployVault.address, isSigner: false, isWritable: true },
41
109
  ];
42
- if (options.adminAccount) {
43
- deployAccounts.push({
44
- pubkey: options.adminAccount,
45
- isSigner: false,
46
- isWritable: true,
47
- });
48
- }
49
- const instructionData = encodeDeployInstruction(bytecode, options.permissions || 0, exportMetadata);
110
+ deployAccounts.push({
111
+ pubkey: "11111111111111111111111111111111",
112
+ isSigner: false,
113
+ isWritable: false,
114
+ });
115
+ const instructionData = encodeDeployInstruction(bytecode, options.permissions || 0, exportMetadata, deployShardIndex, deployVault.bump);
50
116
  const result = {
51
117
  programId: programId,
52
118
  instruction: {
@@ -110,14 +176,12 @@ options = {}) {
110
176
  const exportMetadata = encodeExportMetadata(options.exportMetadata);
111
177
  const totalAccountSize = SCRIPT_HEADER_SIZE + exportMetadata.length + bytecode.length;
112
178
  const rentLamports = await connection.getMinimumBalanceForRentExemption(totalAccountSize);
113
- // Generate VM state keypair
114
- const vmStateKeypair = Keypair.generate();
115
- const VM_STATE_SIZE = 56; // FIVEVMState::LEN
116
- const vmStateRent = await connection.getMinimumBalanceForRentExemption(VM_STATE_SIZE);
179
+ const vmStatePDA = await PDAUtils.deriveVMStatePDA(programIdStr);
180
+ const vmStatePubkey = new PublicKey(vmStatePDA.address);
117
181
  if (options.debug) {
118
182
  console.log(`[FiveSDK] Preparing deployment transaction:`);
119
183
  console.log(` - Script Account: ${scriptAccount}`);
120
- console.log(` - VM State Account: ${vmStateKeypair.publicKey.toString()}`);
184
+ console.log(` - VM State Account: ${vmStatePubkey.toString()}`);
121
185
  console.log(` - Deployer: ${deployerPublicKey.toString()}`);
122
186
  }
123
187
  const tx = new Transaction();
@@ -127,24 +191,21 @@ options = {}) {
127
191
  units: options.computeBudget,
128
192
  }));
129
193
  }
130
- // 1. Create VM State Account
131
- tx.add(SystemProgram.createAccount({
132
- fromPubkey: deployerPublicKey,
133
- newAccountPubkey: vmStateKeypair.publicKey,
134
- lamports: vmStateRent,
135
- space: VM_STATE_SIZE,
136
- programId: programId,
137
- }));
138
- // 2. Initialize VM State
139
- tx.add(new TransactionInstruction({
140
- keys: [
141
- { pubkey: vmStateKeypair.publicKey, isSigner: false, isWritable: true },
142
- { pubkey: deployerPublicKey, isSigner: true, isWritable: false },
143
- ],
144
- programId: programId,
145
- data: Buffer.from([0]), // Initialize discriminator
146
- }));
147
- // 3. Create Script Account
194
+ // 1. Initialize canonical VM State if missing
195
+ const vmStateInfo = await connection.getAccountInfo(vmStatePubkey);
196
+ if (!vmStateInfo) {
197
+ tx.add(new TransactionInstruction({
198
+ keys: [
199
+ { pubkey: vmStatePubkey, isSigner: false, isWritable: true },
200
+ { pubkey: deployerPublicKey, isSigner: true, isWritable: false },
201
+ { pubkey: deployerPublicKey, isSigner: true, isWritable: true },
202
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
203
+ ],
204
+ programId: programId,
205
+ data: buildInitializeVmStateInstructionData(vmStatePDA.bump),
206
+ }));
207
+ }
208
+ // 2. Create Script Account
148
209
  tx.add(SystemProgram.createAccount({
149
210
  fromPubkey: deployerPublicKey,
150
211
  newAccountPubkey: scriptKeypair.publicKey,
@@ -152,12 +213,12 @@ options = {}) {
152
213
  space: totalAccountSize,
153
214
  programId: programId,
154
215
  }));
155
- // 4. Deploy Instruction
216
+ // 3. Deploy Instruction
156
217
  const deployData = encodeDeployInstruction(bytecode, 0, exportMetadata);
157
218
  tx.add(new TransactionInstruction({
158
219
  keys: [
159
220
  { pubkey: scriptKeypair.publicKey, isSigner: false, isWritable: true },
160
- { pubkey: vmStateKeypair.publicKey, isSigner: false, isWritable: true },
221
+ { pubkey: vmStatePubkey, isSigner: false, isWritable: true },
161
222
  { pubkey: deployerPublicKey, isSigner: true, isWritable: true },
162
223
  ],
163
224
  programId: programId,
@@ -168,11 +229,10 @@ options = {}) {
168
229
  tx.feePayer = deployerPublicKey;
169
230
  // Partial sign with generated keys
170
231
  tx.partialSign(scriptKeypair);
171
- tx.partialSign(vmStateKeypair);
172
232
  return {
173
233
  transaction: tx,
174
234
  scriptKeypair,
175
- vmStateKeypair,
235
+ vmStateKeypair: null,
176
236
  programId: scriptAccount,
177
237
  rentLamports,
178
238
  };
@@ -200,22 +260,28 @@ options = {}) {
200
260
  const exportMetadata = encodeExportMetadata(options.exportMetadata);
201
261
  const totalAccountSize = SCRIPT_HEADER_SIZE + exportMetadata.length + bytecode.length;
202
262
  const rentLamports = await connection.getMinimumBalanceForRentExemption(totalAccountSize);
203
- // Generate VM state keypair for this deployment OR reuse
204
- let vmStatePubkey;
205
- let vmStateKeypair;
206
- let vmStateRent = 0;
207
- const VM_STATE_SIZE = 56; // FIVEVMState::LEN
208
- if (options.vmStateAccount) {
209
- vmStatePubkey = new PublicKey(options.vmStateAccount);
210
- if (options.debug) {
211
- console.log(`[FiveSDK] Reuse VM State: ${vmStatePubkey.toString()}`);
212
- }
213
- }
214
- else {
215
- vmStateKeypair = Keypair.generate();
216
- vmStatePubkey = vmStateKeypair.publicKey;
217
- vmStateRent = await connection.getMinimumBalanceForRentExemption(VM_STATE_SIZE);
218
- }
263
+ const vmStateResolution = await ensureCanonicalVmStateAccount(connection, deployerKeypair, new PublicKey(programId), {
264
+ vmStateAccount: options.vmStateAccount,
265
+ maxRetries: options.maxRetries,
266
+ debug: options.debug,
267
+ });
268
+ const vmStatePubkey = vmStateResolution.vmStatePubkey;
269
+ const vmStateRent = vmStateResolution.vmStateRent;
270
+ const vmStateFeeConfig = await readVMStateFeeConfig(connection, vmStatePubkey.toString());
271
+ const deployShardIndex = 0;
272
+ const deployVault = await deriveProgramFeeVault(programId, deployShardIndex);
273
+ const feeVaultKeys = [
274
+ {
275
+ pubkey: new PublicKey(deployVault.address),
276
+ isSigner: false,
277
+ isWritable: true,
278
+ },
279
+ {
280
+ pubkey: SystemProgram.programId,
281
+ isSigner: false,
282
+ isWritable: false,
283
+ },
284
+ ];
219
285
  if (options.debug) {
220
286
  console.log(`[FiveSDK] Script Account: ${scriptAccount}`);
221
287
  console.log(`[FiveSDK] VM State Account: ${vmStatePubkey.toString()}`);
@@ -223,7 +289,8 @@ options = {}) {
223
289
  console.log(`[FiveSDK] Export metadata size: ${exportMetadata.length} bytes`);
224
290
  console.log(`[FiveSDK] Rent cost: ${((rentLamports + vmStateRent) / 1e9)} SOL`);
225
291
  }
226
- // SINGLE TRANSACTION: create VM state + initialize + create script account + deploy bytecode
292
+ const feeVaultInitSigs = await initProgramFeeVaultShards(connection, programId, vmStatePubkey.toString(), vmStateFeeConfig.shardCount, deployerKeypair, { debug: options.debug, maxRetries: options.maxRetries });
293
+ // SINGLE TRANSACTION: create script account + deploy bytecode
227
294
  const tx = new Transaction();
228
295
  // Optional compute budget
229
296
  if (options.computeBudget && options.computeBudget > 0) {
@@ -235,36 +302,7 @@ options = {}) {
235
302
  }
236
303
  catch { }
237
304
  }
238
- if (!options.vmStateAccount) {
239
- // 1) Create VM state account owned by the program
240
- const createVmStateIx = SystemProgram.createAccount({
241
- fromPubkey: deployerKeypair.publicKey,
242
- newAccountPubkey: vmStatePubkey,
243
- lamports: vmStateRent,
244
- space: VM_STATE_SIZE,
245
- programId: new PublicKey(programId),
246
- });
247
- tx.add(createVmStateIx);
248
- // 2) Initialize VM state: [discriminator(0)] with accounts [vm_state, authority]
249
- const initVmStateIx = new TransactionInstruction({
250
- keys: [
251
- {
252
- pubkey: vmStatePubkey,
253
- isSigner: false,
254
- isWritable: true,
255
- },
256
- {
257
- pubkey: deployerKeypair.publicKey,
258
- isSigner: true,
259
- isWritable: false,
260
- },
261
- ],
262
- programId: new PublicKey(programId),
263
- data: Buffer.from([0]), // Initialize discriminator
264
- });
265
- tx.add(initVmStateIx);
266
- }
267
- // 3) Create script account
305
+ // 1) Create script account
268
306
  const createAccountIx = SystemProgram.createAccount({
269
307
  fromPubkey: deployerKeypair.publicKey,
270
308
  newAccountPubkey: scriptKeypair.publicKey,
@@ -273,7 +311,7 @@ options = {}) {
273
311
  programId: new PublicKey(programId),
274
312
  });
275
313
  tx.add(createAccountIx);
276
- const deployData = encodeDeployInstruction(bytecode, 0, exportMetadata);
314
+ const deployData = encodeDeployInstruction(bytecode, 0, exportMetadata, deployShardIndex, deployVault.bump);
277
315
  const instructionDataBuffer = Buffer.from(deployData);
278
316
  const deployIx = new TransactionInstruction({
279
317
  keys: [
@@ -292,6 +330,16 @@ options = {}) {
292
330
  isSigner: true,
293
331
  isWritable: true,
294
332
  },
333
+ {
334
+ pubkey: new PublicKey(deployVault.address),
335
+ isSigner: false,
336
+ isWritable: true,
337
+ },
338
+ {
339
+ pubkey: SystemProgram.programId,
340
+ isSigner: false,
341
+ isWritable: false,
342
+ },
295
343
  ],
296
344
  programId: new PublicKey(programId),
297
345
  data: instructionDataBuffer,
@@ -301,9 +349,6 @@ options = {}) {
301
349
  tx.recentBlockhash = blockhash;
302
350
  tx.feePayer = deployerKeypair.publicKey;
303
351
  tx.partialSign(deployerKeypair);
304
- if (!options.vmStateAccount) {
305
- tx.partialSign(vmStateKeypair);
306
- }
307
352
  tx.partialSign(scriptKeypair);
308
353
  const txSerialized = tx.serialize();
309
354
  if (options.debug) {
@@ -347,13 +392,14 @@ options = {}) {
347
392
  success: true,
348
393
  programId: scriptAccount,
349
394
  transactionId: signature,
350
- deploymentCost: rentLamports,
395
+ deploymentCost: rentLamports + vmStateRent,
351
396
  logs: [
352
397
  `Script Account: ${scriptAccount}`,
353
398
  `Deployment TX: ${signature}`,
354
399
  `Deployment cost (rent): ${rentLamports / 1e9} SOL`,
355
400
  `Bytecode size: ${bytecode.length} bytes`,
356
401
  `VM State Account: ${vmStatePubkey.toString()}`,
402
+ `Fee vault shards initialized: ${feeVaultInitSigs.length}`,
357
403
  ],
358
404
  vmStateAccount: vmStatePubkey.toString(),
359
405
  };
@@ -380,7 +426,7 @@ options = {}) {
380
426
  console.log(`[FiveSDK] options:`, options);
381
427
  try {
382
428
  // If bytecode is small enough, use regular deployment
383
- if (bytecode.length <= 800) {
429
+ if (bytecode.length <= 800 && !options.forceChunkedSmallProgram) {
384
430
  if (options.debug) {
385
431
  console.log(`[FiveSDK] Bytecode is small (${bytecode.length} bytes), using regular deployment`);
386
432
  }
@@ -390,6 +436,7 @@ options = {}) {
390
436
  maxRetries: options.maxRetries,
391
437
  fiveVMProgramId: options.fiveVMProgramId,
392
438
  vmStateAccount: options.vmStateAccount,
439
+ adminAccount: options.adminAccount,
393
440
  });
394
441
  }
395
442
  const { Keypair, PublicKey, Transaction, TransactionInstruction, SystemProgram, } = await import("@solana/web3.js");
@@ -402,76 +449,37 @@ options = {}) {
402
449
  const rentLamports = await connection.getMinimumBalanceForRentExemption(totalAccountSize);
403
450
  const programIdStr = ProgramIdResolver.resolve(options.fiveVMProgramId);
404
451
  const programId = new PublicKey(programIdStr);
405
- // Handle VM state account (reuse or create)
406
- let vmStatePubkey;
407
- let vmStateKeypair;
408
- let vmStateRent = 0;
409
- const VM_STATE_SIZE = 56; // FIVEVMState::LEN
410
- if (options.vmStateAccount) {
411
- vmStatePubkey = new PublicKey(options.vmStateAccount);
412
- if (options.debug) {
413
- console.log(`[FiveSDK] Reuse existing VM State: ${vmStatePubkey.toString()}`);
414
- }
415
- }
416
- else {
417
- // Generate NEW VM state account
418
- vmStateKeypair = Keypair.generate();
419
- vmStatePubkey = vmStateKeypair.publicKey;
420
- vmStateRent = await connection.getMinimumBalanceForRentExemption(VM_STATE_SIZE);
421
- }
452
+ const vmStateResolution = await ensureCanonicalVmStateAccount(connection, deployerKeypair, programId, {
453
+ vmStateAccount: options.vmStateAccount,
454
+ maxRetries: options.maxRetries,
455
+ debug: options.debug,
456
+ });
457
+ const vmStatePubkey = vmStateResolution.vmStatePubkey;
458
+ const vmStateRent = vmStateResolution.vmStateRent;
459
+ const vmStateFeeConfig = await readVMStateFeeConfig(connection, vmStatePubkey.toString());
460
+ const deployShardIndex = 0;
461
+ const deployVault = await deriveProgramFeeVault(programIdStr, deployShardIndex);
462
+ const feeVaultKeys = [
463
+ {
464
+ pubkey: new PublicKey(deployVault.address),
465
+ isSigner: false,
466
+ isWritable: true,
467
+ },
468
+ {
469
+ pubkey: SystemProgram.programId,
470
+ isSigner: false,
471
+ isWritable: false,
472
+ },
473
+ ];
422
474
  if (options.debug) {
423
475
  console.log(`[FiveSDK] Script Account: ${scriptAccount}`);
424
476
  console.log(`[FiveSDK] VM State Account: ${vmStatePubkey.toString()}`);
425
477
  console.log(`[FiveSDK] Total account size: ${totalAccountSize} bytes`);
426
478
  console.log(`[FiveSDK] Initial rent cost: ${(rentLamports + vmStateRent) / 1e9} SOL`);
427
479
  }
480
+ const feeVaultInitSigs = await initProgramFeeVaultShards(connection, programIdStr, vmStatePubkey.toString(), vmStateFeeConfig.shardCount, deployerKeypair, { debug: options.debug, maxRetries: options.maxRetries });
428
481
  const transactionIds = [];
429
482
  let totalCost = rentLamports + vmStateRent;
430
- // TRANSACTION 0: Create VM State Account + Initialize (ONLY IF CREATING NEW)
431
- if (!options.vmStateAccount) {
432
- if (options.debug) {
433
- console.log(`[FiveSDK] Step 0: Create VM state account and initialize`);
434
- }
435
- const vmStateTransaction = new Transaction();
436
- vmStateTransaction.add(SystemProgram.createAccount({
437
- fromPubkey: deployerKeypair.publicKey,
438
- newAccountPubkey: vmStateKeypair.publicKey,
439
- lamports: vmStateRent,
440
- space: VM_STATE_SIZE,
441
- programId: programId,
442
- }));
443
- vmStateTransaction.add(new TransactionInstruction({
444
- keys: [
445
- {
446
- pubkey: vmStateKeypair.publicKey,
447
- isSigner: false,
448
- isWritable: true,
449
- },
450
- {
451
- pubkey: deployerKeypair.publicKey,
452
- isSigner: true,
453
- isWritable: false,
454
- },
455
- ],
456
- programId: programId,
457
- data: Buffer.from([0]), // Initialize discriminator
458
- }));
459
- vmStateTransaction.feePayer = deployerKeypair.publicKey;
460
- const vmStateBlockhash = await connection.getLatestBlockhash("confirmed");
461
- vmStateTransaction.recentBlockhash = vmStateBlockhash.blockhash;
462
- vmStateTransaction.partialSign(deployerKeypair);
463
- vmStateTransaction.partialSign(vmStateKeypair);
464
- const vmStateSignature = await connection.sendRawTransaction(vmStateTransaction.serialize(), {
465
- skipPreflight: true,
466
- preflightCommitment: "confirmed",
467
- maxRetries: options.maxRetries || 3,
468
- });
469
- await connection.confirmTransaction(vmStateSignature, "confirmed");
470
- transactionIds.push(vmStateSignature);
471
- if (options.debug) {
472
- console.log(`[FiveSDK] ✅ VM state initialized: ${vmStateSignature}`);
473
- }
474
- }
475
483
  // TRANSACTION 1: Create Account + InitLargeProgram
476
484
  if (options.debug) {
477
485
  console.log(`[FiveSDK] Step 1: Create account and initialize large program`);
@@ -487,10 +495,7 @@ options = {}) {
487
495
  });
488
496
  initTransaction.add(createAccountInstruction);
489
497
  // Add InitLargeProgram instruction (discriminator 4 + expected_size as u32)
490
- const initInstructionData = Buffer.concat([
491
- Buffer.from([4]), // InitLargeProgram discriminator
492
- Buffer.from(new Uint32Array([bytecode.length]).buffer), // expected_size as little-endian u32
493
- ]);
498
+ const initInstructionData = createInitLargeProgramInstructionData(bytecode.length);
494
499
  const initLargeProgramInstruction = new TransactionInstruction({
495
500
  keys: [
496
501
  {
@@ -506,8 +511,9 @@ options = {}) {
506
511
  {
507
512
  pubkey: vmStatePubkey,
508
513
  isSigner: false,
509
- isWritable: true,
514
+ isWritable: false,
510
515
  },
516
+ ...feeVaultKeys,
511
517
  ],
512
518
  programId: programId,
513
519
  data: initInstructionData,
@@ -587,10 +593,7 @@ options = {}) {
587
593
  totalCost += additionalRent;
588
594
  }
589
595
  // Add AppendBytecode instruction (discriminator 5 + chunk data)
590
- const appendInstructionData = Buffer.concat([
591
- Buffer.from([5]), // AppendBytecode discriminator
592
- chunk,
593
- ]);
596
+ const appendInstructionData = createAppendBytecodeInstructionData(chunk);
594
597
  const appendBytecodeInstruction = new TransactionInstruction({
595
598
  keys: [
596
599
  {
@@ -606,8 +609,9 @@ options = {}) {
606
609
  {
607
610
  pubkey: vmStatePubkey,
608
611
  isSigner: false,
609
- isWritable: true,
612
+ isWritable: false,
610
613
  },
614
+ ...feeVaultKeys,
611
615
  ],
612
616
  programId: programId,
613
617
  data: appendInstructionData,
@@ -648,6 +652,7 @@ options = {}) {
648
652
  vmStateAccount: vmStatePubkey.toString(),
649
653
  logs: [
650
654
  `Deployed ${bytecode.length} bytes in ${chunks.length} chunks using ${transactionIds.length} transactions`,
655
+ `Fee vault shards initialized: ${feeVaultInitSigs.length}`,
651
656
  ],
652
657
  };
653
658
  }
@@ -675,7 +680,7 @@ options = {}) {
675
680
  console.log(`[FiveSDK] Expected optimization: 50-70% fewer transactions`);
676
681
  try {
677
682
  // If bytecode is small enough, use regular deployment
678
- if (bytecode.length <= 800) {
683
+ if (bytecode.length <= 800 && !options.forceChunkedSmallProgram) {
679
684
  if (options.debug) {
680
685
  console.log(`[FiveSDK] Bytecode is small (${bytecode.length} bytes), using regular deployment`);
681
686
  }
@@ -683,6 +688,10 @@ options = {}) {
683
688
  debug: options.debug,
684
689
  network: options.network,
685
690
  maxRetries: options.maxRetries,
691
+ fiveVMProgramId: options.fiveVMProgramId,
692
+ vmStateAccount: options.vmStateAccount,
693
+ adminAccount: options.adminAccount,
694
+ exportMetadata: options.exportMetadata,
686
695
  });
687
696
  }
688
697
  const { Keypair, PublicKey, Transaction, TransactionInstruction, SystemProgram, } = await import("@solana/web3.js");
@@ -690,72 +699,42 @@ options = {}) {
690
699
  const scriptKeypair = Keypair.generate();
691
700
  const scriptAccount = scriptKeypair.publicKey.toString();
692
701
  // Calculate full account size upfront
693
- const SCRIPT_HEADER_SIZE = 128; // FIVEScriptHeaderV2::LEN
702
+ const SCRIPT_HEADER_SIZE = 64; // ScriptAccountHeader::LEN
694
703
  const totalAccountSize = SCRIPT_HEADER_SIZE + bytecode.length;
695
704
  const rentLamports = await connection.getMinimumBalanceForRentExemption(totalAccountSize);
696
705
  const programIdStr = ProgramIdResolver.resolve(options.fiveVMProgramId);
697
706
  const programId = new PublicKey(programIdStr);
698
- // Generate VM state account for this deployment
699
- const vmStateKeypair = Keypair.generate();
700
- const VM_STATE_SIZE = 56; // FIVEVMState::LEN (32 + 8 + 4 + 4 + 1 + 7)
701
- const vmStateRent = await connection.getMinimumBalanceForRentExemption(VM_STATE_SIZE);
707
+ const vmStateResolution = await ensureCanonicalVmStateAccount(connection, deployerKeypair, programId, {
708
+ vmStateAccount: options.vmStateAccount,
709
+ maxRetries: options.maxRetries,
710
+ debug: options.debug,
711
+ });
712
+ const vmStatePubkey = vmStateResolution.vmStatePubkey;
713
+ const vmStateRent = vmStateResolution.vmStateRent;
714
+ const vmStateFeeConfig = await readVMStateFeeConfig(connection, vmStatePubkey.toString());
715
+ const deployShardIndex = 0;
716
+ const deployVault = await deriveProgramFeeVault(programIdStr, deployShardIndex);
717
+ const feeVaultKeys = [
718
+ {
719
+ pubkey: new PublicKey(deployVault.address),
720
+ isSigner: false,
721
+ isWritable: true,
722
+ },
723
+ {
724
+ pubkey: SystemProgram.programId,
725
+ isSigner: false,
726
+ isWritable: false,
727
+ },
728
+ ];
702
729
  if (options.debug) {
703
730
  console.log(`[FiveSDK] Script Account: ${scriptAccount}`);
704
- console.log(`[FiveSDK] VM State Account: ${vmStateKeypair.publicKey.toString()}`);
731
+ console.log(`[FiveSDK] VM State Account: ${vmStatePubkey.toString()}`);
705
732
  console.log(`[FiveSDK] PRE-ALLOCATED full account size: ${totalAccountSize} bytes`);
706
733
  console.log(`[FiveSDK] Full rent cost paid upfront: ${(rentLamports + vmStateRent) / 1e9} SOL`);
707
734
  }
735
+ const feeVaultInitSigs = await initProgramFeeVaultShards(connection, programIdStr, vmStatePubkey.toString(), vmStateFeeConfig.shardCount, deployerKeypair, { debug: options.debug, maxRetries: options.maxRetries });
708
736
  const transactionIds = [];
709
737
  let totalCost = rentLamports + vmStateRent;
710
- if (options.debug) {
711
- console.log(`[FiveSDK] Create VM state account and initialize`);
712
- }
713
- const vmStateTransaction = new Transaction();
714
- vmStateTransaction.add(SystemProgram.createAccount({
715
- fromPubkey: deployerKeypair.publicKey,
716
- newAccountPubkey: vmStateKeypair.publicKey,
717
- lamports: vmStateRent,
718
- space: VM_STATE_SIZE,
719
- programId: programId,
720
- }));
721
- vmStateTransaction.add(new TransactionInstruction({
722
- keys: [
723
- {
724
- pubkey: vmStateKeypair.publicKey,
725
- isSigner: false,
726
- isWritable: true,
727
- },
728
- {
729
- pubkey: deployerKeypair.publicKey,
730
- isSigner: true,
731
- isWritable: false,
732
- },
733
- ],
734
- programId: programId,
735
- data: Buffer.from([0]), // Initialize discriminator
736
- }));
737
- vmStateTransaction.feePayer = deployerKeypair.publicKey;
738
- const vmStateBlockhash = await connection.getLatestBlockhash("confirmed");
739
- vmStateTransaction.recentBlockhash = vmStateBlockhash.blockhash;
740
- vmStateTransaction.partialSign(deployerKeypair);
741
- vmStateTransaction.partialSign(vmStateKeypair);
742
- const vmStateSignature = await connection.sendRawTransaction(vmStateTransaction.serialize(), {
743
- skipPreflight: true,
744
- preflightCommitment: "confirmed",
745
- maxRetries: options.maxRetries || 3,
746
- });
747
- const vmStateConfirmation = await pollForConfirmation(connection, vmStateSignature, "confirmed", 120000, options.debug);
748
- if (!vmStateConfirmation.success) {
749
- return {
750
- success: false,
751
- error: `VM state initialization confirmation failed: ${vmStateConfirmation.error}`,
752
- transactionIds: [vmStateSignature]
753
- };
754
- }
755
- transactionIds.push(vmStateSignature);
756
- if (options.debug) {
757
- console.log(`[FiveSDK] ✅ VM state initialized: ${vmStateSignature}`);
758
- }
759
738
  const chunks = chunkBytecode(bytecode, chunkSize);
760
739
  const firstChunk = chunks[0];
761
740
  const remainingChunks = chunks.slice(1);
@@ -774,13 +753,7 @@ options = {}) {
774
753
  programId: programId,
775
754
  });
776
755
  initTransaction.add(createAccountInstruction);
777
- const sizeBuffer = Buffer.allocUnsafe(4);
778
- sizeBuffer.writeUInt32LE(bytecode.length, 0);
779
- const initInstructionData = Buffer.concat([
780
- Buffer.from([4]), // InitLargeProgramWithChunk discriminator (same as InitLargeProgram)
781
- sizeBuffer,
782
- firstChunk,
783
- ]);
756
+ const initInstructionData = createInitLargeProgramInstructionData(bytecode.length, firstChunk);
784
757
  const initLargeProgramWithChunkInstruction = new TransactionInstruction({
785
758
  keys: [
786
759
  {
@@ -794,10 +767,11 @@ options = {}) {
794
767
  isWritable: true,
795
768
  },
796
769
  {
797
- pubkey: vmStateKeypair.publicKey,
770
+ pubkey: vmStatePubkey,
798
771
  isSigner: false,
799
- isWritable: true,
772
+ isWritable: false,
800
773
  },
774
+ ...feeVaultKeys,
801
775
  ],
802
776
  programId: programId,
803
777
  data: initInstructionData,
@@ -847,10 +821,7 @@ options = {}) {
847
821
  if (options.debug) {
848
822
  console.log(`[FiveSDK] Using single-chunk AppendBytecode for remaining chunk (${chunkGroup[0].length} bytes)`);
849
823
  }
850
- const singleChunkData = Buffer.concat([
851
- Buffer.from([5]), // AppendBytecode discriminator
852
- Buffer.from(chunkGroup[0]),
853
- ]);
824
+ const singleChunkData = createAppendBytecodeInstructionData(chunkGroup[0]);
854
825
  appendInstruction = new TransactionInstruction({
855
826
  keys: [
856
827
  {
@@ -864,10 +835,11 @@ options = {}) {
864
835
  isWritable: true,
865
836
  },
866
837
  {
867
- pubkey: vmStateKeypair.publicKey,
838
+ pubkey: vmStatePubkey,
868
839
  isSigner: false,
869
840
  isWritable: true,
870
841
  },
842
+ ...feeVaultKeys,
871
843
  ],
872
844
  programId: programId,
873
845
  data: singleChunkData,
@@ -889,10 +861,11 @@ options = {}) {
889
861
  isWritable: true,
890
862
  },
891
863
  {
892
- pubkey: vmStateKeypair.publicKey,
864
+ pubkey: vmStatePubkey,
893
865
  isSigner: false,
894
866
  isWritable: true,
895
867
  },
868
+ ...feeVaultKeys,
896
869
  ],
897
870
  programId: programId,
898
871
  data: multiChunkData,
@@ -941,9 +914,10 @@ options = {}) {
941
914
  isSigner: true,
942
915
  isWritable: true,
943
916
  },
917
+ ...feeVaultKeys,
944
918
  ],
945
919
  programId: programId,
946
- data: Buffer.from([7]), // FinalizeScript discriminator
920
+ data: createFinalizeScriptInstructionData(),
947
921
  }));
948
922
  finalizeTransaction.feePayer = deployerKeypair.publicKey;
949
923
  const finalizeBlockhash = await connection.getLatestBlockhash("confirmed");
@@ -983,7 +957,7 @@ options = {}) {
983
957
  totalTransactions: optimizedTransactionCount,
984
958
  deploymentCost: totalCost,
985
959
  chunksUsed: chunks.length,
986
- vmStateAccount: vmStateKeypair.publicKey.toString(),
960
+ vmStateAccount: vmStatePubkey.toString(),
987
961
  optimizationSavings: {
988
962
  transactionsSaved,
989
963
  estimatedCostSaved,
@@ -994,6 +968,7 @@ options = {}) {
994
968
  `💰 Cost: ${totalCost / 1e9} SOL`,
995
969
  `🧩 Chunks: ${chunks.length}`,
996
970
  `⚡ Optimization: ${Math.round((transactionsSaved / traditionalTransactionCount) * 100)}% fewer transactions`,
971
+ `🏦 Fee vault shards initialized: ${feeVaultInitSigs.length}`,
997
972
  ],
998
973
  };
999
974
  }
@@ -1007,18 +982,24 @@ options = {}) {
1007
982
  };
1008
983
  }
1009
984
  }
1010
- function encodeDeployInstruction(bytecode, permissions = 0, metadata = new Uint8Array()) {
985
+ function encodeDeployInstruction(bytecode, permissions = 0, metadata = new Uint8Array(), feeShardIndex = 0, feeVaultBump) {
1011
986
  const lengthBuffer = Buffer.allocUnsafe(4);
1012
987
  lengthBuffer.writeUInt32LE(bytecode.length, 0);
1013
988
  const metadataLenBuffer = Buffer.allocUnsafe(4);
1014
989
  metadataLenBuffer.writeUInt32LE(metadata.length, 0);
1015
- const result = new Uint8Array(1 + 4 + 1 + 4 + metadata.length + bytecode.length);
990
+ const hasFeeTrailer = typeof feeVaultBump === "number";
991
+ const result = new Uint8Array(1 + 4 + 1 + 4 + metadata.length + bytecode.length + (hasFeeTrailer ? 2 : 0));
1016
992
  result[0] = 8; // Deploy discriminator (matches on-chain FIVE program)
1017
993
  result.set(new Uint8Array(lengthBuffer), 1); // u32 LE length at bytes 1-4
1018
994
  result[5] = permissions; // permissions byte at byte 5
1019
995
  result.set(new Uint8Array(metadataLenBuffer), 6); // metadata length at bytes 6-9
1020
996
  result.set(metadata, 10);
1021
997
  result.set(bytecode, 10 + metadata.length);
998
+ if (hasFeeTrailer) {
999
+ const trailerOffset = 10 + metadata.length + bytecode.length;
1000
+ result[trailerOffset] = feeShardIndex & 0xff;
1001
+ result[trailerOffset + 1] = feeVaultBump & 0xff;
1002
+ }
1022
1003
  console.log(`[FiveSDK] Deploy instruction encoded:`, {
1023
1004
  discriminator: result[0],
1024
1005
  lengthBytes: Array.from(new Uint8Array(lengthBuffer)),
@@ -1026,7 +1007,7 @@ function encodeDeployInstruction(bytecode, permissions = 0, metadata = new Uint8
1026
1007
  metadataLength: metadata.length,
1027
1008
  bytecodeLength: bytecode.length,
1028
1009
  totalInstructionLength: result.length,
1029
- expectedFormat: `[8, ${bytecode.length}_as_u32le, 0x${permissions.toString(16).padStart(2, '0')}, ${metadata.length}_as_u32le, metadata_bytes, bytecode_bytes]`,
1010
+ expectedFormat: `[8, ${bytecode.length}_as_u32le, 0x${permissions.toString(16).padStart(2, '0')}, ${metadata.length}_as_u32le, metadata_bytes, bytecode_bytes, optional(shard,bump)]`,
1030
1011
  instructionHex: Buffer.from(result).toString("hex").substring(0, 20) + "...",
1031
1012
  });
1032
1013
  return result;
@@ -1068,6 +1049,54 @@ function encodeExportMetadata(input) {
1068
1049
  }
1069
1050
  return Uint8Array.from(out);
1070
1051
  }
1052
+ async function ensureCanonicalVmStateAccount(connection, deployerKeypair, programId, options = {}) {
1053
+ const { PublicKey, Transaction, TransactionInstruction, SystemProgram } = await import("@solana/web3.js");
1054
+ const canonical = await PDAUtils.deriveVMStatePDA(programId.toString());
1055
+ if (options.vmStateAccount && options.vmStateAccount !== canonical.address) {
1056
+ throw new Error(`vmStateAccount must be canonical PDA ${canonical.address}; got ${options.vmStateAccount}`);
1057
+ }
1058
+ const vmStatePubkey = new PublicKey(canonical.address);
1059
+ const existing = await connection.getAccountInfo(vmStatePubkey);
1060
+ if (existing) {
1061
+ if (existing.owner.toBase58() !== programId.toBase58()) {
1062
+ throw new Error(`canonical VM state ${canonical.address} exists but is owned by ${existing.owner.toBase58()}, expected ${programId.toBase58()}`);
1063
+ }
1064
+ if (options.debug) {
1065
+ console.log(`[FiveSDK] Reusing canonical VM State PDA: ${canonical.address}`);
1066
+ }
1067
+ return { vmStatePubkey, vmStateRent: 0, created: false, bump: canonical.bump };
1068
+ }
1069
+ const VM_STATE_SIZE = 56;
1070
+ const vmStateRent = await connection.getMinimumBalanceForRentExemption(VM_STATE_SIZE);
1071
+ if (options.debug) {
1072
+ console.log(`[FiveSDK] Initializing canonical VM State PDA: ${canonical.address}`);
1073
+ }
1074
+ const initTransaction = new Transaction();
1075
+ initTransaction.add(new TransactionInstruction({
1076
+ keys: [
1077
+ { pubkey: vmStatePubkey, isSigner: false, isWritable: true },
1078
+ { pubkey: deployerKeypair.publicKey, isSigner: true, isWritable: false },
1079
+ { pubkey: deployerKeypair.publicKey, isSigner: true, isWritable: true },
1080
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
1081
+ ],
1082
+ programId,
1083
+ data: buildInitializeVmStateInstructionData(canonical.bump),
1084
+ }));
1085
+ initTransaction.feePayer = deployerKeypair.publicKey;
1086
+ const initBlockhash = await connection.getLatestBlockhash("confirmed");
1087
+ initTransaction.recentBlockhash = initBlockhash.blockhash;
1088
+ initTransaction.partialSign(deployerKeypair);
1089
+ const initSignature = await connection.sendRawTransaction(initTransaction.serialize(), {
1090
+ skipPreflight: true,
1091
+ preflightCommitment: "confirmed",
1092
+ maxRetries: options.maxRetries || 3,
1093
+ });
1094
+ const initConfirmation = await pollForConfirmation(connection, initSignature, "confirmed", 120000, options.debug);
1095
+ if (!initConfirmation.success || initConfirmation.err) {
1096
+ throw new Error(`canonical VM state initialization failed: ${initConfirmation.error || JSON.stringify(initConfirmation.err)}`);
1097
+ }
1098
+ return { vmStatePubkey, vmStateRent, created: true, bump: canonical.bump };
1099
+ }
1071
1100
  function chunkBytecode(bytecode, chunkSize) {
1072
1101
  const chunks = [];
1073
1102
  for (let i = 0; i < bytecode.length; i += chunkSize) {
@@ -1116,3 +1145,27 @@ function createMultiChunkInstructionData(chunks) {
1116
1145
  }
1117
1146
  return Buffer.concat(buffers);
1118
1147
  }
1148
+ function buildInitializeVmStateInstructionData(bump = 0) {
1149
+ return Buffer.from([0, bump & 0xff]);
1150
+ }
1151
+ function createInitLargeProgramInstructionData(expectedSize, firstChunk) {
1152
+ const sizeBuffer = Buffer.allocUnsafe(4);
1153
+ sizeBuffer.writeUInt32LE(expectedSize, 0);
1154
+ const parts = [Buffer.from([4]), sizeBuffer];
1155
+ if (firstChunk && firstChunk.length > 0) {
1156
+ parts.push(Buffer.from(firstChunk));
1157
+ }
1158
+ return Buffer.concat(parts);
1159
+ }
1160
+ function createAppendBytecodeInstructionData(chunk) {
1161
+ return Buffer.concat([Buffer.from([5]), Buffer.from(chunk)]);
1162
+ }
1163
+ function createFinalizeScriptInstructionData() {
1164
+ return Buffer.from([7]);
1165
+ }
1166
+ export const __deployTestUtils = {
1167
+ buildInitializeVmStateInstructionData,
1168
+ createInitLargeProgramInstructionData,
1169
+ createAppendBytecodeInstructionData,
1170
+ createFinalizeScriptInstructionData,
1171
+ };