@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.
- package/README.md +279 -0
- package/dist/FiveSDK.d.ts +336 -0
- package/dist/FiveSDK.js +395 -0
- package/dist/accounts/index.d.ts +254 -0
- package/dist/accounts/index.js +543 -0
- package/dist/assets/vm/dummy.file +0 -0
- package/dist/assets/vm/five_vm_wasm.d.ts +762 -0
- package/dist/assets/vm/five_vm_wasm.js +3754 -0
- package/dist/assets/vm/five_vm_wasm_bg.js +3307 -0
- package/dist/assets/vm/five_vm_wasm_bg.wasm +0 -0
- package/dist/assets/vm/five_vm_wasm_bg.wasm.d.ts +247 -0
- package/dist/assets/vm/package.json +11 -0
- package/dist/bin/gen-types.d.ts +2 -0
- package/dist/bin/gen-types.js +35 -0
- package/dist/compiler/BytecodeCompiler.d.ts +83 -0
- package/dist/compiler/BytecodeCompiler.js +379 -0
- package/dist/config/ConfigManager.d.ts +13 -0
- package/dist/config/ConfigManager.js +27 -0
- package/dist/config/ProgramIdResolver.d.ts +62 -0
- package/dist/config/ProgramIdResolver.js +104 -0
- package/dist/crypto/index.d.ts +211 -0
- package/dist/crypto/index.js +451 -0
- package/dist/encoding/ParameterEncoder.d.ts +31 -0
- package/dist/encoding/ParameterEncoder.js +278 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +28 -0
- package/dist/lib/bytecode-encoder.d.ts +62 -0
- package/dist/lib/bytecode-encoder.js +281 -0
- package/dist/logging/index.d.ts +9 -0
- package/dist/logging/index.js +10 -0
- package/dist/metadata/index.d.ts +213 -0
- package/dist/metadata/index.js +296 -0
- package/dist/modules/accounts.d.ts +60 -0
- package/dist/modules/accounts.js +275 -0
- package/dist/modules/deploy.d.ts +90 -0
- package/dist/modules/deploy.js +1118 -0
- package/dist/modules/execute.d.ts +90 -0
- package/dist/modules/execute.js +649 -0
- package/dist/modules/fees.d.ts +14 -0
- package/dist/modules/fees.js +112 -0
- package/dist/modules/namespaces.d.ts +39 -0
- package/dist/modules/namespaces.js +190 -0
- package/dist/modules/state-diff.d.ts +35 -0
- package/dist/modules/state-diff.js +342 -0
- package/dist/modules/vm-state.d.ts +7 -0
- package/dist/modules/vm-state.js +44 -0
- package/dist/program/AccountResolver.d.ts +67 -0
- package/dist/program/AccountResolver.js +134 -0
- package/dist/program/BorshSchemaGenerator.d.ts +8 -0
- package/dist/program/BorshSchemaGenerator.js +57 -0
- package/dist/program/FiveProgram.d.ts +144 -0
- package/dist/program/FiveProgram.js +282 -0
- package/dist/program/FunctionBuilder.d.ts +114 -0
- package/dist/program/FunctionBuilder.js +347 -0
- package/dist/program/ProgramAccount.d.ts +38 -0
- package/dist/program/ProgramAccount.js +170 -0
- package/dist/program/TypeGenerator.d.ts +90 -0
- package/dist/program/TypeGenerator.js +195 -0
- package/dist/program/index.d.ts +24 -0
- package/dist/program/index.js +21 -0
- package/dist/project/config.d.ts +5 -0
- package/dist/project/config.js +33 -0
- package/dist/project/toml.d.ts +6 -0
- package/dist/project/toml.js +43 -0
- package/dist/project/workspace.d.ts +160 -0
- package/dist/project/workspace.js +73 -0
- package/dist/testing/AccountMetaGenerator.d.ts +121 -0
- package/dist/testing/AccountMetaGenerator.js +261 -0
- package/dist/testing/AccountTestFixture.d.ts +211 -0
- package/dist/testing/AccountTestFixture.js +530 -0
- package/dist/testing/OnChainAccountManager.d.ts +81 -0
- package/dist/testing/OnChainAccountManager.js +260 -0
- package/dist/testing/StateSerializer.d.ts +65 -0
- package/dist/testing/StateSerializer.js +330 -0
- package/dist/testing/TestDiscovery.d.ts +79 -0
- package/dist/testing/TestDiscovery.js +274 -0
- package/dist/testing/TestRunner.d.ts +117 -0
- package/dist/testing/TestRunner.js +346 -0
- package/dist/testing/index.d.ts +14 -0
- package/dist/testing/index.js +13 -0
- package/dist/types.d.ts +356 -0
- package/dist/types.js +32 -0
- package/dist/utils/abi.d.ts +31 -0
- package/dist/utils/abi.js +92 -0
- package/dist/utils/transaction.d.ts +5 -0
- package/dist/utils/transaction.js +48 -0
- package/dist/validation/InputValidator.d.ts +142 -0
- package/dist/validation/InputValidator.js +332 -0
- package/dist/validation/index.d.ts +4 -0
- package/dist/validation/index.js +4 -0
- package/dist/wasm/compiler/AbiLogic.d.ts +4 -0
- package/dist/wasm/compiler/AbiLogic.js +37 -0
- package/dist/wasm/compiler/AnalysisLogic.d.ts +6 -0
- package/dist/wasm/compiler/AnalysisLogic.js +61 -0
- package/dist/wasm/compiler/CompilationLogic.d.ts +10 -0
- package/dist/wasm/compiler/CompilationLogic.js +431 -0
- package/dist/wasm/compiler/FiveCompiler.d.ts +48 -0
- package/dist/wasm/compiler/FiveCompiler.js +183 -0
- package/dist/wasm/compiler/InfoLogic.d.ts +6 -0
- package/dist/wasm/compiler/InfoLogic.js +24 -0
- package/dist/wasm/compiler/OptimizationLogic.d.ts +2 -0
- package/dist/wasm/compiler/OptimizationLogic.js +13 -0
- package/dist/wasm/compiler/ValidationLogic.d.ts +7 -0
- package/dist/wasm/compiler/ValidationLogic.js +26 -0
- package/dist/wasm/compiler/index.d.ts +2 -0
- package/dist/wasm/compiler/index.js +2 -0
- package/dist/wasm/compiler/types.d.ts +8 -0
- package/dist/wasm/compiler/types.js +1 -0
- package/dist/wasm/compiler/utils.d.ts +8 -0
- package/dist/wasm/compiler/utils.js +75 -0
- package/dist/wasm/index.d.ts +9 -0
- package/dist/wasm/index.js +12 -0
- package/dist/wasm/instance.d.ts +1 -0
- package/dist/wasm/instance.js +26 -0
- package/dist/wasm/loader.d.ts +7 -0
- package/dist/wasm/loader.js +112 -0
- package/dist/wasm/vm.d.ts +33 -0
- package/dist/wasm/vm.js +250 -0
- 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
|
+
}
|