@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,24 @@
|
|
|
1
|
+
import { createCompilerError } from "./utils.js";
|
|
2
|
+
export function getCompilerInfo(ctx) {
|
|
3
|
+
if (!ctx.compiler) {
|
|
4
|
+
throw createCompilerError("Compiler not initialized");
|
|
5
|
+
}
|
|
6
|
+
try {
|
|
7
|
+
const info = ctx.compiler.get_compiler_stats();
|
|
8
|
+
return JSON.parse(info);
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
throw createCompilerError("Failed to get compiler info", error);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export async function discoverModules(ctx, entryPoint) {
|
|
15
|
+
if (!ctx.compiler) {
|
|
16
|
+
throw createCompilerError("Compiler not initialized");
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
return ctx.compiler.discoverModules(entryPoint);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
throw createCompilerError(`Module discovery failed: ${error instanceof Error ? error.message : String(error)}`, error);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createCompilerError } from "./utils.js";
|
|
2
|
+
export async function optimizeBytecode(ctx, bytecode) {
|
|
3
|
+
if (!ctx.compiler) {
|
|
4
|
+
throw createCompilerError("Compiler not initialized");
|
|
5
|
+
}
|
|
6
|
+
try {
|
|
7
|
+
const optimized = ctx.compiler.optimize_bytecode(bytecode);
|
|
8
|
+
return new Uint8Array(optimized);
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
throw createCompilerError("Bytecode optimization failed", error);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { CompilationContext } from "./types.js";
|
|
2
|
+
export declare function validateSource(ctx: CompilationContext, sourceCode: string): Promise<{
|
|
3
|
+
valid: boolean;
|
|
4
|
+
errors: string[];
|
|
5
|
+
warnings: string[];
|
|
6
|
+
}>;
|
|
7
|
+
export declare function validateAccountConstraints(ctx: CompilationContext, sourceCode: string, functionName: string, accounts: any[]): Promise<any>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createCompilerError } from "./utils.js";
|
|
2
|
+
export async function validateSource(ctx, sourceCode) {
|
|
3
|
+
if (!ctx.compiler) {
|
|
4
|
+
throw createCompilerError("Compiler not initialized");
|
|
5
|
+
}
|
|
6
|
+
try {
|
|
7
|
+
const result = ctx.compiler.validate_syntax(sourceCode);
|
|
8
|
+
return JSON.parse(result);
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
throw createCompilerError("Syntax validation failed", error);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export async function validateAccountConstraints(ctx, sourceCode, functionName, accounts) {
|
|
15
|
+
if (!ctx.compiler) {
|
|
16
|
+
throw createCompilerError("Compiler not initialized");
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const accountsJson = JSON.stringify(accounts);
|
|
20
|
+
const validation = ctx.compiler.validate_account_constraints(sourceCode, functionName, accountsJson);
|
|
21
|
+
return JSON.parse(validation);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
throw createCompilerError("Account constraint validation failed", error);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CLIError } from "../../types.js";
|
|
2
|
+
export declare function createCompilerError(message: string, cause?: Error): CLIError;
|
|
3
|
+
export declare function extractMetrics(result: any, defaultFormat: string): {
|
|
4
|
+
format: string;
|
|
5
|
+
exported: string;
|
|
6
|
+
detailed?: any;
|
|
7
|
+
} | undefined;
|
|
8
|
+
export declare function extractAbi(result: any, logger: any): any | undefined;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export function createCompilerError(message, cause) {
|
|
2
|
+
const error = new Error(message);
|
|
3
|
+
error.name = "CompilerError";
|
|
4
|
+
error.code = "COMPILER_ERROR";
|
|
5
|
+
error.category = "wasm";
|
|
6
|
+
error.exitCode = 1;
|
|
7
|
+
if (cause) {
|
|
8
|
+
error.details = {
|
|
9
|
+
cause: cause.message,
|
|
10
|
+
stack: cause.stack,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
return error;
|
|
14
|
+
}
|
|
15
|
+
export function extractMetrics(result, defaultFormat) {
|
|
16
|
+
if (!result || typeof result !== "object") {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const exported = result.metrics;
|
|
20
|
+
if (!exported) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
const format = result.metrics_format || defaultFormat;
|
|
24
|
+
let detailed;
|
|
25
|
+
try {
|
|
26
|
+
const getDetailed = result.get_metrics_detailed;
|
|
27
|
+
if (typeof getDetailed === "function") {
|
|
28
|
+
detailed = getDetailed.call(result);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
const getObject = result.get_metrics_object;
|
|
32
|
+
if (typeof getObject === "function") {
|
|
33
|
+
detailed = getObject.call(result);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Ignore metrics errors
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
format,
|
|
42
|
+
exported,
|
|
43
|
+
detailed,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function extractAbi(result, logger) {
|
|
47
|
+
if (!result) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
if (typeof result.get_abi === "function") {
|
|
51
|
+
try {
|
|
52
|
+
const abiJson = result.get_abi();
|
|
53
|
+
if (abiJson) {
|
|
54
|
+
return JSON.parse(abiJson);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
logger.debug("Failed to parse ABI from get_abi():", error);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const directAbi = result.abi;
|
|
62
|
+
if (!directAbi) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
if (typeof directAbi === "string") {
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(directAbi);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
logger.debug("Failed to parse ABI from result.abi string:", error);
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return directAbi;
|
|
75
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Five SDK WASM Integration Index
|
|
3
|
+
*
|
|
4
|
+
* Provides access to WASM VM and Compiler classes for the SDK.
|
|
5
|
+
* Includes cross-platform path resolution and resource management.
|
|
6
|
+
*/
|
|
7
|
+
export { FiveVM } from './vm.js';
|
|
8
|
+
export { FiveCompiler } from './compiler/index.js';
|
|
9
|
+
export { BytecodeEncoder } from '../lib/bytecode-encoder.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Five SDK WASM Integration Index
|
|
3
|
+
*
|
|
4
|
+
* Provides access to WASM VM and Compiler classes for the SDK.
|
|
5
|
+
* Includes cross-platform path resolution and resource management.
|
|
6
|
+
*/
|
|
7
|
+
// Direct re-exports from CLI WASM modules
|
|
8
|
+
export { FiveVM } from './vm.js';
|
|
9
|
+
export { FiveCompiler } from './compiler/index.js';
|
|
10
|
+
// Bytecode encoder from lib
|
|
11
|
+
export { BytecodeEncoder } from '../lib/bytecode-encoder.js';
|
|
12
|
+
// Simple WASM utilities (no over-engineering)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function loadWasmVM(): Promise<any>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FiveSDKError } from "../types.js";
|
|
2
|
+
let wasmVMInstance = null;
|
|
3
|
+
export async function loadWasmVM() {
|
|
4
|
+
if (wasmVMInstance) {
|
|
5
|
+
return wasmVMInstance;
|
|
6
|
+
}
|
|
7
|
+
try {
|
|
8
|
+
// Import existing WASM VM from five-cli infrastructure
|
|
9
|
+
const { FiveVM } = await import('./vm.js');
|
|
10
|
+
// Create a simple logger for WASM VM
|
|
11
|
+
const logger = {
|
|
12
|
+
debug: (msg) => console.debug("[WASM VM]", msg),
|
|
13
|
+
info: (msg) => console.info("[WASM VM]", msg),
|
|
14
|
+
warn: (msg) => console.warn("[WASM VM]", msg),
|
|
15
|
+
error: (msg) => console.error("[WASM VM]", msg),
|
|
16
|
+
};
|
|
17
|
+
wasmVMInstance = new FiveVM(logger); // Initialize WASM VM
|
|
18
|
+
if (wasmVMInstance.initialize) {
|
|
19
|
+
await wasmVMInstance.initialize();
|
|
20
|
+
}
|
|
21
|
+
return wasmVMInstance;
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
throw new FiveSDKError(`Failed to load WASM VM: ${error instanceof Error ? error.message : "Unknown error"}`, "WASM_LOAD_ERROR");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WASM Loader for Five VM
|
|
3
|
+
*
|
|
4
|
+
* Loader that uses the main wasm-pack generated entry point.
|
|
5
|
+
* Compatible with both Node.js and Browser environments.
|
|
6
|
+
*/
|
|
7
|
+
// Store the initialized module instance globally within this module's scope.
|
|
8
|
+
let wasmModule = null;
|
|
9
|
+
import { ConfigManager } from '../config/ConfigManager.js';
|
|
10
|
+
export async function getWasmModule() {
|
|
11
|
+
if (wasmModule) {
|
|
12
|
+
return wasmModule;
|
|
13
|
+
}
|
|
14
|
+
// Build candidate list from config and sensible defaults
|
|
15
|
+
const cfg = await ConfigManager.getInstance().get();
|
|
16
|
+
const prefer = cfg.wasm?.loader || 'auto';
|
|
17
|
+
const configured = Array.isArray(cfg.wasm?.modulePaths) ? cfg.wasm.modulePaths : [];
|
|
18
|
+
const nodeCandidates = [
|
|
19
|
+
'../../five_vm_wasm.cjs',
|
|
20
|
+
'../five_vm_wasm.cjs',
|
|
21
|
+
'./five_vm_wasm.cjs',
|
|
22
|
+
'../../five_vm_wasm.js',
|
|
23
|
+
'../five_vm_wasm.js',
|
|
24
|
+
'./five_vm_wasm.js', // Sibling check
|
|
25
|
+
];
|
|
26
|
+
const bundlerCandidates = [
|
|
27
|
+
'../../assets/vm/five_vm_wasm.cjs',
|
|
28
|
+
'../assets/vm/five_vm_wasm.cjs',
|
|
29
|
+
'../../assets/vm/five_vm_wasm.js',
|
|
30
|
+
'../assets/vm/five_vm_wasm.js',
|
|
31
|
+
// Fallback for direct import (bundler alias)
|
|
32
|
+
'five-wasm',
|
|
33
|
+
'five-vm-wasm'
|
|
34
|
+
];
|
|
35
|
+
let candidates = [];
|
|
36
|
+
candidates.push(...configured);
|
|
37
|
+
// Detect environment to prioritize candidates
|
|
38
|
+
const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
|
39
|
+
if (prefer === 'node' || (prefer === 'auto' && isNode)) {
|
|
40
|
+
candidates.push(...nodeCandidates);
|
|
41
|
+
candidates.push(...bundlerCandidates);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
candidates.push(...bundlerCandidates); // Bundler/Browser preferred
|
|
45
|
+
candidates.push(...nodeCandidates);
|
|
46
|
+
}
|
|
47
|
+
const tried = [];
|
|
48
|
+
for (const candidate of candidates) {
|
|
49
|
+
try {
|
|
50
|
+
const mod = await import(candidate);
|
|
51
|
+
// Node.js specific initialization using fs/path (Dynamic Import to avoid Browser errors)
|
|
52
|
+
if (mod && typeof mod.initSync === 'function' && isNode) {
|
|
53
|
+
try {
|
|
54
|
+
const fs = await import('fs');
|
|
55
|
+
const path = await import('path');
|
|
56
|
+
const url = await import('url');
|
|
57
|
+
const dirname = path.dirname;
|
|
58
|
+
const resolve = path.resolve;
|
|
59
|
+
const fileURLToPath = url.fileURLToPath;
|
|
60
|
+
const existsSync = fs.existsSync;
|
|
61
|
+
const readFileSync = fs.readFileSync;
|
|
62
|
+
// Resolve path safely
|
|
63
|
+
let here;
|
|
64
|
+
try {
|
|
65
|
+
here = dirname(fileURLToPath(import.meta.url));
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
// Fallback if import.meta.url is not file://
|
|
69
|
+
here = process.cwd();
|
|
70
|
+
}
|
|
71
|
+
const wasmFiles = [
|
|
72
|
+
resolve(here, '../five_vm_wasm_bg.wasm'),
|
|
73
|
+
resolve(here, '../../five_vm_wasm_bg.wasm'),
|
|
74
|
+
resolve(here, '../assets/vm/five_vm_wasm_bg.wasm'),
|
|
75
|
+
resolve(here, '../../assets/vm/five_vm_wasm_bg.wasm'),
|
|
76
|
+
];
|
|
77
|
+
for (const wf of wasmFiles) {
|
|
78
|
+
if (existsSync(wf)) {
|
|
79
|
+
mod.initSync(readFileSync(wf));
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (syncErr) {
|
|
85
|
+
// Ignore sync init errors in node, might work with default init
|
|
86
|
+
// tried.push({ path: candidate, error: syncErr });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Universal initialization (Browser/Node) if default export is init function
|
|
90
|
+
if (mod && typeof mod.default === 'function') {
|
|
91
|
+
try {
|
|
92
|
+
await mod.default();
|
|
93
|
+
}
|
|
94
|
+
catch (initErr) {
|
|
95
|
+
tried.push({ path: candidate, error: initErr });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (mod) {
|
|
99
|
+
wasmModule = mod;
|
|
100
|
+
return wasmModule;
|
|
101
|
+
}
|
|
102
|
+
tried.push({ path: candidate, error: 'Module import returned null/undefined' });
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
tried.push({ path: candidate, error: e });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const attempted = tried
|
|
109
|
+
.map(t => ` - ${t.path}: ${t.error instanceof Error ? t.error.message : String(t.error)}`)
|
|
110
|
+
.join('\n');
|
|
111
|
+
throw new Error(`Five VM WASM module not found or failed to load. Please ensure five-wasm is built.\nAttempts:\n${attempted}`);
|
|
112
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { VMExecutionOptions, VMExecutionResult, AccountInfo, Logger } from '../types.js';
|
|
2
|
+
export declare class FiveVM {
|
|
3
|
+
private vm;
|
|
4
|
+
private logger;
|
|
5
|
+
private initialized;
|
|
6
|
+
constructor(logger: Logger);
|
|
7
|
+
initialize(): Promise<void>;
|
|
8
|
+
private isWasmOnlyExecution;
|
|
9
|
+
private hasFiveMagic;
|
|
10
|
+
private looksLikeScriptHeader;
|
|
11
|
+
private looksLikeOptimizedHeader;
|
|
12
|
+
execute(options: VMExecutionOptions): Promise<VMExecutionResult>;
|
|
13
|
+
private parseVMResult;
|
|
14
|
+
executeFunction(bytecode: Uint8Array, functionIndex: number, parameters: Array<{
|
|
15
|
+
type: string;
|
|
16
|
+
value: any;
|
|
17
|
+
}>, accounts?: AccountInfo[]): Promise<VMExecutionResult>;
|
|
18
|
+
getState(): Promise<any>;
|
|
19
|
+
validateBytecode(bytecode: Uint8Array): Promise<{
|
|
20
|
+
valid: boolean;
|
|
21
|
+
error?: string;
|
|
22
|
+
}>;
|
|
23
|
+
getVMConstants(): any;
|
|
24
|
+
private convertAccountsToWasm;
|
|
25
|
+
private loggerwarn;
|
|
26
|
+
private createVMError;
|
|
27
|
+
getVMInfo(): {
|
|
28
|
+
version: string;
|
|
29
|
+
features: string[];
|
|
30
|
+
};
|
|
31
|
+
isReady(): boolean;
|
|
32
|
+
cleanup(): void;
|
|
33
|
+
}
|
package/dist/wasm/vm.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// Five VM WASM integration.
|
|
2
|
+
import { getWasmModule } from './loader.js';
|
|
3
|
+
let FiveVMWasm;
|
|
4
|
+
let WasmAccount;
|
|
5
|
+
let ParameterEncoder;
|
|
6
|
+
let wrap_with_script_header;
|
|
7
|
+
const SCRIPT_HEADER_LEN = 64; // ScriptHeader::LEN
|
|
8
|
+
const OPTIMIZED_HEADER_LEN = 7; // OptimizedHeader V2 size
|
|
9
|
+
const FIVE_MAGIC = [0x35, 0x49, 0x56, 0x45];
|
|
10
|
+
export class FiveVM {
|
|
11
|
+
constructor(logger) {
|
|
12
|
+
this.vm = null;
|
|
13
|
+
this.initialized = false;
|
|
14
|
+
this.logger = logger;
|
|
15
|
+
}
|
|
16
|
+
async initialize() {
|
|
17
|
+
try {
|
|
18
|
+
this.logger.debug('[DEBUG] Starting VM WASM initialization...');
|
|
19
|
+
const wasmModule = await getWasmModule();
|
|
20
|
+
FiveVMWasm = wasmModule.FiveVMWasm;
|
|
21
|
+
WasmAccount = wasmModule.WasmAccount;
|
|
22
|
+
ParameterEncoder = wasmModule.ParameterEncoder;
|
|
23
|
+
if (typeof wasmModule.wrap_with_script_header !== 'function') {
|
|
24
|
+
// Warning only? Or throw?
|
|
25
|
+
this.logger.warn('WASM VM missing wrap_with_script_header');
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
wrap_with_script_header = wasmModule.wrap_with_script_header;
|
|
29
|
+
}
|
|
30
|
+
this.initialized = true;
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
throw this.createVMError('Five VM WASM modules not found. Please run "npm run build:wasm".', error);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
isWasmOnlyExecution() {
|
|
37
|
+
// Check basic env
|
|
38
|
+
if (typeof process !== 'undefined' && process.argv) {
|
|
39
|
+
if (process.argv.includes('local'))
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
hasFiveMagic(data) {
|
|
45
|
+
if (data.length < FIVE_MAGIC.length)
|
|
46
|
+
return false;
|
|
47
|
+
return FIVE_MAGIC.every((byte, index) => data[index] === byte);
|
|
48
|
+
}
|
|
49
|
+
looksLikeScriptHeader(data) {
|
|
50
|
+
if (data.length < SCRIPT_HEADER_LEN)
|
|
51
|
+
return false;
|
|
52
|
+
if (!this.hasFiveMagic(data))
|
|
53
|
+
return false;
|
|
54
|
+
const encodedLen = data[4] + (data[5] << 8) + (data[6] << 16);
|
|
55
|
+
const payloadLen = data.length - SCRIPT_HEADER_LEN;
|
|
56
|
+
return encodedLen === payloadLen;
|
|
57
|
+
}
|
|
58
|
+
looksLikeOptimizedHeader(data) {
|
|
59
|
+
if (data.length < OPTIMIZED_HEADER_LEN)
|
|
60
|
+
return false;
|
|
61
|
+
if (!this.hasFiveMagic(data))
|
|
62
|
+
return false;
|
|
63
|
+
return !this.looksLikeScriptHeader(data);
|
|
64
|
+
}
|
|
65
|
+
async execute(options) {
|
|
66
|
+
if (!this.initialized)
|
|
67
|
+
await this.initialize();
|
|
68
|
+
if (!this.initialized)
|
|
69
|
+
throw this.createVMError('VM not initialized');
|
|
70
|
+
const startTime = Date.now();
|
|
71
|
+
try {
|
|
72
|
+
let scriptData;
|
|
73
|
+
const hasScriptHeader = this.looksLikeScriptHeader(options.bytecode);
|
|
74
|
+
const hasOptimizedHeader = this.looksLikeOptimizedHeader(options.bytecode);
|
|
75
|
+
if (hasScriptHeader || hasOptimizedHeader) {
|
|
76
|
+
scriptData = options.bytecode;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
if (!wrap_with_script_header) {
|
|
80
|
+
// Fallback if binding missing
|
|
81
|
+
scriptData = options.bytecode;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
const wrapped = wrap_with_script_header(options.bytecode);
|
|
85
|
+
scriptData = new Uint8Array(wrapped);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Create VM instance
|
|
89
|
+
this.vm = new FiveVMWasm(scriptData);
|
|
90
|
+
const wasmAccounts = this.convertAccountsToWasm(options.accounts || []);
|
|
91
|
+
const inputData = options.inputData || new Uint8Array(0);
|
|
92
|
+
// Execute
|
|
93
|
+
const result = this.vm.execute_partial(inputData, wasmAccounts);
|
|
94
|
+
const executionTime = Date.now() - startTime;
|
|
95
|
+
return this.parseVMResult(result, executionTime);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
const executionTime = Date.now() - startTime;
|
|
99
|
+
return {
|
|
100
|
+
success: false,
|
|
101
|
+
error: {
|
|
102
|
+
type: 'ExecutionError',
|
|
103
|
+
message: error instanceof Error ? error.message : 'Unknown VM error',
|
|
104
|
+
instructionPointer: 0,
|
|
105
|
+
stackTrace: [],
|
|
106
|
+
errorCode: -1
|
|
107
|
+
},
|
|
108
|
+
executionTime,
|
|
109
|
+
logs: []
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
parseVMResult(result, executionTime) {
|
|
114
|
+
let resultValue = null;
|
|
115
|
+
let success = false;
|
|
116
|
+
let status = 'Failed';
|
|
117
|
+
let errorMessage = undefined;
|
|
118
|
+
// Handle simple string results.
|
|
119
|
+
if (typeof result === 'string') {
|
|
120
|
+
if (result.startsWith('Ok(')) {
|
|
121
|
+
success = true;
|
|
122
|
+
status = 'Completed';
|
|
123
|
+
// Regex parsing for basic types
|
|
124
|
+
const u64Match = result.match(/Ok\(Some\(U64\((\d+)\)\)\)/);
|
|
125
|
+
if (u64Match)
|
|
126
|
+
resultValue = { type: 'U64', value: parseInt(u64Match[1]) };
|
|
127
|
+
// ... (Can expand primitive parsing if needed, but maintaining minimal logic here)
|
|
128
|
+
}
|
|
129
|
+
else if (result.startsWith('Err(')) {
|
|
130
|
+
success = false;
|
|
131
|
+
status = 'Failed';
|
|
132
|
+
errorMessage = result;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// Handle Object result (WASM binding struct)
|
|
137
|
+
const resultStatus = typeof result.status === 'function' ? result.status() : result.status;
|
|
138
|
+
success = resultStatus === 'Completed';
|
|
139
|
+
status = resultStatus || 'Completed';
|
|
140
|
+
const resultErrorMessage = typeof result.error_message === 'function' ? result.error_message() : result.error_message;
|
|
141
|
+
errorMessage = resultErrorMessage;
|
|
142
|
+
if (result.has_result_value) {
|
|
143
|
+
const raw = result.get_result_value;
|
|
144
|
+
resultValue = typeof raw === 'bigint' ? Number(raw) : raw;
|
|
145
|
+
}
|
|
146
|
+
else if (result.result_value !== undefined) {
|
|
147
|
+
resultValue = result.result_value;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
success,
|
|
152
|
+
result: resultValue,
|
|
153
|
+
computeUnitsUsed: typeof result === 'object' ? result.compute_units_used : 0,
|
|
154
|
+
executionTime,
|
|
155
|
+
logs: [],
|
|
156
|
+
status,
|
|
157
|
+
stoppedAt: typeof result === 'object' ? result.stopped_at_opcode_name : undefined,
|
|
158
|
+
errorMessage
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
async executeFunction(bytecode, functionIndex, parameters, accounts) {
|
|
162
|
+
if (!this.initialized)
|
|
163
|
+
await this.initialize();
|
|
164
|
+
// Import BytecodeEncoder
|
|
165
|
+
const { BytecodeEncoder } = await import('../lib/bytecode-encoder.js');
|
|
166
|
+
// Pass typed parameters through to WASM encoder
|
|
167
|
+
const typedParams = parameters.map(param => ({
|
|
168
|
+
type: param.type,
|
|
169
|
+
value: param.value
|
|
170
|
+
}));
|
|
171
|
+
if (!ParameterEncoder || !ParameterEncoder.encode_execute) {
|
|
172
|
+
throw new Error('ParameterEncoder WASM binding not loaded or missing encode_execute');
|
|
173
|
+
}
|
|
174
|
+
// Use WASM binding to encode parameters (returns fixed-size encoded params)
|
|
175
|
+
const rawParams = ParameterEncoder.encode_execute(functionIndex, typedParams);
|
|
176
|
+
// EXECUTE_INSTRUCTION is 9 (matches on-chain protocol)
|
|
177
|
+
const discriminator = new Uint8Array([9]);
|
|
178
|
+
// Encode function index as u32 little endian
|
|
179
|
+
const functionIndexBytes = BytecodeEncoder.encodeU32(functionIndex);
|
|
180
|
+
// Assemble: [discriminator, function_index(u32), param_count(u32), rawParams...]
|
|
181
|
+
// Construct instruction:
|
|
182
|
+
const functionIndexArr = BytecodeEncoder.encodeU32(functionIndex); // 4 bytes
|
|
183
|
+
const paramCountArr = BytecodeEncoder.encodeU32(parameters.length);
|
|
184
|
+
const properInstructionData = new Uint8Array(discriminator.length + functionIndexArr.length + paramCountArr.length + rawParams.length);
|
|
185
|
+
properInstructionData.set(discriminator, 0);
|
|
186
|
+
properInstructionData.set(functionIndexArr, discriminator.length);
|
|
187
|
+
properInstructionData.set(paramCountArr, discriminator.length + functionIndexArr.length);
|
|
188
|
+
properInstructionData.set(rawParams, discriminator.length + functionIndexArr.length + paramCountArr.length);
|
|
189
|
+
return await this.execute({
|
|
190
|
+
bytecode,
|
|
191
|
+
inputData: properInstructionData,
|
|
192
|
+
accounts: accounts || []
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
async getState() {
|
|
196
|
+
if (!this.vm)
|
|
197
|
+
throw this.createVMError('No VM instance available');
|
|
198
|
+
return JSON.parse(this.vm.get_state());
|
|
199
|
+
}
|
|
200
|
+
async validateBytecode(bytecode) {
|
|
201
|
+
if (!this.initialized)
|
|
202
|
+
await this.initialize();
|
|
203
|
+
try {
|
|
204
|
+
const valid = FiveVMWasm.validate_bytecode(bytecode);
|
|
205
|
+
return { valid };
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
return { valid: false, error: String(error) };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
getVMConstants() {
|
|
212
|
+
if (!this.initialized)
|
|
213
|
+
throw this.createVMError('VM not initialized');
|
|
214
|
+
return JSON.parse(FiveVMWasm.get_constants());
|
|
215
|
+
}
|
|
216
|
+
convertAccountsToWasm(accounts) {
|
|
217
|
+
const wasmAccounts = [];
|
|
218
|
+
for (const account of accounts) {
|
|
219
|
+
try {
|
|
220
|
+
const wasmAccount = new WasmAccount(account.key, account.data || new Uint8Array(0), account.lamports || 0, account.isWritable || false, account.isSigner || false, account.owner || new Uint8Array(32));
|
|
221
|
+
wasmAccounts.push(wasmAccount);
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
this.loggerwarn(`Failed to convert account ${account.key}: ${error}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return wasmAccounts;
|
|
228
|
+
}
|
|
229
|
+
loggerwarn(msg) {
|
|
230
|
+
if (this.logger && this.logger.warn)
|
|
231
|
+
this.logger.warn(msg);
|
|
232
|
+
else
|
|
233
|
+
console.warn(msg);
|
|
234
|
+
}
|
|
235
|
+
createVMError(message, cause) {
|
|
236
|
+
const error = new Error(message);
|
|
237
|
+
error.name = 'VMError';
|
|
238
|
+
error.code = 'VM_ERROR';
|
|
239
|
+
error.category = 'wasm';
|
|
240
|
+
if (cause) {
|
|
241
|
+
error.details = { cause: cause.message };
|
|
242
|
+
}
|
|
243
|
+
return error;
|
|
244
|
+
}
|
|
245
|
+
getVMInfo() {
|
|
246
|
+
return { version: '1.0.0', features: ['wasm', 'sdk'] };
|
|
247
|
+
}
|
|
248
|
+
isReady() { return this.initialized; }
|
|
249
|
+
cleanup() { this.vm = null; }
|
|
250
|
+
}
|