@5ive-tech/cli 1.0.4

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 (139) hide show
  1. package/README.md +226 -0
  2. package/dist/assets/vm/five_vm_wasm.d.ts +762 -0
  3. package/dist/assets/vm/five_vm_wasm.js +3754 -0
  4. package/dist/assets/vm/five_vm_wasm_bg.wasm +0 -0
  5. package/dist/assets/vm/five_vm_wasm_bg.wasm.d.ts +247 -0
  6. package/dist/assets/vm/package.json +11 -0
  7. package/dist/cli.d.ts +47 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +343 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/commands/analyze.d.ts +3 -0
  12. package/dist/commands/analyze.d.ts.map +1 -0
  13. package/dist/commands/analyze.js +435 -0
  14. package/dist/commands/analyze.js.map +1 -0
  15. package/dist/commands/build.d.ts +3 -0
  16. package/dist/commands/build.d.ts.map +1 -0
  17. package/dist/commands/build.js +66 -0
  18. package/dist/commands/build.js.map +1 -0
  19. package/dist/commands/compile.d.ts +3 -0
  20. package/dist/commands/compile.d.ts.map +1 -0
  21. package/dist/commands/compile.js +872 -0
  22. package/dist/commands/compile.js.map +1 -0
  23. package/dist/commands/config.d.ts +3 -0
  24. package/dist/commands/config.d.ts.map +1 -0
  25. package/dist/commands/config.js +431 -0
  26. package/dist/commands/config.js.map +1 -0
  27. package/dist/commands/deploy-and-execute.d.ts +3 -0
  28. package/dist/commands/deploy-and-execute.d.ts.map +1 -0
  29. package/dist/commands/deploy-and-execute.js +317 -0
  30. package/dist/commands/deploy-and-execute.js.map +1 -0
  31. package/dist/commands/deploy.d.ts +21 -0
  32. package/dist/commands/deploy.d.ts.map +1 -0
  33. package/dist/commands/deploy.js +806 -0
  34. package/dist/commands/deploy.js.map +1 -0
  35. package/dist/commands/donate.d.ts +4 -0
  36. package/dist/commands/donate.d.ts.map +1 -0
  37. package/dist/commands/donate.js +104 -0
  38. package/dist/commands/donate.js.map +1 -0
  39. package/dist/commands/execute.d.ts +6 -0
  40. package/dist/commands/execute.d.ts.map +1 -0
  41. package/dist/commands/execute.js +749 -0
  42. package/dist/commands/execute.js.map +1 -0
  43. package/dist/commands/fmt.d.ts +3 -0
  44. package/dist/commands/fmt.d.ts.map +1 -0
  45. package/dist/commands/fmt.js +327 -0
  46. package/dist/commands/fmt.js.map +1 -0
  47. package/dist/commands/help.d.ts +6 -0
  48. package/dist/commands/help.d.ts.map +1 -0
  49. package/dist/commands/help.js +224 -0
  50. package/dist/commands/help.js.map +1 -0
  51. package/dist/commands/index.d.ts +45 -0
  52. package/dist/commands/index.d.ts.map +1 -0
  53. package/dist/commands/index.js +119 -0
  54. package/dist/commands/index.js.map +1 -0
  55. package/dist/commands/init.d.ts +3 -0
  56. package/dist/commands/init.d.ts.map +1 -0
  57. package/dist/commands/init.js +887 -0
  58. package/dist/commands/init.js.map +1 -0
  59. package/dist/commands/local.d.ts +3 -0
  60. package/dist/commands/local.d.ts.map +1 -0
  61. package/dist/commands/local.js +703 -0
  62. package/dist/commands/local.js.map +1 -0
  63. package/dist/commands/namespace.d.ts +3 -0
  64. package/dist/commands/namespace.d.ts.map +1 -0
  65. package/dist/commands/namespace.js +328 -0
  66. package/dist/commands/namespace.js.map +1 -0
  67. package/dist/commands/template.d.ts +4 -0
  68. package/dist/commands/template.d.ts.map +1 -0
  69. package/dist/commands/template.js +486 -0
  70. package/dist/commands/template.js.map +1 -0
  71. package/dist/commands/test.d.ts +6 -0
  72. package/dist/commands/test.d.ts.map +1 -0
  73. package/dist/commands/test.js +890 -0
  74. package/dist/commands/test.js.map +1 -0
  75. package/dist/commands/version.d.ts +6 -0
  76. package/dist/commands/version.d.ts.map +1 -0
  77. package/dist/commands/version.js +339 -0
  78. package/dist/commands/version.js.map +1 -0
  79. package/dist/config/ConfigManager.d.ts +69 -0
  80. package/dist/config/ConfigManager.d.ts.map +1 -0
  81. package/dist/config/ConfigManager.js +261 -0
  82. package/dist/config/ConfigManager.js.map +1 -0
  83. package/dist/config/index.d.ts +10 -0
  84. package/dist/config/index.d.ts.map +1 -0
  85. package/dist/config/index.js +21 -0
  86. package/dist/config/index.js.map +1 -0
  87. package/dist/config/types.d.ts +35 -0
  88. package/dist/config/types.d.ts.map +1 -0
  89. package/dist/config/types.js +105 -0
  90. package/dist/config/types.js.map +1 -0
  91. package/dist/index.d.ts +3 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +29 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/project/ProjectLoader.d.ts +12 -0
  96. package/dist/project/ProjectLoader.d.ts.map +1 -0
  97. package/dist/project/ProjectLoader.js +115 -0
  98. package/dist/project/ProjectLoader.js.map +1 -0
  99. package/dist/types.d.ts +334 -0
  100. package/dist/types.d.ts.map +1 -0
  101. package/dist/types.js +2 -0
  102. package/dist/types.js.map +1 -0
  103. package/dist/utils/AccountFixtureGenerator.d.ts +48 -0
  104. package/dist/utils/AccountFixtureGenerator.d.ts.map +1 -0
  105. package/dist/utils/AccountFixtureGenerator.js +265 -0
  106. package/dist/utils/AccountFixtureGenerator.js.map +1 -0
  107. package/dist/utils/FiveFileManager.d.ts +96 -0
  108. package/dist/utils/FiveFileManager.d.ts.map +1 -0
  109. package/dist/utils/FiveFileManager.js +329 -0
  110. package/dist/utils/FiveFileManager.js.map +1 -0
  111. package/dist/utils/ascii-art.d.ts +72 -0
  112. package/dist/utils/ascii-art.d.ts.map +1 -0
  113. package/dist/utils/ascii-art.js +314 -0
  114. package/dist/utils/ascii-art.js.map +1 -0
  115. package/dist/utils/cli-ui.d.ts +39 -0
  116. package/dist/utils/cli-ui.d.ts.map +1 -0
  117. package/dist/utils/cli-ui.js +75 -0
  118. package/dist/utils/cli-ui.js.map +1 -0
  119. package/dist/utils/fileUtils.d.ts +25 -0
  120. package/dist/utils/fileUtils.d.ts.map +1 -0
  121. package/dist/utils/fileUtils.js +50 -0
  122. package/dist/utils/fileUtils.js.map +1 -0
  123. package/dist/utils/logger.d.ts +53 -0
  124. package/dist/utils/logger.d.ts.map +1 -0
  125. package/dist/utils/logger.js +287 -0
  126. package/dist/utils/logger.js.map +1 -0
  127. package/dist/wasm/compiler.d.ts +101 -0
  128. package/dist/wasm/compiler.d.ts.map +1 -0
  129. package/dist/wasm/compiler.js +906 -0
  130. package/dist/wasm/compiler.js.map +1 -0
  131. package/dist/wasm/loader.d.ts +2 -0
  132. package/dist/wasm/loader.d.ts.map +1 -0
  133. package/dist/wasm/loader.js +90 -0
  134. package/dist/wasm/loader.js.map +1 -0
  135. package/dist/wasm/vm.d.ts +32 -0
  136. package/dist/wasm/vm.d.ts.map +1 -0
  137. package/dist/wasm/vm.js +440 -0
  138. package/dist/wasm/vm.js.map +1 -0
  139. package/package.json +100 -0
@@ -0,0 +1,749 @@
1
+ // Execute command.
2
+ import { readFile } from 'fs/promises';
3
+ import { readFileSync, existsSync } from 'fs';
4
+ import { extname, isAbsolute, join, resolve } from 'path';
5
+ import ora from 'ora';
6
+ import { FiveSDK, ProgramIdResolver } from '@5ive-tech/sdk';
7
+ import { ConfigManager } from '../config/ConfigManager.js';
8
+ import { FiveFileManager } from '../utils/FiveFileManager.js';
9
+ import { loadBuildManifest, loadProjectConfig } from '../project/ProjectLoader.js';
10
+ import { section, success as uiSuccess, error as uiError, hint, keyValue } from '../utils/cli-ui.js';
11
+ /**
12
+ * Five execute command implementation
13
+ */
14
+ export const executeCommand = {
15
+ name: 'execute',
16
+ description: 'Execute Five VM bytecode',
17
+ aliases: ['exec', 'run'],
18
+ options: [
19
+ {
20
+ flags: '-i, --input <file>',
21
+ description: 'Input data file (JSON format)',
22
+ required: false
23
+ },
24
+ {
25
+ flags: '-a, --accounts <file>',
26
+ description: 'Accounts configuration file (JSON format) [local execution only] ',
27
+ required: false
28
+ },
29
+ {
30
+ flags: '--accounts-json <json>',
31
+ description: 'Additional accounts for on-chain execution as JSON array or object of pubkeys',
32
+ required: false
33
+ },
34
+ {
35
+ flags: '-f, --function <index>',
36
+ description: 'Execute specific function by index',
37
+ required: false
38
+ },
39
+ {
40
+ flags: '-p, --params <file>',
41
+ description: 'Function parameters file (JSON format)',
42
+ required: false
43
+ },
44
+ {
45
+ flags: '--max-cu <units>',
46
+ description: 'Maximum compute units (default: 1000000)',
47
+ defaultValue: 1000000
48
+ },
49
+ {
50
+ flags: '--validate',
51
+ description: 'Validate bytecode before execution',
52
+ defaultValue: false
53
+ },
54
+ {
55
+ flags: '--partial',
56
+ description: 'Enable partial execution (stops at system calls)',
57
+ defaultValue: true
58
+ },
59
+ {
60
+ flags: '--format <format>',
61
+ description: 'Output format',
62
+ choices: ['text', 'json', 'table'],
63
+ defaultValue: 'text'
64
+ },
65
+ {
66
+ flags: '--trace',
67
+ description: 'Show execution trace',
68
+ defaultValue: false
69
+ },
70
+ {
71
+ flags: '--state',
72
+ description: 'Show VM state after execution',
73
+ defaultValue: false
74
+ },
75
+ {
76
+ flags: '-t, --target <target>',
77
+ description: 'Override target network (devnet, testnet, mainnet, local)',
78
+ required: false
79
+ },
80
+ {
81
+ flags: '-n, --network <url>',
82
+ description: 'Override network RPC URL',
83
+ required: false
84
+ },
85
+ {
86
+ flags: '-k, --keypair <file>',
87
+ description: 'Override keypair file path',
88
+ required: false
89
+ },
90
+ {
91
+ flags: '--local',
92
+ description: 'Force local execution (overrides config)',
93
+ defaultValue: false
94
+ },
95
+ {
96
+ flags: '--script-account <account>',
97
+ description: 'Execute deployed script by account ID (on-chain execution)',
98
+ required: false
99
+ },
100
+ {
101
+ flags: '--vm-state-account <account>',
102
+ description: 'VM state account address (optional, required for on-chain execution if known)',
103
+ required: false
104
+ },
105
+ {
106
+ flags: '--program-id <id>',
107
+ description: 'Override Five VM program ID (for custom deployments)',
108
+ required: false
109
+ },
110
+ {
111
+ flags: '--project <path>',
112
+ description: 'Project directory or five.toml path',
113
+ required: false
114
+ }
115
+ ],
116
+ arguments: [
117
+ {
118
+ name: 'bytecode',
119
+ description: 'Five VM artifact file (.five/.bin) or script account ID',
120
+ required: false
121
+ }
122
+ ],
123
+ examples: [
124
+ {
125
+ command: 'five execute program.five',
126
+ description: 'Execute using configured target (default)'
127
+ },
128
+ {
129
+ command: 'five execute program.five --local',
130
+ description: 'Force local execution (overrides config)'
131
+ },
132
+ {
133
+ command: 'five execute program.five --target devnet',
134
+ description: 'Execute on devnet (overrides config)'
135
+ },
136
+ {
137
+ command: 'five execute program.five -f 0 -p params.json',
138
+ description: 'Execute function 0 with parameters'
139
+ },
140
+ {
141
+ command: 'five execute program.five --validate --trace --format json',
142
+ description: 'Validate and execute with JSON trace output'
143
+ },
144
+ {
145
+ command: 'five execute src/main.v -f 0 --local --params "[10, 5]"',
146
+ description: 'Compile and execute Five source locally with parameters'
147
+ },
148
+ {
149
+ command: 'five execute --script-account 459SanqV8nQDDYW3gWq5JZZAPCMYs78Z5ZnrtH4eFffw -f 0',
150
+ description: 'Execute deployed script by account ID on-chain'
151
+ },
152
+ {
153
+ command: 'five execute --script-account 459SanqV8nQDDYW3gWq5JZZAPCMYs78Z5ZnrtH4eFffw -f 0 --program-id 9MHGM73eszNUtmJS6ypDCESguxWhCBnkUPpTMyLGqURH',
154
+ description: 'Execute with custom Five VM program ID (for custom deployments)'
155
+ }
156
+ ],
157
+ handler: async (args, options, context) => {
158
+ const { logger } = context;
159
+ try {
160
+ const projectContext = await loadProjectConfig(options.project, process.cwd());
161
+ const manifest = projectContext ? await loadBuildManifest(projectContext.rootDir) : null;
162
+ // Apply project defaults to options if not provided
163
+ if (!options.target && projectContext?.config.cluster) {
164
+ options.target = projectContext.config.cluster;
165
+ }
166
+ if (!options.network && projectContext?.config.rpcUrl) {
167
+ options.network = projectContext.config.rpcUrl;
168
+ }
169
+ if (!options.keypair && projectContext?.config.keypairPath) {
170
+ options.keypair = projectContext.config.keypairPath;
171
+ }
172
+ let inputFile = args[0] || manifest?.artifact_path;
173
+ if (inputFile && projectContext && !isAbsolute(inputFile)) {
174
+ inputFile = join(projectContext.rootDir, inputFile);
175
+ }
176
+ const scriptAccount = options.scriptAccount;
177
+ // Debug: Log received options
178
+ if (context.options.verbose) {
179
+ console.log('[execute] Received options:', Object.keys(options));
180
+ console.log('[execute] scriptAccount value:', scriptAccount);
181
+ }
182
+ // Validate input - either bytecode file OR script account required
183
+ if (!inputFile && !scriptAccount) {
184
+ throw new Error('No bytecode or script account provided. Pass a .five/.bin file, use --project to load the last build artifact, or provide --script-account for on-chain execution.');
185
+ }
186
+ // Apply config with CLI overrides
187
+ const configManager = ConfigManager.getInstance();
188
+ const overrides = {
189
+ target: options.target,
190
+ network: options.network,
191
+ keypair: options.keypair
192
+ };
193
+ const config = await configManager.applyOverrides(overrides);
194
+ // Resolve program ID AFTER target override is applied, using the correct target
195
+ // Precedence: CLI flag → project config → config file (per-target) → env var
196
+ if (!options.programId) {
197
+ const configuredProgramId = await configManager.getProgramId(config.target);
198
+ options.programId = projectContext?.config.programId || configuredProgramId || process.env.FIVE_PROGRAM_ID;
199
+ }
200
+ // Show target context prefix
201
+ const targetPrefix = ConfigManager.getTargetPrefix(config.target);
202
+ // Force local execution if --local flag is used, but not if script account is specified
203
+ const forceLocal = options.local || false;
204
+ const executeLocally = (config.target === 'wasm' || forceLocal) && !scriptAccount;
205
+ // Log execution mode only in verbose
206
+ if (context.options.verbose) {
207
+ if (scriptAccount) {
208
+ logger.info(`${targetPrefix} Executing deployed script account on-chain`);
209
+ }
210
+ else if (executeLocally) {
211
+ logger.info(`${ConfigManager.getTargetPrefix('wasm')} Executing Five VM bytecode locally`);
212
+ }
213
+ else {
214
+ logger.info(`${targetPrefix} Executing Five VM bytecode`);
215
+ }
216
+ }
217
+ // Show config details only if explicitly enabled and verbose
218
+ if (config.showConfig && !executeLocally && context.options.verbose) {
219
+ logger.info(`Target: ${config.target}`);
220
+ logger.info(`Network: ${config.networks[config.target].rpcUrl}`);
221
+ logger.info(`Keypair: ${config.keypairPath}`);
222
+ }
223
+ if (scriptAccount) {
224
+ await executeScriptAccount(scriptAccount, options, context, config);
225
+ }
226
+ else if (executeLocally) {
227
+ await executeLocallyWithSDK(inputFile, options, context, config);
228
+ }
229
+ else {
230
+ await executeOnChain(inputFile, options, context, config);
231
+ }
232
+ }
233
+ catch (error) {
234
+ logger.error('Execution failed:', error);
235
+ throw error;
236
+ }
237
+ }
238
+ };
239
+ /**
240
+ * Execute locally using Five SDK
241
+ */
242
+ async function executeLocallyWithSDK(inputFile, options, context, config) {
243
+ const { logger } = context;
244
+ // Initialize for local execution
245
+ if (context.options.verbose) {
246
+ const spinner = ora('Preparing local execution...').start();
247
+ spinner.succeed('Five SDK ready for local execution');
248
+ }
249
+ try {
250
+ let result;
251
+ if (extname(inputFile) === '.v') {
252
+ // Compile and execute Five source file
253
+ logger.info(`Compiling and executing Five source: ${inputFile}`);
254
+ const sourceCode = await readFile(inputFile, 'utf8');
255
+ // For .v files, we don't have ABI yet, so we'll just use the provided function or default to 0
256
+ // The real fix here would be to compile first, extract ABI, then auto-detect the public function
257
+ const functionIndex = parseFunctionIndex(options.function) || 0;
258
+ const parameters = parseParameters(options.params);
259
+ // Parse accounts if provided
260
+ const accounts = options.accounts ? parseAccounts(options.accounts) : undefined;
261
+ result = await FiveSDK.execute(sourceCode, functionIndex, parameters, {
262
+ debug: options.trace || context.options.debug,
263
+ trace: options.trace,
264
+ optimize: true,
265
+ computeUnitLimit: options.maxCu,
266
+ accounts
267
+ });
268
+ // Display compilation info with proper type guard
269
+ if ('compilation' in result && result.compilation) {
270
+ console.log(`Compilation: ${result.compilation.success ? 'OK' : 'FAIL'}`);
271
+ if ('bytecodeSize' in result && result.bytecodeSize) {
272
+ console.log(`Bytecode size: ${result.bytecodeSize} bytes`);
273
+ }
274
+ }
275
+ }
276
+ else {
277
+ // Execute existing bytecode file
278
+ logger.info(`Executing bytecode: ${inputFile}`);
279
+ // Load file using centralized manager
280
+ const fileManager = FiveFileManager.getInstance();
281
+ const loadedFile = await fileManager.loadFile(inputFile, {
282
+ validateFormat: true
283
+ });
284
+ const bytecode = loadedFile.bytecode;
285
+ const abi = loadedFile.abi;
286
+ if (options.trace || context.options.debug) {
287
+ console.log(`[CLI] Loaded ${loadedFile.format.toUpperCase()} file: bytecode=${bytecode.length} bytes`);
288
+ if (abi && abi.functions) {
289
+ console.log(`[CLI] Available functions: ${Object.keys(abi.functions).length}`);
290
+ }
291
+ }
292
+ // FUNCTION RESOLUTION FIX: Auto-detect public function when no -f flag provided
293
+ let functionIndex = parseFunctionIndex(options.function);
294
+ if (functionIndex === undefined && abi && abi.functions) {
295
+ // SYSTEMATIC APPROACH: Public functions always have the lowest indices (0, 1, 2...)
296
+ // So we always default to function index 0, which is guaranteed to be public if any functions exist
297
+ functionIndex = 0;
298
+ if (context.options.verbose) {
299
+ let functionName = 'function_0';
300
+ if (Array.isArray(abi.functions)) {
301
+ // FIVEABI format: find function with index 0
302
+ const func0 = abi.functions.find((f) => f.index === 0);
303
+ if (func0 && func0.name)
304
+ functionName = func0.name;
305
+ }
306
+ else {
307
+ // SimpleABI format: find function with index 0
308
+ const func0Entry = Object.entries(abi.functions).find(([_, f]) => f.index === 0);
309
+ if (func0Entry)
310
+ functionName = func0Entry[0];
311
+ }
312
+ logger.info(`Auto-detected public function: ${functionName} (index 0 - first public function)`);
313
+ }
314
+ }
315
+ else if (functionIndex === undefined) {
316
+ functionIndex = 0; // No ABI available, use legacy default
317
+ }
318
+ // Ensure functionIndex is never undefined
319
+ if (functionIndex === undefined) {
320
+ functionIndex = 0;
321
+ }
322
+ const parameters = parseParameters(options.params);
323
+ // Parse accounts if provided
324
+ const accounts = options.accounts ? parseAccounts(options.accounts) : undefined;
325
+ result = await FiveSDK.executeLocally(bytecode, functionIndex, parameters, {
326
+ debug: options.trace || context.options.debug,
327
+ trace: options.trace,
328
+ computeUnitLimit: options.maxCu,
329
+ abi: abi, // Pass ABI for function name resolution
330
+ accounts
331
+ });
332
+ }
333
+ // Display execution results
334
+ displayLocalExecutionResult(result, options, logger);
335
+ }
336
+ catch (error) {
337
+ throw new Error(`Local execution failed: ${error}`);
338
+ }
339
+ }
340
+ /**
341
+ * Execute deployed script account on-chain using Five SDK
342
+ */
343
+ async function executeScriptAccount(scriptAccount, options, context, config) {
344
+ const { logger } = context;
345
+ const { Connection, Keypair } = await import('@solana/web3.js');
346
+ const targetPrefix = ConfigManager.getTargetPrefix(config.target);
347
+ logger.info(`${targetPrefix} Executing script account: ${scriptAccount}`);
348
+ try {
349
+ // Show configuration
350
+ console.log('\n' + section('Execution Configuration'));
351
+ console.log(` Script Account: ${scriptAccount}`);
352
+ console.log(` Target: ${config.target}`);
353
+ console.log(` Network: ${config.networks[config.target].rpcUrl}`);
354
+ console.log(` Keypair: ${config.keypairPath}`);
355
+ // Set up connection and keypair
356
+ const rpcUrl = config.networks[config.target].rpcUrl;
357
+ const connection = new Connection(rpcUrl, 'confirmed');
358
+ // Load keypair
359
+ const keypairPath = config.keypairPath;
360
+ const keypairData = JSON.parse(await readFile(keypairPath, 'utf8'));
361
+ const deployerKeypair = Keypair.fromSecretKey(new Uint8Array(keypairData));
362
+ console.log(` Executor: ${deployerKeypair.publicKey.toBase58()}`);
363
+ const spinner = ora('Executing script account on-chain...').start();
364
+ try {
365
+ // Get function index, parameters, and additional accounts
366
+ const functionIndex = options.function ? parseInt(options.function) : 0;
367
+ const parameters = parseParameters(options.params);
368
+ // Parse additional accounts for on-chain execution
369
+ let additionalAccounts = [];
370
+ if (options.accountsJson) {
371
+ try {
372
+ const parsed = JSON.parse(options.accountsJson);
373
+ if (Array.isArray(parsed)) {
374
+ additionalAccounts = parsed;
375
+ }
376
+ else if (typeof parsed === 'object' && parsed !== null) {
377
+ // Preserve insertion order from the JSON string
378
+ const orderedKeys = Object.keys(parsed);
379
+ additionalAccounts = orderedKeys.map(k => parsed[k]);
380
+ }
381
+ }
382
+ catch (e) {
383
+ console.warn('Warning: Failed to parse --accounts-json, ignoring.');
384
+ }
385
+ }
386
+ // Validate program ID for script account execution
387
+ let resolvedProgramId;
388
+ if (config.target !== 'wasm') {
389
+ try {
390
+ resolvedProgramId = ProgramIdResolver.resolve(options.programId);
391
+ }
392
+ catch (error) {
393
+ throw new Error(`Program ID required for script account execution on ${config.target}. ` +
394
+ `Provide via: --program-id <pubkey>, five.toml programId, ` +
395
+ `or: five config set --program-id <pubkey>`);
396
+ }
397
+ options.programId = resolvedProgramId;
398
+ }
399
+ // Fetch current fees from VM state
400
+ let fees;
401
+ try {
402
+ fees = await FiveSDK.getFees(connection, options.programId);
403
+ if (fees.executeFeeBps > 0) {
404
+ if (context.options.verbose || options.debug) {
405
+ console.log('\n' + section('VM Fees'));
406
+ console.log(keyValue('Execution Fee', `${(fees.executeFeeBps / 100).toFixed(2)}%`));
407
+ if (fees.adminAccount) {
408
+ console.log(keyValue('Admin Account', fees.adminAccount));
409
+ }
410
+ }
411
+ // Attach admin account to options
412
+ options.adminAccount = fees.adminAccount;
413
+ }
414
+ }
415
+ catch (e) {
416
+ if (context.options.debug) {
417
+ logger.debug(`Could not fetch VM fees: ${e instanceof Error ? e.message : String(e)}`);
418
+ }
419
+ }
420
+ // Execute script account on-chain with additional accounts
421
+ const executeOptions = {
422
+ debug: options.trace || context.options.debug,
423
+ network: config.target,
424
+ computeUnitLimit: options.maxCu || 1400000,
425
+ maxRetries: 3,
426
+ vmStateAccount: options.vmStateAccount,
427
+ adminAccount: options.adminAccount // Pass admin account
428
+ };
429
+ // Add program ID override if provided
430
+ if (options.programId) {
431
+ executeOptions.fiveVMProgramId = options.programId;
432
+ }
433
+ const result = await FiveSDK.executeOnSolana(scriptAccount, connection, deployerKeypair, functionIndex, parameters, additionalAccounts, executeOptions);
434
+ spinner.succeed('Script account execution completed');
435
+ // Display results
436
+ displayOnChainExecutionResult(result, options, logger);
437
+ }
438
+ catch (error) {
439
+ spinner.fail('Script account execution failed');
440
+ throw error;
441
+ }
442
+ }
443
+ catch (error) {
444
+ throw new Error(`Script account execution failed: ${error}`);
445
+ }
446
+ }
447
+ /**
448
+ * Execute on-chain using Five SDK
449
+ */
450
+ async function executeOnChain(inputFile, options, context, config) {
451
+ const { logger } = context;
452
+ const { Connection, Keypair } = await import('@solana/web3.js');
453
+ const targetPrefix = ConfigManager.getTargetPrefix(config.target);
454
+ logger.info(`${targetPrefix} On-chain execution using Five SDK`);
455
+ try {
456
+ // Show configuration
457
+ console.log('\n' + section('Execution Configuration'));
458
+ console.log(` Target: ${config.target}`);
459
+ console.log(` Network: ${config.networks[config.target].rpcUrl}`);
460
+ console.log(` Keypair: ${config.keypairPath}`);
461
+ // Check if input is a script account (base58 string) or bytecode file
462
+ let scriptAccount;
463
+ if (inputFile.length > 30 && inputFile.length < 50 && !inputFile.includes('/') && !inputFile.includes('.')) {
464
+ // Looks like a base58 script account address
465
+ scriptAccount = inputFile;
466
+ console.log(`Using script account: ${scriptAccount}`);
467
+ }
468
+ else {
469
+ // It's a file - need to deploy first or prompt user
470
+ console.log(uiError('On-chain execution requires a deployed script account.'));
471
+ console.log(hint(`deploy first: five deploy ${inputFile}`));
472
+ console.log(hint(`then execute: five execute <SCRIPT_ACCOUNT> --target ${config.target}`));
473
+ console.log(hint(`or run locally: five execute ${inputFile} --local`));
474
+ return;
475
+ }
476
+ // Setup connection
477
+ const rpcUrl = config.networks[config.target].rpcUrl;
478
+ const connection = new Connection(rpcUrl, 'confirmed');
479
+ // Load signer keypair
480
+ const signerKeypair = await loadKeypair(config.keypairPath, logger);
481
+ // Parse execution options
482
+ const functionName = parseFunctionIndex(options.function) || 0;
483
+ const parameters = parseParameters(options.params);
484
+ const accounts = []; // No additional accounts for simple execution
485
+ console.log(`\nExecuting function ${functionName} with ${parameters.length} parameters...`);
486
+ // Fetch current fees from VM state
487
+ let fees;
488
+ try {
489
+ fees = await FiveSDK.getFees(connection, options.programId);
490
+ if (fees.executeFeeBps > 0) {
491
+ if (context.options.verbose || options.debug) {
492
+ console.log('\n' + section('VM Fees'));
493
+ console.log(keyValue('Execution Fee', `${(fees.executeFeeBps / 100).toFixed(2)}%`));
494
+ if (fees.adminAccount) {
495
+ console.log(keyValue('Admin Account', fees.adminAccount));
496
+ }
497
+ }
498
+ // Attach admin account to options
499
+ options.adminAccount = fees.adminAccount;
500
+ }
501
+ }
502
+ catch (e) {
503
+ if (context.options.debug) {
504
+ logger.debug(`Could not fetch VM fees: ${e instanceof Error ? e.message : String(e)}`);
505
+ }
506
+ }
507
+ // Execute using Five SDK
508
+ const spinner = ora('Executing on-chain via Five SDK...').start();
509
+ const executeOptions = {
510
+ debug: options.debug || context.options.debug || false,
511
+ network: config.target,
512
+ computeUnitLimit: options.maxCu,
513
+ maxRetries: 3,
514
+ vmStateAccount: options.vmStateAccount,
515
+ adminAccount: options.adminAccount // Pass admin account
516
+ };
517
+ // Add program ID override if provided
518
+ if (options.programId) {
519
+ executeOptions.fiveVMProgramId = options.programId;
520
+ }
521
+ const result = await FiveSDK.executeOnSolana(scriptAccount, connection, signerKeypair, functionName, parameters, accounts, executeOptions);
522
+ if (result.success) {
523
+ spinner.succeed('On-chain execution completed successfully!');
524
+ displayOnChainExecutionResult(result, options, logger);
525
+ }
526
+ else {
527
+ spinner.fail('On-chain execution failed');
528
+ displayOnChainExecutionResult(result, options, logger);
529
+ process.exit(1);
530
+ }
531
+ }
532
+ catch (error) {
533
+ logger.error('On-chain execution failed:', error);
534
+ throw error;
535
+ }
536
+ }
537
+ /**
538
+ * Parse function index - handle both numeric strings and function names
539
+ */
540
+ function parseFunctionIndex(functionOption) {
541
+ if (!functionOption) {
542
+ return undefined;
543
+ }
544
+ // If it's already a number, return it
545
+ if (typeof functionOption === 'number') {
546
+ return functionOption;
547
+ }
548
+ // If it's a string that looks like a number, convert it
549
+ if (typeof functionOption === 'string') {
550
+ const numericValue = parseInt(functionOption, 10);
551
+ if (!isNaN(numericValue) && numericValue.toString() === functionOption) {
552
+ return numericValue;
553
+ }
554
+ // Otherwise, treat it as a function name
555
+ return functionOption;
556
+ }
557
+ return functionOption;
558
+ }
559
+ /**
560
+ * Parse parameters from JSON string or file
561
+ */
562
+ function parseParameters(paramsOption) {
563
+ if (!paramsOption) {
564
+ return [];
565
+ }
566
+ try {
567
+ // Try to parse as JSON directly
568
+ if (typeof paramsOption === 'string' && paramsOption.startsWith('[')) {
569
+ return JSON.parse(paramsOption);
570
+ }
571
+ // Handle parameter files
572
+ if (typeof paramsOption === 'string') {
573
+ const filePath = isAbsolute(paramsOption) ? paramsOption : resolve(process.cwd(), paramsOption);
574
+ if (existsSync(filePath)) {
575
+ const fileContent = readFileSync(filePath, 'utf-8');
576
+ return JSON.parse(fileContent);
577
+ }
578
+ }
579
+ // If it looks like a file path but didn't exist, and doesn't look like JSON
580
+ if (typeof paramsOption === 'string' && (paramsOption.endsWith('.json') || paramsOption.includes('/'))) {
581
+ throw new Error(`Parameter file not found or invalid: ${paramsOption}`);
582
+ }
583
+ return [];
584
+ }
585
+ catch (error) {
586
+ throw new Error(`Failed to parse parameters: ${error}`);
587
+ }
588
+ }
589
+ /**
590
+ * Parse accounts from comma-separated string
591
+ */
592
+ function parseAccounts(accountsOption) {
593
+ if (!accountsOption) {
594
+ return undefined;
595
+ }
596
+ try {
597
+ // If it's a string, split by comma and trim whitespace
598
+ if (typeof accountsOption === 'string') {
599
+ const accounts = accountsOption
600
+ .split(',')
601
+ .map(account => account.trim())
602
+ .filter(account => account.length > 0);
603
+ return accounts.length > 0 ? accounts : undefined;
604
+ }
605
+ // If it's already an array, return as-is
606
+ if (Array.isArray(accountsOption)) {
607
+ return accountsOption;
608
+ }
609
+ return undefined;
610
+ }
611
+ catch (error) {
612
+ throw new Error(`Failed to parse accounts: ${error}`);
613
+ }
614
+ }
615
+ /**
616
+ * Display on-chain execution results
617
+ */
618
+ function displayOnChainExecutionResult(result, options, logger) {
619
+ if (options.format === 'json') {
620
+ console.log(JSON.stringify(result, null, 2));
621
+ return;
622
+ }
623
+ console.log('\n' + section('On-Chain Execution'));
624
+ if (result.success) {
625
+ console.log(uiSuccess('Execution succeeded'));
626
+ if (result.transactionId) {
627
+ console.log(`Transaction: ${result.transactionId}`);
628
+ }
629
+ if (result.computeUnitsUsed !== undefined) {
630
+ console.log(`Compute units: ${result.computeUnitsUsed}`);
631
+ }
632
+ if (result.result !== undefined) {
633
+ console.log(`Result: ${result.result}`);
634
+ }
635
+ if (result.logs && result.logs.length > 0) {
636
+ console.log('\n' + section('Logs'));
637
+ result.logs.forEach((log) => {
638
+ // Filter out system logs and show only Five VM logs
639
+ if (log.includes('Five') || log.includes('success') || log.includes('error')) {
640
+ console.log(` ${log}`);
641
+ }
642
+ });
643
+ }
644
+ }
645
+ else {
646
+ console.log(uiError('Execution failed'));
647
+ if (result.error) {
648
+ console.log(`Error: ${result.error}`);
649
+ }
650
+ if (result.transactionId) {
651
+ console.log(`Transaction: ${result.transactionId}`);
652
+ }
653
+ if (result.logs && result.logs.length > 0) {
654
+ console.log('\n' + section('Error Logs'));
655
+ result.logs.forEach((log) => {
656
+ console.log(` ${log}`);
657
+ });
658
+ }
659
+ }
660
+ }
661
+ /**
662
+ * Load Solana keypair from file
663
+ */
664
+ async function loadKeypair(keypairPath, logger) {
665
+ const { readFile } = await import('fs/promises');
666
+ const { Keypair } = await import('@solana/web3.js');
667
+ // Expand tilde in path
668
+ const path = keypairPath.startsWith('~/')
669
+ ? keypairPath.replace('~', process.env.HOME || '')
670
+ : keypairPath;
671
+ try {
672
+ const keypairData = await readFile(path, 'utf8');
673
+ const secretKey = Uint8Array.from(JSON.parse(keypairData));
674
+ const keypair = Keypair.fromSecretKey(secretKey);
675
+ if (logger.debug) {
676
+ logger.debug(`Loaded keypair from: ${path}`);
677
+ logger.debug(`Public key: ${keypair.publicKey.toString()}`);
678
+ }
679
+ return keypair;
680
+ }
681
+ catch (error) {
682
+ throw new Error(`Failed to load keypair from ${path}: ${error}`);
683
+ }
684
+ }
685
+ /**
686
+ * Display local execution results
687
+ */
688
+ function displayLocalExecutionResult(result, options, logger) {
689
+ console.log('\n' + section('Local Execution'));
690
+ if (result.success) {
691
+ console.log(uiSuccess('Execution succeeded'));
692
+ if (result.result !== undefined) {
693
+ console.log(` Result: ${JSON.stringify(result.result)}`);
694
+ }
695
+ if (result.executionTime) {
696
+ console.log(` Time: ${result.executionTime}ms`);
697
+ }
698
+ if (result.computeUnitsUsed) {
699
+ console.log(` Compute units: ${result.computeUnitsUsed}`);
700
+ }
701
+ if (result.logs && result.logs.length > 0) {
702
+ console.log('\n' + section('Logs'));
703
+ result.logs.forEach((log) => {
704
+ console.log(` ${log}`);
705
+ });
706
+ }
707
+ if (result.trace && options.trace) {
708
+ console.log('\n' + section('Trace'));
709
+ // Display trace information if available
710
+ if (Array.isArray(result.trace)) {
711
+ result.trace.slice(0, 10).forEach((step, i) => {
712
+ console.log(` ${i}: ${JSON.stringify(step)}`);
713
+ });
714
+ if (result.trace.length > 10) {
715
+ console.log(` ... and ${result.trace.length - 10} more steps`);
716
+ }
717
+ }
718
+ }
719
+ }
720
+ else {
721
+ console.log(uiError('Execution failed'));
722
+ if (result.error) {
723
+ if (typeof result.error === 'object' && result.error.message) {
724
+ console.log(` Error: ${result.error.message}`);
725
+ if (result.error.type) {
726
+ console.log(` Type: ${result.error.type}`);
727
+ }
728
+ }
729
+ else {
730
+ const errorMessage = typeof result.error === 'object'
731
+ ? JSON.stringify(result.error, null, 2)
732
+ : result.error;
733
+ console.log(` Error: ${errorMessage}`);
734
+ }
735
+ }
736
+ if (result.compilationErrors && result.compilationErrors.length > 0) {
737
+ console.log('\n Compilation errors:');
738
+ result.compilationErrors.forEach((error) => {
739
+ console.log(` - ${error.message || error}`);
740
+ });
741
+ }
742
+ }
743
+ // Display output format
744
+ if (options.format === 'json') {
745
+ console.log('\n' + section('JSON Output'));
746
+ console.log(JSON.stringify(result, null, 2));
747
+ }
748
+ }
749
+ //# sourceMappingURL=execute.js.map