@leachain/vm-shim is a reusable VM shim for Lea-chain WebAssembly modules, providing the necessary host environment for running smart contracts compatibly in both Node.js and browser environments.
- Environment-Agnostic: Works seamlessly in both Node.js and modern web browsers.
- Memory Helpers: Includes utility functions (
copyToWasm,readFromWasm,malloc) for easier memory management. - Secure by Default: Provides a sandboxed environment with no filesystem or network access unless explicitly passed in.
- Configurable: Allows overriding the default
aborthandler and extending the environment with custom functions. - Typed API: Includes TypeScript definitions for a better developer experience.
npm install @leachain/vm-shimThe primary export is createShim, which generates the importObject and exposes helper functions under utils for a WebAssembly instance.
import { promises as fs } from 'fs';
import { createShim } from '@leachain/vm-shim';
async function runWasm() {
// 1. Create the shim instance
const {
importObject,
bindInstance,
utils: { copyToWasm, readFromWasm }
} = createShim();
// 2. Read your Wasm module bytes
const wasmBytes = await fs.readFile('./path/to/your_contract.wasm');
// 3. Instantiate the module with the shim's import object
const { instance } = await WebAssembly.instantiate(wasmBytes, importObject);
// 4. IMPORTANT: Bind the created instance to the shim
// This allows host functions and helpers to access Wasm memory and exports.
bindInstance(instance);
// 5. Use helpers to interact with Wasm memory
const data = new TextEncoder().encode("Hello from JS");
const ptr = copyToWasm(data); // Allocates and copies data
// 6. Call an exported function from your Wasm module
const resultPtr = instance.exports.process_data(ptr, data.length);
// 7. Read the result back from Wasm memory
const result = readFromWasm(resultPtr, 13); // Assuming result is 13 bytes
console.log(`Wasm function returned: ${new TextDecoder().decode(result)}`);
}
runWasm().catch(console.error);const { promises: fs } = require('fs');
const { createShim } = require('@leachain/vm-shim');
async function runWasm() {
// 1. Create the shim instance
const {
importObject,
bindInstance,
utils: { copyToWasm }
} = createShim();
// 2. Read your Wasm module bytes
const wasmBytes = await fs.readFile('./path/to/your_contract.wasm');
// 3. Instantiate the module
const { instance } = await WebAssembly.instantiate(wasmBytes, importObject);
// 4. Bind the instance
bindInstance(instance);
// 5. Use a helper and call a Wasm function
const data = new TextEncoder().encode("Hello");
const ptr = copyToWasm(data);
instance.exports.your_function(ptr, data.length);
}
runWasm().catch(console.error);config<object>(Optional)onAbort<(message: string) => void>: Overrides the default abort behavior. The default logs an error and exits (Node.js) or throws an error (browser).customEnv<object>: An object with custom host functions to add to theenvnamespace. See Extending the Shim.
- Returns
<object>importObject<object>: The WebAssembly import object. Pass this toWebAssembly.instantiate.bindInstance<(instance: WebAssembly.Instance) => void>: Binds the Wasm instance to the shim. Call after instantiation.print<object>: Colored logging utility withred,orange,green,blue.utils<object>: Helper functions for memory and allocation:copyToWasm(data: Uint8Array): numberreadFromWasm(ptr: number, length: number): Uint8Arraymalloc(length: number): numberreset(): void
A utility function to read a null-terminated UTF-8 string from the Wasm instance's memory.
memory<WebAssembly.Memory>: The exported memory from your Wasm instance (instance.exports.memory).ptr<number>: The pointer (memory address) of the string.- Returns
<string>: The decoded string.
The shim provides several host functions by default, available under the env namespace.
__lea_abort(line: number): Handles fatal errors.__lea_log(ptr: number, len: number): Prints a message from Wasm to the host console.__lea_ubsen(...): Handles errors from the Undefined Behavior Sanitizer (UBSan).__lea_randombytes(ptr: number, len: number): Fills a buffer in Wasm memory with secure random bytes.__execution_limit(...),__address_add(...),__execution_stack_add(...): Hooks for blockchain-specific operations.
You can add your own host functions to the env namespace by passing a customEnv object in the createShim configuration.
import { createShim } from '@leachain/vm-shim';
const customEnv = {
my_custom_function: (arg1, arg2) => {
console.log(`my_custom_function called with: ${arg1}, ${arg2}`);
return 42;
}
};
const { importObject } = createShim({ customEnv });
// ... proceed with Wasm instantiationYour WebAssembly module can then import and call this function (e.g., extern int my_custom_function(int arg1, int arg2); in C/C++).
Contributions are welcome! Please open an issue or submit a pull request for any bugs, features, or improvements.
This project is licensed under the ISC License. See the LICENSE file for details.