Deterministic smart contract execution engine for the XChain Platform. Runs JavaScript contracts in sandboxed V8 isolates with AST-based gas metering, ensuring identical results across all indexer nodes. Plugs into the XChain Indexer as the runtime for DEPLOY and EXECUTE actions.
- Sandboxed V8 isolates: contracts run in isolated-vm with no access to the host process, filesystem, or network
- Deterministic execution: all non-deterministic APIs (Date, Math.random, setTimeout, etc.) stripped; same input always produces same output
- AST-based gas metering: acorn parses contract code and injects
__gas()calls at control flow points; no V8 modifications required - 19 emittable actions: contracts can emit SEND, DESTROY, ISSUE, MINT, ORDER, DISPENSER, DIVIDEND, AIRDROP, CALLBACK, FILE, LIST, COINPAY, SWEEP, LINK, BROADCAST, MESSAGE, VOTE, EXECUTE (cross-contract call: deferred, caller-funded gasLimit, max depth 4, no return value), and XCALL (cross-chain call via
emit.crossExecute: federation-relayed to a contract on another chain, outcome delivered to a callback method) - Cross-chain contract calls:
emit.crossExecute(...)emits an XCALL for federation relay to a target chain; the receiving contract must list the method in its exportedcrossCallablearray; outcomes arrive asynchronously via a named callback method;crossChain.getCallResult(callId)reads the terminal result - External attestation:
xchain.attestation.request(...)namespace lets contracts emitATTESTv0 (request) against a registered provider (http_get,llm) with a deterministicrequest_id; the hub federation reaches PBFT quorum off-chain and submitsATTESTv1 (response) to invoke the contract's callback. Payload cap: 8192 bytes. - Deterministic math:
xchain.math.*wraps mathjs bignumber with string I/O; no floating-point; nativeMath.sqrt/pow/log/log2/log10rejected at deploy time - Contract state management: key-value state with dirty tracking, key count limits, and value size limits
- Deploy-time validation: syntax checking via V8 + acorn, reserved identifier detection, banned Math/literal/async/generator/WebAssembly checks (see
CONSENSUS_RULESinsrc/lint-core.js), float usage warnings - Per-block compilation cache: V8 cached compilation data eliminates redundant parsing for hot contracts
- Resource limits: configurable memory (MB), gas ceiling, emission cap, state key cap, value size cap, wall-clock timeout
- Multi-method contracts: contracts export a function (single entry) or an object with named methods
Full VM architecture and protocol details are available in the xchain-documentation repository:
| Document | Description |
|---|---|
| Smart Contracts | VM architecture, contract model, bounded execution, use cases |
| Block Hashes | Ledger, actions, and contract hashes: how contract state is verified |
| Ledger | Double-entry ledger: how contract derived addresses participate |
| DEPLOY | DEPLOY action spec: code encoding, api_version, gas costs |
| EXECUTE | EXECUTE action spec: method calls, params, gas metering |
| DEPOSIT | DEPOSIT action spec: transferring tokens into contract custody |
| WITHDRAW | WITHDRAW action spec: owner-initiated withdrawal from contract |
| Indexer Database | Schema reference: contracts, contract_state, executions, emissions tables |
| Fee Schedule | Unified gas schedule: VM gas costs, GAS_PRICE, fee conversion |
git clone https://github.com/XChain-Platform/xchain-vm.git
cd xchain-vm
npm installisolated-vm requires native C++ compilation. Install build dependencies:
# Ubuntu/Debian
sudo apt-get install -y build-essential python3 libnghttp2-dev libicu-dev libbrotli-dev libc-ares-dev
# macOS (Xcode command-line tools)
xcode-select --installIf npm install fails to compile isolated-vm, rebuild after installing dependencies:
npm rebuild isolated-vmconst XChainVM = require('xchain-vm');
const vm = new XChainVM({
gasSchedule: {
VM_COMPUTATION: 1,
VM_STATE_READ: 100,
VM_STATE_WRITE: 200,
VM_STATE_DELETE: 100,
VM_ORACLE_READ: 100,
VM_CROSSCHAIN_READ: 100,
VM_ATTEST_REQUEST: 5000,
VM_EMISSION: 500,
VM_XCALL_REQUEST: 1000,
VM_XCALL_CALLBACK: 1000
},
gasCeiling: 1000000,
limits: {
maxCpuTimeMs: 30000,
maxMemory: 8,
maxEmissions: 50,
maxStateKeys: 10000,
maxStateValueSize: 65536,
maxCodeSize: 65536
}
});
const result = await vm.execute({
code: `module.exports = {
increment: function(xchain) {
var count = xchain.state.get('counter') || '0';
count = xchain.math.add(count, '1');
xchain.state.set('counter', count);
return count;
}
};`,
state: { counter: '5' },
method: 'increment',
params: [],
caller: 'source_address',
contractAddress: 'C:BTC:100',
blockContext: { height: 500, timestamp: 1700000000, hash: 'blockhash' }
});
// result = {
// success: true,
// error: null,
// gasUsed: 302,
// returnValue: '"6"',
// stateChanges: [{ key: 'counter', value: '6' }],
// stateDeletes: [],
// emittedActions: [],
// logs: []
// }A local on-ramp for authoring XChain contracts: write, lint, gas-profile, and
unit-test a contract with millisecond feedback and no regtest stack. Ships as
two bins plus a library at require('xchain-vm/toolkit').
# Scaffold a project (contract + simulator test + README); add --ts for TypeScript
npx create-xchain-contract my-token
npx create-xchain-contract my-token --ts
# Static determinism gate + gas estimate (runs on ANY OS/CPU; no isolated-vm)
xchain-foundry lint contracts/my-token.js
# Deploy + run a method in the in-memory simulator (Node 22 / Linux)
xchain-foundry simulate contracts/my-token.js --constructor 5 --method increment --params 3
# AI-assisted authoring (Tier 3): print a ready-to-use prompt, no network call or key
xchain-foundry describe "an escrow that releases on a signed delivery attestation"
xchain-foundry from-solidity MyContract.sol
# Close the loop: run a model's reply through the deploy gate, printing a repair
# prompt on failure (use `-` for stdin)
xchain-foundry validate model-response.txtProgrammatic use:
const { ContractSimulator, runGate } = require('xchain-vm/toolkit');
runGate(source); // { ok, errors, advisories, warnings, gas }
const sim = new ContractSimulator({ coin: 'BTC' });
sim.setBalance('alice', 'GOLD', '1000'); // seed read-only ledger/oracle state
const { contractIndex } = await sim.deploy(source, { constructorParams: ['5'] });
const res = await sim.call(contractIndex, 'increment', ['3']); // state persists across calls
await sim.close();The lint gate (banned-API / float / async / syntax checks + gas estimate)
is pure JS and runs anywhere. The simulator executes contracts, so it needs
the isolated-vm binding (Node 22 / Linux); on a macOS dev box use lint
locally and run the simulator / generated tests on Node-22 Linux (CI). See the
src/toolkit/ modules for details.
| Command | Description |
|---|---|
npm test |
Unit tests (669 tests, 30s timeout) |
npm run test:toolkit |
Developer-toolkit tests (gate/scaffold/transpile run anywhere; simulator on Node-22 Linux) (52 tests) |
npm run test:integration |
Integration tests (164 tests) |
npm run test:security |
Security tests (201 tests) |
npm run test:boundary |
Boundary condition tests (115 tests) |
npm run test:determinism |
Determinism tests (79 tests) |
npm run test:performance |
Performance benchmarks-as-tests (5 tests) |
npm run test:all |
Every *.test.js under test/ (1,653+ tests) |
npm run test:e2e |
E2E tests only (64 tests) |
npm run smoke |
Smoke tests (10 tests, < 5s) |
npm run test:fuzz |
Fuzz / property-based tests (57 tests) |
npm run test:chaos |
Chaos engineering tests (76 tests) |
npm run test:regression:smoke |
P0 regression (11 tests, < 50ms) |
npm run test:regression:core |
P0+P1 regression (31 tests, < 200ms) |
npm run test:regression:full |
P0-P3 + gate/pin regression (128 tests, < 1s) |
npm run test:regression:nightly |
Regression + E2E + fuzz + chaos phase 1 |
npm run test:regression:release |
All tests + mutation testing |
npm run mutation |
Mutation testing (Stryker, full suite) |
npm run bench:quick |
Pipeline + gateway benchmarks |
npm run bench:full |
All benchmarks except soak |
| Category | Tests | Description |
|---|---|---|
| Metering | 69 | AST injection points, edge cases (arrow bodies, directive prologue, nested ternary, optional chaining, deep binary expressions) |
| Gas | 23 | Ceiling enforcement, boundary conditions, cumulative charges, negative/float/non-number rejection |
| Math | 48 | Precision (0.1+0.2=0.3), large numbers, comparisons, division by zero, string I/O, input length limits |
| State | 38 | CRUD, delete-then-set cycles, key/value/key-size limits, NaN/Infinity rejection, UTF-8 handling, insertion order |
| Collector | 16 | Emission cap, log truncation (byte-aware), param copy isolation, multi-byte truncation |
| Compilation | 2 | Metering benchmark, worst-case 64KB contract compilation time |
| Sandbox | 37 | 18 blocked globals, constructor escapes, prototype chain, eval/Function, Math freeze, xchain freeze |
| Gateway | 39 | State ops, emit queuing, math, revert/require, logging, method routing, oracle stubs |
| Gateway-Emit | 67 | All 19 emit types, required field validation, gas charging, params copy/rejection |
| Validator | 15 | Action allowlist, unknown action rejection, params type validation |
| Syntax | 44 | Valid/invalid code, ES2020 support, __gas rejection, float warnings, edge cases |
| Errors | 18 | ContractRevertError, GasExhaustedError construction and instanceof checks |
| Isolate | 14 | Isolate creation, compilation, disposal, cached data |
| Newer coverage (~20 files added since this table was last authored) | 239 | Contract-language-version guards, execution-mode dispatch, gateway .d.ts parity + guard-mode, cross-contract emit.execute / cross-chain emit.crossExecute call-path derivation, lint CLI/generator/hardening/parity/shared-rules, metered compilation cache, protocol constants, capability-manifest reading, read-only accessors, xcall bounds parity, limits backfill, dependency-advisory scan, mutation-report scripting, error-classification corroboration, consensus-runtime gating. Row total is exact (669 minus the 13 rows above); the file-to-topic grouping is a best-effort summary and needs operator review, not a verified per-row breakdown |
| Category | Tests | Description |
|---|---|---|
| Deploy & Execute | 8 | Contract lifecycle, invalid syntax rejection, code size limits |
| Deposit & Withdraw | 5 | Token custody transfers, balance tracking |
| Error Handling | 6 | Revert recovery, gas exhaustion, runtime errors |
| State Persistence | 6 | Cross-execution state, multi-contract isolation |
| Security | 9 | Sandbox enforcement in full pipeline |
| Resource Limits | 9 | Gas, OOM, timeout, emission/state floods |
| Determinism | 3 | 10-run consistency, block replay |
| Complex Workflows | 7 | AMM swap, vesting, multi-action, sequential counter |
| Gas Fees | 5 | Fee accounting, gas charging on failure |
| Oracle & Cross-chain | 6 | Oracle price reads, cross-chain attestation |
Full-pipeline coverage promoted out of the old embedded "Index (Integration)" unit category into its own test/integration/ suite: result structure, atomicity, return values, method routing, error classification, context, and all 19 emit types.
Sandbox escape vectors, error spoofing, gas-bypass attempts, prototype pollution, math abuse, and information-leakage probes, now in a dedicated test/security/ suite (grown well past the RISK-01 through RISK-15 set the table used to enumerate; see the suite for the current catalog).
Gas ceiling, timeout, memory, code size, state management, emissions, logs, return values, math, metering, sandbox, gateway, emit-field, and compound-interaction edge cases in test/boundary/.
VM instantiation, sandbox creation, basic execution, method dispatch, gateway, math, syntax, and revert, unchanged from the original set.
Cross-run and cross-process determinism guarantees in test/determinism/: golden-hash fixtures, consensus-parameter and consensus-runtime gates, cross-repo call-id byte-matching, cache/subprocess/stack-depth/timeout-fee determinism, plus a known-red probe subset (npm run test:known-red) that intentionally documents non-determinism failure modes rather than passing.
Latency/throughput assertions in test/performance/ (npm run test:performance), distinct from the bench/ scenario scripts below.
xchain-foundry / create-xchain-contract developer-toolkit coverage: gate, scaffold, and TypeScript-strip logic run on any OS; simulator-backed cases need the isolated-vm binding (Node 22 / Linux).
Regression Tests (128 via test:regression:full; +31 determinism-tagged tests live alongside them in test/regression/ but run under test:determinism)
| Tier | Tests | Target Time | Scope |
|---|---|---|---|
| P0 Smoke | 11 | < 50ms | VM boot, sandbox, basic execution, emit, revert |
| P1 Security | 20 | < 200ms | Blocked globals, escape vectors, gas bypass, atomicity, determinism |
| P2 Functional | 41 | < 300ms | Metering injection, state ops, all 19 emit types, math, validation |
| P3 Integration | 24 | < 350ms | Resource limits, full pipeline, cache, E2E lifecycle, state isolation |
| Gate / pin regressions | 32 | < 1s | binary-alloc-gate, compilation-cache-live, math-golden, metering-eval-order-gate, slash-token-delimiter-gate, state-key-nul-gate, state-key-type-gate: single-issue pinned regressions, one file each |
Property-based and adversarial input testing: code mutation, argument injection, emission payloads, state operations, math properties, sandbox escapes, determinism verification, resource exhaustion.
3-phase resilience testing: Phase 1 (critical failures), Phase 2 (load and concurrency), Phase 3 (parser divergence and precision boundaries).
Stryker with Mocha runner plus custom VM-specific operators. Mutant/module counts come from a live Stryker run, not a static scan, so they are not re-verified here; treat the previously published "1,136 mutants across 13 modules" as unverified pending a fresh npm run mutation pass.
Static it()/test() occurrence count across every file under test/ (all categories above, including toolkit and the determinism-tagged files inside test/regression/). This is a source-line count, not a suite-run count: property-based fuzz cases and looped fixtures can execute more assertions per matched line than this number shows, so treat it as a floor, not an exact total.
Fixture contracts in test/contracts/ cover real-world and adversarial scenarios:
| Contract | Purpose |
|---|---|
simple_send.js |
Reads state, emits one SEND |
state_counter.js |
Increments a counter, tests state read/write cycle |
amm_swap.js |
Constant-product AMM with xchain.math, emits SENDs |
vesting.js |
Time-based release using block height |
multi_method.js |
Object export with multiple named methods |
infinite_loop.js |
Must hit gas limit |
memory_bomb.js |
Must hit memory limit |
sandbox_escape.js |
All known escape techniques, must all fail |
emit_flood.js |
51 emits, must fail at 51st |
state_flood.js |
10,001 keys, must fail at limit |
bad_math.js |
Native 0.1+0.2 vs xchain.math |
compile_bomb.js |
Deeply nested expressions for compilation benchmark |
oracle_read.js |
Oracle stub API (returns null until Track B) |
Contract Source Code
|
acorn (parse AST)
|
metering.js (inject __gas() calls at control flow points)
|
astring (regenerate source from modified AST)
|
isolated-vm (V8 isolate)
|-- sandbox.js (strip non-deterministic globals)
|-- gateway.js (inject xchain object via ivm.Reference callbacks)
|-- gas.js (__gas -> chargeComputation on host side)
+-- script.runSync() (execute with wall-clock timeout)
|
Collect results
|-- state.js -> stateChanges, stateDeletes
|-- collector.js -> emittedActions, logs
+-- gas.js -> gasUsed
|
Return to indexer (execute.js)
xchain-vm/
|-- package.json
|-- src/
| |-- index.js (XChainVM class, main entry point)
| |-- isolate.js (V8 isolate management: create, compile, dispose)
| |-- gateway.js (builds the xchain gateway object)
| |-- gateway-emit.js (emit API: 19 action types, incl. cross-contract emit.execute and cross-chain emit.crossExecute)
| |-- gas.js (gas tracking and ceiling enforcement)
| |-- sandbox.js (strips non-deterministic APIs)
| |-- metering.js (AST-based gas injection)
| |-- validator.js (validates emitted actions)
| |-- syntax.js (deploy-time validation + float warnings)
| |-- math.js (deterministic math, wraps mathjs bignumber)
| |-- state.js (contract state management)
| |-- collector.js (emission and log collection)
| +-- errors.js (ContractRevertError, GasExhaustedError)
|-- test/
| |-- *.test.js (36 unit test files, 669 tests)
| |-- contracts/ (13 test fixture contracts)
| |-- integration/ (164 tests: full pipeline, all 19 emit types)
| |-- security/ (201 tests: sandbox escapes, gas bypass, info leakage)
| |-- boundary/ (115 tests: gas/timeout/memory/code-size/state edges)
| |-- smoke/ (10 tests)
| |-- determinism/ (79 tests, incl. a known-red probe subset)
| |-- performance/ (5 tests, distinct from bench/ below)
| |-- toolkit/ (52 tests: xchain-foundry gate/scaffold/simulate)
| |-- e2e/ (10 E2E test files, 64 tests + helpers + contracts)
| |-- fuzz/ (9 fuzz test files, 57 tests + harness + generators)
| |-- chaos/ (3-phase chaos tests, 76 tests + helpers + contracts)
| |-- regression/ (4-tier + gate/pin regression suite, 159 tests + helpers)
| +-- mutation/ (stryker-xchain-vm-mutator/: custom mutation testing operators)
+-- bench/ (5 benchmark scenarios + harness + contracts)
The VM integrates with the XChain Indexer at xchain-indexer/src/actions/execute.js. The indexer instantiates a single XChainVM instance at startup and calls vm.execute() for each EXECUTE action and vm.validateSyntax() for each DEPLOY action.
// In xchain-indexer/src/actions.js
const XChainVM = require('xchain-vm');
this.vm = new XChainVM({
gasSchedule: this.config['GAS_SCHEDULE'],
gasCeiling: 1000000,
limits: { ... }
});Per-block compilation caching:
this.actions.vm.beginBlock(); // before processing block transactions
// ... process transactions ...
this.actions.vm.endBlock(); // after processing block transactions| Package | Purpose |
|---|---|
isolated-vm |
V8 isolate sandbox (native C++ module) |
mathjs |
Deterministic bignumber arithmetic |
acorn |
JavaScript parser for AST-based gas injection |
acorn-walk |
AST walker for gas injection and float detection |
astring |
AST-to-source code generator |
Copyright © 2025-2026 Dankest, LLC
Based on XChain Platform by Dankest, LLC – https://dankest.llc
Licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-or-later) with a commercial license available for proprietary use.
You may use, modify, and distribute this material under the terms of the License. See LICENSE and NOTICE for full terms. See the licensing overview.