@5ive-tech/sdk 1.1.2

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 (119) hide show
  1. package/README.md +279 -0
  2. package/dist/FiveSDK.d.ts +336 -0
  3. package/dist/FiveSDK.js +395 -0
  4. package/dist/accounts/index.d.ts +254 -0
  5. package/dist/accounts/index.js +543 -0
  6. package/dist/assets/vm/dummy.file +0 -0
  7. package/dist/assets/vm/five_vm_wasm.d.ts +762 -0
  8. package/dist/assets/vm/five_vm_wasm.js +3754 -0
  9. package/dist/assets/vm/five_vm_wasm_bg.js +3307 -0
  10. package/dist/assets/vm/five_vm_wasm_bg.wasm +0 -0
  11. package/dist/assets/vm/five_vm_wasm_bg.wasm.d.ts +247 -0
  12. package/dist/assets/vm/package.json +11 -0
  13. package/dist/bin/gen-types.d.ts +2 -0
  14. package/dist/bin/gen-types.js +35 -0
  15. package/dist/compiler/BytecodeCompiler.d.ts +83 -0
  16. package/dist/compiler/BytecodeCompiler.js +379 -0
  17. package/dist/config/ConfigManager.d.ts +13 -0
  18. package/dist/config/ConfigManager.js +27 -0
  19. package/dist/config/ProgramIdResolver.d.ts +62 -0
  20. package/dist/config/ProgramIdResolver.js +104 -0
  21. package/dist/crypto/index.d.ts +211 -0
  22. package/dist/crypto/index.js +451 -0
  23. package/dist/encoding/ParameterEncoder.d.ts +31 -0
  24. package/dist/encoding/ParameterEncoder.js +278 -0
  25. package/dist/index.d.ts +21 -0
  26. package/dist/index.js +28 -0
  27. package/dist/lib/bytecode-encoder.d.ts +62 -0
  28. package/dist/lib/bytecode-encoder.js +281 -0
  29. package/dist/logging/index.d.ts +9 -0
  30. package/dist/logging/index.js +10 -0
  31. package/dist/metadata/index.d.ts +213 -0
  32. package/dist/metadata/index.js +296 -0
  33. package/dist/modules/accounts.d.ts +60 -0
  34. package/dist/modules/accounts.js +275 -0
  35. package/dist/modules/deploy.d.ts +90 -0
  36. package/dist/modules/deploy.js +1118 -0
  37. package/dist/modules/execute.d.ts +90 -0
  38. package/dist/modules/execute.js +649 -0
  39. package/dist/modules/fees.d.ts +14 -0
  40. package/dist/modules/fees.js +112 -0
  41. package/dist/modules/namespaces.d.ts +39 -0
  42. package/dist/modules/namespaces.js +190 -0
  43. package/dist/modules/state-diff.d.ts +35 -0
  44. package/dist/modules/state-diff.js +342 -0
  45. package/dist/modules/vm-state.d.ts +7 -0
  46. package/dist/modules/vm-state.js +44 -0
  47. package/dist/program/AccountResolver.d.ts +67 -0
  48. package/dist/program/AccountResolver.js +134 -0
  49. package/dist/program/BorshSchemaGenerator.d.ts +8 -0
  50. package/dist/program/BorshSchemaGenerator.js +57 -0
  51. package/dist/program/FiveProgram.d.ts +144 -0
  52. package/dist/program/FiveProgram.js +282 -0
  53. package/dist/program/FunctionBuilder.d.ts +114 -0
  54. package/dist/program/FunctionBuilder.js +347 -0
  55. package/dist/program/ProgramAccount.d.ts +38 -0
  56. package/dist/program/ProgramAccount.js +170 -0
  57. package/dist/program/TypeGenerator.d.ts +90 -0
  58. package/dist/program/TypeGenerator.js +195 -0
  59. package/dist/program/index.d.ts +24 -0
  60. package/dist/program/index.js +21 -0
  61. package/dist/project/config.d.ts +5 -0
  62. package/dist/project/config.js +33 -0
  63. package/dist/project/toml.d.ts +6 -0
  64. package/dist/project/toml.js +43 -0
  65. package/dist/project/workspace.d.ts +160 -0
  66. package/dist/project/workspace.js +73 -0
  67. package/dist/testing/AccountMetaGenerator.d.ts +121 -0
  68. package/dist/testing/AccountMetaGenerator.js +261 -0
  69. package/dist/testing/AccountTestFixture.d.ts +211 -0
  70. package/dist/testing/AccountTestFixture.js +530 -0
  71. package/dist/testing/OnChainAccountManager.d.ts +81 -0
  72. package/dist/testing/OnChainAccountManager.js +260 -0
  73. package/dist/testing/StateSerializer.d.ts +65 -0
  74. package/dist/testing/StateSerializer.js +330 -0
  75. package/dist/testing/TestDiscovery.d.ts +79 -0
  76. package/dist/testing/TestDiscovery.js +274 -0
  77. package/dist/testing/TestRunner.d.ts +117 -0
  78. package/dist/testing/TestRunner.js +346 -0
  79. package/dist/testing/index.d.ts +14 -0
  80. package/dist/testing/index.js +13 -0
  81. package/dist/types.d.ts +356 -0
  82. package/dist/types.js +32 -0
  83. package/dist/utils/abi.d.ts +31 -0
  84. package/dist/utils/abi.js +92 -0
  85. package/dist/utils/transaction.d.ts +5 -0
  86. package/dist/utils/transaction.js +48 -0
  87. package/dist/validation/InputValidator.d.ts +142 -0
  88. package/dist/validation/InputValidator.js +332 -0
  89. package/dist/validation/index.d.ts +4 -0
  90. package/dist/validation/index.js +4 -0
  91. package/dist/wasm/compiler/AbiLogic.d.ts +4 -0
  92. package/dist/wasm/compiler/AbiLogic.js +37 -0
  93. package/dist/wasm/compiler/AnalysisLogic.d.ts +6 -0
  94. package/dist/wasm/compiler/AnalysisLogic.js +61 -0
  95. package/dist/wasm/compiler/CompilationLogic.d.ts +10 -0
  96. package/dist/wasm/compiler/CompilationLogic.js +431 -0
  97. package/dist/wasm/compiler/FiveCompiler.d.ts +48 -0
  98. package/dist/wasm/compiler/FiveCompiler.js +183 -0
  99. package/dist/wasm/compiler/InfoLogic.d.ts +6 -0
  100. package/dist/wasm/compiler/InfoLogic.js +24 -0
  101. package/dist/wasm/compiler/OptimizationLogic.d.ts +2 -0
  102. package/dist/wasm/compiler/OptimizationLogic.js +13 -0
  103. package/dist/wasm/compiler/ValidationLogic.d.ts +7 -0
  104. package/dist/wasm/compiler/ValidationLogic.js +26 -0
  105. package/dist/wasm/compiler/index.d.ts +2 -0
  106. package/dist/wasm/compiler/index.js +2 -0
  107. package/dist/wasm/compiler/types.d.ts +8 -0
  108. package/dist/wasm/compiler/types.js +1 -0
  109. package/dist/wasm/compiler/utils.d.ts +8 -0
  110. package/dist/wasm/compiler/utils.js +75 -0
  111. package/dist/wasm/index.d.ts +9 -0
  112. package/dist/wasm/index.js +12 -0
  113. package/dist/wasm/instance.d.ts +1 -0
  114. package/dist/wasm/instance.js +26 -0
  115. package/dist/wasm/loader.d.ts +7 -0
  116. package/dist/wasm/loader.js +112 -0
  117. package/dist/wasm/vm.d.ts +33 -0
  118. package/dist/wasm/vm.js +250 -0
  119. package/package.json +59 -0
@@ -0,0 +1,1118 @@
1
+ import { PDAUtils, RentCalculator } from "../crypto/index.js";
2
+ import { validator, Validators } from "../validation/index.js";
3
+ import { calculateDeployFee } from "./fees.js";
4
+ import { pollForConfirmation } from "../utils/transaction.js";
5
+ import { ProgramIdResolver } from "../config/ProgramIdResolver.js";
6
+ export async function generateDeployInstruction(bytecode, deployer, options = {}, connection, fiveVMProgramId) {
7
+ Validators.bytecode(bytecode);
8
+ validator.validateBase58Address(deployer, "deployer");
9
+ Validators.options(options);
10
+ if (options.scriptAccount) {
11
+ validator.validateBase58Address(options.scriptAccount, "options.scriptAccount");
12
+ }
13
+ // Resolve program ID with consistent precedence
14
+ const programId = ProgramIdResolver.resolve(fiveVMProgramId || options.fiveVMProgramId);
15
+ const exportMetadata = encodeExportMetadata(options.exportMetadata);
16
+ if (options.debug) {
17
+ console.log(`[FiveSDK] Generating deployment transaction (${bytecode.length} bytes)...`);
18
+ console.log(`[FiveSDK] Using program ID: ${programId}`);
19
+ }
20
+ const scriptResult = await PDAUtils.deriveScriptAccount(bytecode, programId);
21
+ const scriptAccount = scriptResult.address;
22
+ const scriptSeed = scriptResult.seed;
23
+ const vmStatePDAResult = await PDAUtils.deriveVMStatePDA(programId);
24
+ const vmStatePDA = vmStatePDAResult.address;
25
+ if (options.debug) {
26
+ console.log(`[FiveSDK] Script Account: ${scriptAccount} (seed: ${scriptSeed})`);
27
+ console.log(`[FiveSDK] VM State PDA: ${vmStatePDA}`);
28
+ }
29
+ const SCRIPT_HEADER_SIZE = 64; // ScriptAccountHeader size from Rust program
30
+ const totalAccountSize = SCRIPT_HEADER_SIZE + exportMetadata.length + bytecode.length;
31
+ const rentLamports = await RentCalculator.calculateRentExemption(totalAccountSize);
32
+ const deployAccounts = [
33
+ { pubkey: scriptAccount, isSigner: false, isWritable: true },
34
+ { pubkey: vmStatePDA, isSigner: false, isWritable: true },
35
+ { pubkey: deployer, isSigner: true, isWritable: true },
36
+ {
37
+ pubkey: "11111111111111111111111111111112",
38
+ isSigner: false,
39
+ isWritable: false,
40
+ },
41
+ ];
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);
50
+ const result = {
51
+ programId: programId,
52
+ instruction: {
53
+ programId: programId,
54
+ accounts: deployAccounts,
55
+ data: Buffer.from(instructionData).toString("base64"),
56
+ },
57
+ scriptAccount,
58
+ requiredSigners: [deployer],
59
+ estimatedCost: rentLamports + (options.extraLamports || 0),
60
+ bytecodeSize: bytecode.length,
61
+ setupInstructions: {
62
+ createScriptAccount: {
63
+ pda: scriptAccount,
64
+ seed: scriptSeed,
65
+ space: totalAccountSize,
66
+ rent: rentLamports,
67
+ owner: programId,
68
+ },
69
+ },
70
+ adminAccount: options.adminAccount,
71
+ };
72
+ if (options.debug) {
73
+ console.log(`[FiveSDK] Generated deployment transaction:`, {
74
+ scriptAccount,
75
+ scriptSeed,
76
+ accountSize: totalAccountSize,
77
+ rentCost: rentLamports,
78
+ deployDataSize: instructionData.length,
79
+ exportMetadataSize: exportMetadata.length,
80
+ adminAccount: options.adminAccount,
81
+ });
82
+ }
83
+ const shouldEstimateFees = options.estimateFees !== false && connection;
84
+ if (shouldEstimateFees) {
85
+ try {
86
+ const deployFee = await calculateDeployFee(bytecode.length, connection, programId);
87
+ result.feeInformation = deployFee;
88
+ if (options.debug) {
89
+ console.log(`[FiveSDK] Deploy fee estimate:`, deployFee);
90
+ }
91
+ }
92
+ catch (error) {
93
+ if (options.debug) {
94
+ console.warn(`[FiveSDK] Could not estimate deploy fees:`, error instanceof Error ? error.message : "Unknown error");
95
+ }
96
+ }
97
+ }
98
+ return result;
99
+ }
100
+ export async function createDeploymentTransaction(bytecode, connection, deployerPublicKey, // PublicKey
101
+ options = {}) {
102
+ const { Keypair, PublicKey, Transaction, TransactionInstruction, SystemProgram, ComputeBudgetProgram, } = await import("@solana/web3.js");
103
+ const programIdStr = ProgramIdResolver.resolve(options.fiveVMProgramId);
104
+ const programId = new PublicKey(programIdStr);
105
+ // Generate script keypair
106
+ const scriptKeypair = Keypair.generate();
107
+ const scriptAccount = scriptKeypair.publicKey.toString();
108
+ // Calculate account size and rent
109
+ const SCRIPT_HEADER_SIZE = 64; // ScriptHeader::LEN
110
+ const exportMetadata = encodeExportMetadata(options.exportMetadata);
111
+ const totalAccountSize = SCRIPT_HEADER_SIZE + exportMetadata.length + bytecode.length;
112
+ 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);
117
+ if (options.debug) {
118
+ console.log(`[FiveSDK] Preparing deployment transaction:`);
119
+ console.log(` - Script Account: ${scriptAccount}`);
120
+ console.log(` - VM State Account: ${vmStateKeypair.publicKey.toString()}`);
121
+ console.log(` - Deployer: ${deployerPublicKey.toString()}`);
122
+ }
123
+ const tx = new Transaction();
124
+ // Add compute budget if requested
125
+ if (options.computeBudget && options.computeBudget > 0) {
126
+ tx.add(ComputeBudgetProgram.setComputeUnitLimit({
127
+ units: options.computeBudget,
128
+ }));
129
+ }
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
148
+ tx.add(SystemProgram.createAccount({
149
+ fromPubkey: deployerPublicKey,
150
+ newAccountPubkey: scriptKeypair.publicKey,
151
+ lamports: rentLamports,
152
+ space: totalAccountSize,
153
+ programId: programId,
154
+ }));
155
+ // 4. Deploy Instruction
156
+ const deployData = encodeDeployInstruction(bytecode, 0, exportMetadata);
157
+ tx.add(new TransactionInstruction({
158
+ keys: [
159
+ { pubkey: scriptKeypair.publicKey, isSigner: false, isWritable: true },
160
+ { pubkey: vmStateKeypair.publicKey, isSigner: false, isWritable: true },
161
+ { pubkey: deployerPublicKey, isSigner: true, isWritable: true },
162
+ ],
163
+ programId: programId,
164
+ data: Buffer.from(deployData),
165
+ }));
166
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
167
+ tx.recentBlockhash = blockhash;
168
+ tx.feePayer = deployerPublicKey;
169
+ // Partial sign with generated keys
170
+ tx.partialSign(scriptKeypair);
171
+ tx.partialSign(vmStateKeypair);
172
+ return {
173
+ transaction: tx,
174
+ scriptKeypair,
175
+ vmStateKeypair,
176
+ programId: scriptAccount,
177
+ rentLamports,
178
+ };
179
+ }
180
+ export async function deployToSolana(bytecode, connection, // Solana Connection object
181
+ deployerKeypair, // Solana Keypair object
182
+ options = {}) {
183
+ console.log(`[FiveSDK] deployToSolana called with bytecode length: ${bytecode.length}`);
184
+ console.log(`[FiveSDK] options:`, options);
185
+ // Resolve program ID with consistent precedence
186
+ const programId = ProgramIdResolver.resolve(options.fiveVMProgramId);
187
+ try {
188
+ if (options.debug) {
189
+ console.log(`[FiveSDK] Starting deployment with ${bytecode.length} bytes of bytecode to program ${programId}`);
190
+ }
191
+ // Generate script keypair like frontend-five
192
+ const { Keypair, PublicKey, Transaction, TransactionInstruction, SystemProgram, } = await import("@solana/web3.js");
193
+ const scriptKeypair = Keypair.generate();
194
+ const scriptAccount = scriptKeypair.publicKey.toString();
195
+ if (options.debug) {
196
+ console.log(`[FiveSDK] Generated script keypair: ${scriptAccount}`);
197
+ }
198
+ // Calculate account size and rent
199
+ const SCRIPT_HEADER_SIZE = 64; // ScriptHeader::LEN (five-protocol)
200
+ const exportMetadata = encodeExportMetadata(options.exportMetadata);
201
+ const totalAccountSize = SCRIPT_HEADER_SIZE + exportMetadata.length + bytecode.length;
202
+ 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
+ }
219
+ if (options.debug) {
220
+ console.log(`[FiveSDK] Script Account: ${scriptAccount}`);
221
+ console.log(`[FiveSDK] VM State Account: ${vmStatePubkey.toString()}`);
222
+ console.log(`[FiveSDK] Account size: ${totalAccountSize} bytes`);
223
+ console.log(`[FiveSDK] Export metadata size: ${exportMetadata.length} bytes`);
224
+ console.log(`[FiveSDK] Rent cost: ${((rentLamports + vmStateRent) / 1e9)} SOL`);
225
+ }
226
+ // SINGLE TRANSACTION: create VM state + initialize + create script account + deploy bytecode
227
+ const tx = new Transaction();
228
+ // Optional compute budget
229
+ if (options.computeBudget && options.computeBudget > 0) {
230
+ try {
231
+ const { ComputeBudgetProgram } = await import("@solana/web3.js");
232
+ tx.add(ComputeBudgetProgram.setComputeUnitLimit({
233
+ units: options.computeBudget,
234
+ }));
235
+ }
236
+ catch { }
237
+ }
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
268
+ const createAccountIx = SystemProgram.createAccount({
269
+ fromPubkey: deployerKeypair.publicKey,
270
+ newAccountPubkey: scriptKeypair.publicKey,
271
+ lamports: rentLamports,
272
+ space: totalAccountSize,
273
+ programId: new PublicKey(programId),
274
+ });
275
+ tx.add(createAccountIx);
276
+ const deployData = encodeDeployInstruction(bytecode, 0, exportMetadata);
277
+ const instructionDataBuffer = Buffer.from(deployData);
278
+ const deployIx = new TransactionInstruction({
279
+ keys: [
280
+ {
281
+ pubkey: scriptKeypair.publicKey,
282
+ isSigner: false,
283
+ isWritable: true,
284
+ },
285
+ {
286
+ pubkey: vmStatePubkey,
287
+ isSigner: false,
288
+ isWritable: true,
289
+ },
290
+ {
291
+ pubkey: deployerKeypair.publicKey,
292
+ isSigner: true,
293
+ isWritable: true,
294
+ },
295
+ ],
296
+ programId: new PublicKey(programId),
297
+ data: instructionDataBuffer,
298
+ });
299
+ tx.add(deployIx);
300
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
301
+ tx.recentBlockhash = blockhash;
302
+ tx.feePayer = deployerKeypair.publicKey;
303
+ tx.partialSign(deployerKeypair);
304
+ if (!options.vmStateAccount) {
305
+ tx.partialSign(vmStateKeypair);
306
+ }
307
+ tx.partialSign(scriptKeypair);
308
+ const txSerialized = tx.serialize();
309
+ if (options.debug) {
310
+ console.log(`[FiveSDK] Transaction serialized: ${txSerialized.length} bytes`);
311
+ }
312
+ const signature = await connection.sendRawTransaction(txSerialized, {
313
+ skipPreflight: true,
314
+ preflightCommitment: "confirmed",
315
+ maxRetries: options.maxRetries || 3,
316
+ });
317
+ if (options.debug) {
318
+ console.log(`[FiveSDK] sendRawTransaction completed, returned signature: ${signature}`);
319
+ }
320
+ // Custom confirmation polling with extended timeout (120 seconds)
321
+ const confirmationResult = await pollForConfirmation(connection, signature, "confirmed", 120000, // 120 second timeout
322
+ options.debug);
323
+ if (!confirmationResult.success) {
324
+ const errorMessage = `Deployment confirmation failed: ${confirmationResult.error || "Unknown error"}`;
325
+ if (options.debug)
326
+ console.log(`[FiveSDK] ${errorMessage}`);
327
+ return {
328
+ success: false,
329
+ error: errorMessage,
330
+ transactionId: signature,
331
+ };
332
+ }
333
+ if (confirmationResult.err) {
334
+ const errorMessage = `Combined deployment failed: ${JSON.stringify(confirmationResult.err)}`;
335
+ if (options.debug)
336
+ console.log(`[FiveSDK] ${errorMessage}`);
337
+ return {
338
+ success: false,
339
+ error: errorMessage,
340
+ transactionId: signature,
341
+ };
342
+ }
343
+ if (options.debug) {
344
+ console.log(`[FiveSDK] Combined deployment succeeded: ${signature}`);
345
+ }
346
+ return {
347
+ success: true,
348
+ programId: scriptAccount,
349
+ transactionId: signature,
350
+ deploymentCost: rentLamports,
351
+ logs: [
352
+ `Script Account: ${scriptAccount}`,
353
+ `Deployment TX: ${signature}`,
354
+ `Deployment cost (rent): ${rentLamports / 1e9} SOL`,
355
+ `Bytecode size: ${bytecode.length} bytes`,
356
+ `VM State Account: ${vmStatePubkey.toString()}`,
357
+ ],
358
+ vmStateAccount: vmStatePubkey.toString(),
359
+ };
360
+ }
361
+ catch (error) {
362
+ const errorMessage = error instanceof Error ? error.message : "Unknown deployment error";
363
+ if (options.debug) {
364
+ console.error(`[FiveSDK] Deployment failed: ${errorMessage}`);
365
+ }
366
+ return {
367
+ success: false,
368
+ error: errorMessage,
369
+ logs: [],
370
+ };
371
+ }
372
+ }
373
+ export async function deployLargeProgramToSolana(bytecode, connection, // Solana Connection object
374
+ deployerKeypair, // Solana Keypair object
375
+ options = {}) {
376
+ const DEFAULT_CHUNK_SIZE = 500; // Leaves room for transaction overhead
377
+ const chunkSize = options.chunkSize || DEFAULT_CHUNK_SIZE;
378
+ console.log(`[FiveSDK] deployLargeProgramToSolana called with ${bytecode.length} bytes`);
379
+ console.log(`[FiveSDK] Using chunk size: ${chunkSize} bytes`);
380
+ console.log(`[FiveSDK] options:`, options);
381
+ try {
382
+ // If bytecode is small enough, use regular deployment
383
+ if (bytecode.length <= 800) {
384
+ if (options.debug) {
385
+ console.log(`[FiveSDK] Bytecode is small (${bytecode.length} bytes), using regular deployment`);
386
+ }
387
+ return await deployToSolana(bytecode, connection, deployerKeypair, {
388
+ debug: options.debug,
389
+ network: options.network,
390
+ maxRetries: options.maxRetries,
391
+ fiveVMProgramId: options.fiveVMProgramId,
392
+ vmStateAccount: options.vmStateAccount,
393
+ });
394
+ }
395
+ const { Keypair, PublicKey, Transaction, TransactionInstruction, SystemProgram, } = await import("@solana/web3.js");
396
+ // Generate script keypair
397
+ const scriptKeypair = Keypair.generate();
398
+ const scriptAccount = scriptKeypair.publicKey.toString();
399
+ // Calculate account size and rent
400
+ const SCRIPT_HEADER_SIZE = 64; // ScriptHeader::LEN (five-protocol)
401
+ const totalAccountSize = SCRIPT_HEADER_SIZE + bytecode.length;
402
+ const rentLamports = await connection.getMinimumBalanceForRentExemption(totalAccountSize);
403
+ const programIdStr = ProgramIdResolver.resolve(options.fiveVMProgramId);
404
+ 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
+ }
422
+ if (options.debug) {
423
+ console.log(`[FiveSDK] Script Account: ${scriptAccount}`);
424
+ console.log(`[FiveSDK] VM State Account: ${vmStatePubkey.toString()}`);
425
+ console.log(`[FiveSDK] Total account size: ${totalAccountSize} bytes`);
426
+ console.log(`[FiveSDK] Initial rent cost: ${(rentLamports + vmStateRent) / 1e9} SOL`);
427
+ }
428
+ const transactionIds = [];
429
+ 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
+ // TRANSACTION 1: Create Account + InitLargeProgram
476
+ if (options.debug) {
477
+ console.log(`[FiveSDK] Step 1: Create account and initialize large program`);
478
+ }
479
+ const initTransaction = new Transaction();
480
+ // Add account creation instruction
481
+ const createAccountInstruction = SystemProgram.createAccount({
482
+ fromPubkey: deployerKeypair.publicKey,
483
+ newAccountPubkey: scriptKeypair.publicKey,
484
+ lamports: rentLamports,
485
+ space: SCRIPT_HEADER_SIZE, // Start with just header space
486
+ programId: programId,
487
+ });
488
+ initTransaction.add(createAccountInstruction);
489
+ // 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
+ ]);
494
+ const initLargeProgramInstruction = new TransactionInstruction({
495
+ keys: [
496
+ {
497
+ pubkey: scriptKeypair.publicKey,
498
+ isSigner: false,
499
+ isWritable: true,
500
+ },
501
+ {
502
+ pubkey: deployerKeypair.publicKey,
503
+ isSigner: true,
504
+ isWritable: true,
505
+ },
506
+ {
507
+ pubkey: vmStatePubkey,
508
+ isSigner: false,
509
+ isWritable: true,
510
+ },
511
+ ],
512
+ programId: programId,
513
+ data: initInstructionData,
514
+ });
515
+ initTransaction.add(initLargeProgramInstruction);
516
+ // Sign and send initialization transaction
517
+ initTransaction.feePayer = deployerKeypair.publicKey;
518
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
519
+ initTransaction.recentBlockhash = blockhash;
520
+ initTransaction.partialSign(deployerKeypair);
521
+ initTransaction.partialSign(scriptKeypair);
522
+ const initSignature = await connection.sendRawTransaction(initTransaction.serialize(), {
523
+ skipPreflight: true,
524
+ preflightCommitment: "confirmed",
525
+ maxRetries: options.maxRetries || 3,
526
+ });
527
+ await connection.confirmTransaction(initSignature, "confirmed");
528
+ transactionIds.push(initSignature);
529
+ if (options.debug) {
530
+ console.log(`[FiveSDK] ✅ Initialization completed: ${initSignature}`);
531
+ }
532
+ // STEP 2: Split bytecode into chunks and append each
533
+ const chunks = chunkBytecode(bytecode, chunkSize);
534
+ if (options.debug) {
535
+ console.log(`[FiveSDK] Split bytecode into ${chunks.length} chunks`);
536
+ }
537
+ for (let i = 0; i < chunks.length; i++) {
538
+ const chunk = chunks[i];
539
+ if (options.progressCallback) {
540
+ options.progressCallback(i + 1, chunks.length);
541
+ }
542
+ if (options.debug) {
543
+ console.log(`[FiveSDK] Step ${i + 2}: Appending chunk ${i + 1}/${chunks.length} (${chunk.length} bytes)`);
544
+ }
545
+ // Calculate additional rent needed for this chunk
546
+ let currentInfo = await connection.getAccountInfo(scriptKeypair.publicKey);
547
+ // Retry logic for account info if null (eventual consistency)
548
+ if (!currentInfo) {
549
+ if (options.debug)
550
+ console.log(`[FiveSDK] Account info null, retrying...`);
551
+ await new Promise(resolve => setTimeout(resolve, 1000));
552
+ currentInfo = await connection.getAccountInfo(scriptKeypair.publicKey);
553
+ if (!currentInfo)
554
+ throw new Error("Script account not found after initialization");
555
+ }
556
+ const newSize = currentInfo.data.length + chunk.length;
557
+ const newRentRequired = await connection.getMinimumBalanceForRentExemption(newSize);
558
+ const additionalRent = Math.max(0, newRentRequired - currentInfo.lamports);
559
+ const appendTransaction = new Transaction();
560
+ // Add rent if needed
561
+ if (additionalRent > 0) {
562
+ if (options.debug) {
563
+ console.log(`[FiveSDK] Adding ${additionalRent / 1e9} SOL for increased rent`);
564
+ }
565
+ appendTransaction.add(SystemProgram.transfer({
566
+ fromPubkey: deployerKeypair.publicKey,
567
+ toPubkey: scriptKeypair.publicKey,
568
+ lamports: additionalRent,
569
+ }));
570
+ }
571
+ // Add compute budget for final chunk (verification is expensive)
572
+ if (i === chunks.length - 1) {
573
+ try {
574
+ const { ComputeBudgetProgram } = await import("@solana/web3.js");
575
+ if (options.debug)
576
+ console.log("[FiveSDK] Adding 1.4M CU limit for final chunk verification");
577
+ appendTransaction.add(ComputeBudgetProgram.setComputeUnitLimit({
578
+ units: 1400000,
579
+ }));
580
+ }
581
+ catch (e) {
582
+ if (options.debug)
583
+ console.warn("[FiveSDK] Failed to add compute budget:", e);
584
+ }
585
+ }
586
+ if (additionalRent > 0) {
587
+ totalCost += additionalRent;
588
+ }
589
+ // Add AppendBytecode instruction (discriminator 5 + chunk data)
590
+ const appendInstructionData = Buffer.concat([
591
+ Buffer.from([5]), // AppendBytecode discriminator
592
+ chunk,
593
+ ]);
594
+ const appendBytecodeInstruction = new TransactionInstruction({
595
+ keys: [
596
+ {
597
+ pubkey: scriptKeypair.publicKey,
598
+ isSigner: false,
599
+ isWritable: true,
600
+ },
601
+ {
602
+ pubkey: deployerKeypair.publicKey,
603
+ isSigner: true,
604
+ isWritable: true,
605
+ },
606
+ {
607
+ pubkey: vmStatePubkey,
608
+ isSigner: false,
609
+ isWritable: true,
610
+ },
611
+ ],
612
+ programId: programId,
613
+ data: appendInstructionData,
614
+ });
615
+ appendTransaction.add(appendBytecodeInstruction);
616
+ // Sign and send append transaction
617
+ const appendBlockhash = await connection.getLatestBlockhash("confirmed");
618
+ appendTransaction.feePayer = deployerKeypair.publicKey;
619
+ appendTransaction.recentBlockhash = appendBlockhash.blockhash;
620
+ appendTransaction.partialSign(deployerKeypair);
621
+ const appendSignature = await connection.sendRawTransaction(appendTransaction.serialize(), {
622
+ skipPreflight: true,
623
+ preflightCommitment: "confirmed",
624
+ maxRetries: options.maxRetries || 3,
625
+ });
626
+ await connection.confirmTransaction(appendSignature, "confirmed");
627
+ transactionIds.push(appendSignature);
628
+ if (options.debug) {
629
+ console.log(`[FiveSDK] ✅ Chunk ${i + 1} appended: ${appendSignature}`);
630
+ }
631
+ }
632
+ // Final verification
633
+ const finalInfo = await connection.getAccountInfo(scriptKeypair.publicKey);
634
+ const expectedSize = SCRIPT_HEADER_SIZE + bytecode.length;
635
+ if (options.debug) {
636
+ console.log(`[FiveSDK] 🔍 Final verification:`);
637
+ console.log(`[FiveSDK] Expected size: ${expectedSize} bytes`);
638
+ console.log(`[FiveSDK] Actual size: ${finalInfo.data.length} bytes`);
639
+ console.log(`[FiveSDK] Match: ${finalInfo.data.length === expectedSize ? "✅ YES" : "❌ NO"}`);
640
+ }
641
+ return {
642
+ success: true,
643
+ scriptAccount,
644
+ transactionIds,
645
+ totalTransactions: transactionIds.length,
646
+ deploymentCost: totalCost,
647
+ chunksUsed: chunks.length,
648
+ vmStateAccount: vmStatePubkey.toString(),
649
+ logs: [
650
+ `Deployed ${bytecode.length} bytes in ${chunks.length} chunks using ${transactionIds.length} transactions`,
651
+ ],
652
+ };
653
+ }
654
+ catch (error) {
655
+ const errorMessage = error instanceof Error
656
+ ? error.message
657
+ : "Unknown large deployment error";
658
+ if (options.debug) {
659
+ console.error(`[FiveSDK] Large deployment failed: ${errorMessage}`);
660
+ }
661
+ return {
662
+ success: false,
663
+ error: errorMessage,
664
+ logs: [],
665
+ };
666
+ }
667
+ }
668
+ export async function deployLargeProgramOptimizedToSolana(bytecode, connection, // Solana Connection object
669
+ deployerKeypair, // Solana Keypair object
670
+ options = {}) {
671
+ const OPTIMIZED_CHUNK_SIZE = 500; // Larger chunks due to reduced transaction overhead
672
+ const chunkSize = options.chunkSize || OPTIMIZED_CHUNK_SIZE;
673
+ console.log(`[FiveSDK] deployLargeProgramOptimizedToSolana called with ${bytecode.length} bytes`);
674
+ console.log(`[FiveSDK] Using optimized chunk size: ${chunkSize} bytes`);
675
+ console.log(`[FiveSDK] Expected optimization: 50-70% fewer transactions`);
676
+ try {
677
+ // If bytecode is small enough, use regular deployment
678
+ if (bytecode.length <= 800) {
679
+ if (options.debug) {
680
+ console.log(`[FiveSDK] Bytecode is small (${bytecode.length} bytes), using regular deployment`);
681
+ }
682
+ return await deployToSolana(bytecode, connection, deployerKeypair, {
683
+ debug: options.debug,
684
+ network: options.network,
685
+ maxRetries: options.maxRetries,
686
+ });
687
+ }
688
+ const { Keypair, PublicKey, Transaction, TransactionInstruction, SystemProgram, } = await import("@solana/web3.js");
689
+ // Generate script keypair
690
+ const scriptKeypair = Keypair.generate();
691
+ const scriptAccount = scriptKeypair.publicKey.toString();
692
+ // Calculate full account size upfront
693
+ const SCRIPT_HEADER_SIZE = 128; // FIVEScriptHeaderV2::LEN
694
+ const totalAccountSize = SCRIPT_HEADER_SIZE + bytecode.length;
695
+ const rentLamports = await connection.getMinimumBalanceForRentExemption(totalAccountSize);
696
+ const programIdStr = ProgramIdResolver.resolve(options.fiveVMProgramId);
697
+ 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);
702
+ if (options.debug) {
703
+ console.log(`[FiveSDK] Script Account: ${scriptAccount}`);
704
+ console.log(`[FiveSDK] VM State Account: ${vmStateKeypair.publicKey.toString()}`);
705
+ console.log(`[FiveSDK] PRE-ALLOCATED full account size: ${totalAccountSize} bytes`);
706
+ console.log(`[FiveSDK] Full rent cost paid upfront: ${(rentLamports + vmStateRent) / 1e9} SOL`);
707
+ }
708
+ const transactionIds = [];
709
+ 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
+ const chunks = chunkBytecode(bytecode, chunkSize);
760
+ const firstChunk = chunks[0];
761
+ const remainingChunks = chunks.slice(1);
762
+ if (options.debug) {
763
+ console.log(`[FiveSDK] Split into ${chunks.length} chunks (first: ${firstChunk.length} bytes, remaining: ${remainingChunks.length})`);
764
+ }
765
+ if (options.debug) {
766
+ console.log(`[FiveSDK] Create account + initialize with first chunk (${firstChunk.length} bytes)`);
767
+ }
768
+ const initTransaction = new Transaction();
769
+ const createAccountInstruction = SystemProgram.createAccount({
770
+ fromPubkey: deployerKeypair.publicKey,
771
+ newAccountPubkey: scriptKeypair.publicKey,
772
+ lamports: rentLamports,
773
+ space: totalAccountSize,
774
+ programId: programId,
775
+ });
776
+ 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
+ ]);
784
+ const initLargeProgramWithChunkInstruction = new TransactionInstruction({
785
+ keys: [
786
+ {
787
+ pubkey: scriptKeypair.publicKey,
788
+ isSigner: false,
789
+ isWritable: true,
790
+ },
791
+ {
792
+ pubkey: deployerKeypair.publicKey,
793
+ isSigner: true,
794
+ isWritable: true,
795
+ },
796
+ {
797
+ pubkey: vmStateKeypair.publicKey,
798
+ isSigner: false,
799
+ isWritable: true,
800
+ },
801
+ ],
802
+ programId: programId,
803
+ data: initInstructionData,
804
+ });
805
+ initTransaction.add(initLargeProgramWithChunkInstruction);
806
+ initTransaction.feePayer = deployerKeypair.publicKey;
807
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
808
+ initTransaction.recentBlockhash = blockhash;
809
+ initTransaction.partialSign(deployerKeypair);
810
+ initTransaction.partialSign(scriptKeypair);
811
+ const initSignature = await connection.sendRawTransaction(initTransaction.serialize(), {
812
+ skipPreflight: true,
813
+ preflightCommitment: "confirmed",
814
+ maxRetries: options.maxRetries || 3,
815
+ });
816
+ const initConfirmation = await pollForConfirmation(connection, initSignature, "confirmed", 120000, options.debug);
817
+ if (!initConfirmation.success) {
818
+ return {
819
+ success: false,
820
+ error: `Initialization confirmation failed: ${initConfirmation.error}`,
821
+ transactionIds
822
+ };
823
+ }
824
+ transactionIds.push(initSignature);
825
+ if (options.debug) {
826
+ console.log(`[FiveSDK] ✅ Optimized initialization completed: ${initSignature}`);
827
+ console.log(`[FiveSDK] First chunk (${firstChunk.length} bytes) included in initialization!`);
828
+ }
829
+ // Group remaining chunks into multi-chunk transactions
830
+ if (remainingChunks.length > 0) {
831
+ const groupedChunks = groupChunksForOptimalTransactions(remainingChunks, 500); // Leave room for multi-chunk overhead
832
+ if (options.debug) {
833
+ console.log(`[FiveSDK] Grouped ${remainingChunks.length} remaining chunks into ${groupedChunks.length} transactions`);
834
+ }
835
+ for (let groupIdx = 0; groupIdx < groupedChunks.length; groupIdx++) {
836
+ const chunkGroup = groupedChunks[groupIdx];
837
+ if (options.progressCallback) {
838
+ options.progressCallback(groupIdx + 2, groupedChunks.length + 1); // +1 for init transaction
839
+ }
840
+ if (options.debug) {
841
+ console.log(`[FiveSDK] Step ${groupIdx + 2}: Appending ${chunkGroup.length} chunks in single transaction`);
842
+ }
843
+ const appendTransaction = new Transaction();
844
+ let appendInstruction; // TransactionInstruction from @solana/web3.js
845
+ if (chunkGroup.length === 1) {
846
+ // Use single-chunk AppendBytecode instruction for optimization fallback
847
+ if (options.debug) {
848
+ console.log(`[FiveSDK] Using single-chunk AppendBytecode for remaining chunk (${chunkGroup[0].length} bytes)`);
849
+ }
850
+ const singleChunkData = Buffer.concat([
851
+ Buffer.from([5]), // AppendBytecode discriminator
852
+ Buffer.from(chunkGroup[0]),
853
+ ]);
854
+ appendInstruction = new TransactionInstruction({
855
+ keys: [
856
+ {
857
+ pubkey: scriptKeypair.publicKey,
858
+ isSigner: false,
859
+ isWritable: true,
860
+ },
861
+ {
862
+ pubkey: deployerKeypair.publicKey,
863
+ isSigner: true,
864
+ isWritable: true,
865
+ },
866
+ {
867
+ pubkey: vmStateKeypair.publicKey,
868
+ isSigner: false,
869
+ isWritable: true,
870
+ },
871
+ ],
872
+ programId: programId,
873
+ data: singleChunkData,
874
+ });
875
+ }
876
+ else {
877
+ // Use multi-chunk instruction for groups with 2+ chunks
878
+ const multiChunkData = createMultiChunkInstructionData(chunkGroup);
879
+ appendInstruction = new TransactionInstruction({
880
+ keys: [
881
+ {
882
+ pubkey: scriptKeypair.publicKey,
883
+ isSigner: false,
884
+ isWritable: true,
885
+ },
886
+ {
887
+ pubkey: deployerKeypair.publicKey,
888
+ isSigner: true,
889
+ isWritable: true,
890
+ },
891
+ {
892
+ pubkey: vmStateKeypair.publicKey,
893
+ isSigner: false,
894
+ isWritable: true,
895
+ },
896
+ ],
897
+ programId: programId,
898
+ data: multiChunkData,
899
+ });
900
+ }
901
+ appendTransaction.add(appendInstruction);
902
+ // Sign and send multi-chunk transaction
903
+ const appendBlockhash = await connection.getLatestBlockhash("confirmed");
904
+ appendTransaction.feePayer = deployerKeypair.publicKey;
905
+ appendTransaction.recentBlockhash = appendBlockhash.blockhash;
906
+ appendTransaction.partialSign(deployerKeypair);
907
+ const appendSignature = await connection.sendRawTransaction(appendTransaction.serialize(), {
908
+ skipPreflight: true,
909
+ preflightCommitment: "confirmed",
910
+ maxRetries: options.maxRetries || 3,
911
+ });
912
+ const appendConfirmation = await pollForConfirmation(connection, appendSignature, "confirmed", 120000, options.debug);
913
+ if (!appendConfirmation.success) {
914
+ return {
915
+ success: false,
916
+ error: `Append confirmation failed: ${appendConfirmation.error}`,
917
+ transactionIds
918
+ };
919
+ }
920
+ transactionIds.push(appendSignature);
921
+ if (options.debug) {
922
+ console.log(`[FiveSDK] ✅ Multi-chunk append completed: ${appendSignature}`);
923
+ console.log(`[FiveSDK] Appended ${chunkGroup.length} chunks totaling ${chunkGroup.reduce((sum, chunk) => sum + chunk.length, 0)} bytes`);
924
+ }
925
+ }
926
+ }
927
+ // Explicitly finalize the script to ensure upload_mode is cleared
928
+ if (options.debug) {
929
+ console.log(`[FiveSDK] Sending FinalizeScript instruction to complete deployment`);
930
+ }
931
+ const finalizeTransaction = new Transaction();
932
+ finalizeTransaction.add(new TransactionInstruction({
933
+ keys: [
934
+ {
935
+ pubkey: scriptKeypair.publicKey,
936
+ isSigner: false,
937
+ isWritable: true,
938
+ },
939
+ {
940
+ pubkey: deployerKeypair.publicKey,
941
+ isSigner: true,
942
+ isWritable: true,
943
+ },
944
+ ],
945
+ programId: programId,
946
+ data: Buffer.from([7]), // FinalizeScript discriminator
947
+ }));
948
+ finalizeTransaction.feePayer = deployerKeypair.publicKey;
949
+ const finalizeBlockhash = await connection.getLatestBlockhash("confirmed");
950
+ finalizeTransaction.recentBlockhash = finalizeBlockhash.blockhash;
951
+ finalizeTransaction.partialSign(deployerKeypair);
952
+ const finalizeSignature = await connection.sendRawTransaction(finalizeTransaction.serialize(), {
953
+ skipPreflight: true,
954
+ preflightCommitment: "confirmed",
955
+ maxRetries: options.maxRetries || 3,
956
+ });
957
+ // Use custom polling for finalize to handle validator latency
958
+ const finalizeConfirmation = await pollForConfirmation(connection, finalizeSignature, "confirmed", 120000, // 120 second timeout
959
+ options.debug);
960
+ if (!finalizeConfirmation.success) {
961
+ console.error(`[FiveSDK] FinalizeScript confirmation failed: ${finalizeConfirmation.error}`);
962
+ }
963
+ transactionIds.push(finalizeSignature);
964
+ if (options.debug) {
965
+ console.log(`[FiveSDK] ✅ FinalizeScript completed: ${finalizeSignature}`);
966
+ }
967
+ // Calculate optimization savings
968
+ const traditionalTransactionCount = 1 + chunks.length; // 1 init + N appends
969
+ const optimizedTransactionCount = transactionIds.length;
970
+ const transactionsSaved = traditionalTransactionCount - optimizedTransactionCount;
971
+ const estimatedCostSaved = transactionsSaved * 0.000005 * 1e9; // Estimate 5000 lamports per transaction saved
972
+ if (options.debug) {
973
+ console.log(`[FiveSDK] 🎉 OPTIMIZATION RESULTS:`);
974
+ console.log(`[FiveSDK] Traditional method: ${traditionalTransactionCount} transactions`);
975
+ console.log(`[FiveSDK] Optimized method: ${optimizedTransactionCount} transactions`);
976
+ console.log(`[FiveSDK] Transactions saved: ${transactionsSaved} (${Math.round((transactionsSaved / traditionalTransactionCount) * 100)}% reduction)`);
977
+ console.log(`[FiveSDK] Estimated cost saved: ${estimatedCostSaved / 1e9} SOL`);
978
+ }
979
+ return {
980
+ success: true,
981
+ scriptAccount,
982
+ transactionIds,
983
+ totalTransactions: optimizedTransactionCount,
984
+ deploymentCost: totalCost,
985
+ chunksUsed: chunks.length,
986
+ vmStateAccount: vmStateKeypair.publicKey.toString(),
987
+ optimizationSavings: {
988
+ transactionsSaved,
989
+ estimatedCostSaved,
990
+ },
991
+ logs: [
992
+ `✅ Optimized deployment completed`,
993
+ `📊 ${optimizedTransactionCount} transactions (saved ${transactionsSaved} vs traditional)`,
994
+ `💰 Cost: ${totalCost / 1e9} SOL`,
995
+ `🧩 Chunks: ${chunks.length}`,
996
+ `⚡ Optimization: ${Math.round((transactionsSaved / traditionalTransactionCount) * 100)}% fewer transactions`,
997
+ ],
998
+ };
999
+ }
1000
+ catch (error) {
1001
+ console.error("[FiveSDK] Optimized deployment failed:", error);
1002
+ const errorMessage = error instanceof Error ? error.message : "Unknown deployment error";
1003
+ return {
1004
+ success: false,
1005
+ error: errorMessage,
1006
+ logs: [],
1007
+ };
1008
+ }
1009
+ }
1010
+ function encodeDeployInstruction(bytecode, permissions = 0, metadata = new Uint8Array()) {
1011
+ const lengthBuffer = Buffer.allocUnsafe(4);
1012
+ lengthBuffer.writeUInt32LE(bytecode.length, 0);
1013
+ const metadataLenBuffer = Buffer.allocUnsafe(4);
1014
+ metadataLenBuffer.writeUInt32LE(metadata.length, 0);
1015
+ const result = new Uint8Array(1 + 4 + 1 + 4 + metadata.length + bytecode.length);
1016
+ result[0] = 8; // Deploy discriminator (matches on-chain FIVE program)
1017
+ result.set(new Uint8Array(lengthBuffer), 1); // u32 LE length at bytes 1-4
1018
+ result[5] = permissions; // permissions byte at byte 5
1019
+ result.set(new Uint8Array(metadataLenBuffer), 6); // metadata length at bytes 6-9
1020
+ result.set(metadata, 10);
1021
+ result.set(bytecode, 10 + metadata.length);
1022
+ console.log(`[FiveSDK] Deploy instruction encoded:`, {
1023
+ discriminator: result[0],
1024
+ lengthBytes: Array.from(new Uint8Array(lengthBuffer)),
1025
+ permissions: result[5],
1026
+ metadataLength: metadata.length,
1027
+ bytecodeLength: bytecode.length,
1028
+ 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]`,
1030
+ instructionHex: Buffer.from(result).toString("hex").substring(0, 20) + "...",
1031
+ });
1032
+ return result;
1033
+ }
1034
+ function encodeExportMetadata(input) {
1035
+ if (!input) {
1036
+ return new Uint8Array();
1037
+ }
1038
+ const methods = (input.methods || []).filter((m) => typeof m === "string" && m.length > 0);
1039
+ const interfaces = (input.interfaces || []).filter((i) => i && typeof i.name === "string" && i.name.length > 0);
1040
+ const out = [];
1041
+ out.push(0x35, 0x45, 0x58, 0x50); // "5EXP"
1042
+ out.push(1); // bundle version
1043
+ out.push(Math.min(methods.length, 255));
1044
+ for (const method of methods.slice(0, 255)) {
1045
+ const bytes = Buffer.from(method, "utf8");
1046
+ out.push(Math.min(bytes.length, 255));
1047
+ for (const b of bytes.slice(0, 255))
1048
+ out.push(b);
1049
+ }
1050
+ out.push(Math.min(interfaces.length, 255));
1051
+ for (const iface of interfaces.slice(0, 255)) {
1052
+ const nameBytes = Buffer.from(iface.name, "utf8");
1053
+ out.push(Math.min(nameBytes.length, 255));
1054
+ for (const b of nameBytes.slice(0, 255))
1055
+ out.push(b);
1056
+ const pairs = Object.entries(iface.methodMap || {});
1057
+ out.push(Math.min(pairs.length, 255));
1058
+ for (const [method, callee] of pairs.slice(0, 255)) {
1059
+ const methodBytes = Buffer.from(method, "utf8");
1060
+ const calleeBytes = Buffer.from(callee, "utf8");
1061
+ out.push(Math.min(methodBytes.length, 255));
1062
+ for (const b of methodBytes.slice(0, 255))
1063
+ out.push(b);
1064
+ out.push(Math.min(calleeBytes.length, 255));
1065
+ for (const b of calleeBytes.slice(0, 255))
1066
+ out.push(b);
1067
+ }
1068
+ }
1069
+ return Uint8Array.from(out);
1070
+ }
1071
+ function chunkBytecode(bytecode, chunkSize) {
1072
+ const chunks = [];
1073
+ for (let i = 0; i < bytecode.length; i += chunkSize) {
1074
+ const chunk = bytecode.slice(i, Math.min(i + chunkSize, bytecode.length));
1075
+ chunks.push(chunk);
1076
+ }
1077
+ return chunks;
1078
+ }
1079
+ function groupChunksForOptimalTransactions(chunks, maxGroupSize) {
1080
+ const groups = [];
1081
+ let currentGroup = [];
1082
+ let currentGroupSize = 0;
1083
+ const getGroupOverhead = (numChunks) => 1 + numChunks * 2;
1084
+ for (const chunk of chunks) {
1085
+ const groupOverhead = getGroupOverhead(currentGroup.length + 1);
1086
+ const newGroupSize = currentGroupSize + chunk.length + 2;
1087
+ if (currentGroup.length === 0) {
1088
+ currentGroup.push(chunk);
1089
+ currentGroupSize = newGroupSize;
1090
+ }
1091
+ else if (newGroupSize + groupOverhead <= maxGroupSize &&
1092
+ currentGroup.length < 8) {
1093
+ currentGroup.push(chunk);
1094
+ currentGroupSize = newGroupSize;
1095
+ }
1096
+ else {
1097
+ groups.push(currentGroup);
1098
+ currentGroup = [chunk];
1099
+ currentGroupSize = chunk.length + 2;
1100
+ }
1101
+ }
1102
+ if (currentGroup.length > 0) {
1103
+ groups.push(currentGroup);
1104
+ }
1105
+ return groups;
1106
+ }
1107
+ function createMultiChunkInstructionData(chunks) {
1108
+ if (chunks.length < 2 || chunks.length > 10) {
1109
+ throw new Error(`Invalid chunk count for multi-chunk instruction: ${chunks.length}`);
1110
+ }
1111
+ const buffers = [
1112
+ Buffer.from([5]), // AppendBytecode discriminator
1113
+ ];
1114
+ for (const chunk of chunks) {
1115
+ buffers.push(Buffer.from(chunk));
1116
+ }
1117
+ return Buffer.concat(buffers);
1118
+ }