VM

Our blockchain runs on hardened JavaScript — and we're never going back

Entry #02 · 2026-01-18 · Devlog

Our blockchain runs on hardened JavaScript — and we're never going back

Smart contracts, for the last decade, have been written in a stack machine bytecode language called Solidity. Or a stack machine bytecode language called Vyper. Or a stack machine bytecode language called Move. Or Rust-compiled-to-WASM. You get the point.

I wrote AsentumChain's contract VM in about three days and the contract language is... JavaScript. Real JavaScript. Not a subset. Not a DSL pretending to be JavaScript. Actual, syntactically-valid, every-feature-you-know JavaScript. And it runs in a deterministic sandbox that makes reentrancy — the class of bug that ate The DAO and keeps eating DeFi protocols — structurally impossible.

I want to tell you why this is kind of a big deal, because I think most people who would care about a chain like this don't realize the option exists.

the SES revelation

There's this project called SES — Secure ECMAScript. It's the thing the Agoric folks built to make JavaScript safe for running untrusted code in production. I knew about SES vaguely before starting this chain. I thought it was cool. I did not realize it was THIS cool.

Here's what SES does, in one sentence: it freezes all the JavaScript primordials (Object.prototype, Array.prototype, all that), strips every source of non-determinism from the runtime (Date.now, Math.random, setTimeout, fetch, anything network or clock related), and gives you a Compartment class for evaluating code in complete isolation from everything outside it.

That means you can take a literal JavaScript source file, load it into a compartment, and trust that:

  • Two machines running the same VM version will produce byte-identical output
  • The contract cannot reach the network, the filesystem, or any ambient authority
  • It cannot mutate your outer-process globals
  • It cannot learn the wall-clock time unless you explicitly hand it a clock

This is what you actually want from a smart contract VM. And it exists. For free. You just import 'ses'.

import 'ses';
lockdown();

const compartment = new Compartment({
  storage, msg, chain, emit, assert,
});
const exports = compartment.evaluate(contractSource);

That's basically the whole VM. I added gas metering on top (charge per storage read, per storage write, per event emission, per base call), a small storage adapter that buffers writes in memory during execution and only flushes on success, and a thin contract-execution wrapper that routes method calls into the compartment. The whole thing is about 400 lines of TypeScript including the error types and the gas meter.

Agoric did the hard work years ago. I'm just standing on their shoulders.

writing a token contract felt illegal

Here's basically the AsentumChain ERC-20:

const BALANCE_PREFIX = 'b:';

({
  init(args) {
    storage.set('supply', String(args.totalSupply));
    storage.set(BALANCE_PREFIX + args.owner, String(args.totalSupply));
    emit('Transfer', { from: null, to: args.owner, value: args.totalSupply });
  },

  balanceOf(address) {
    return storage.get(BALANCE_PREFIX + address) || '0';
  },

  transfer(to, amount) {
    const from = msg.sender;
    const amt = BigInt(amount);
    assert(amt > 0n, 'amount must be positive');

    const fromBal = BigInt(storage.get(BALANCE_PREFIX + from) || '0');
    assert(fromBal >= amt, 'insufficient balance');

    const toBal = BigInt(storage.get(BALANCE_PREFIX + to) || '0');

    storage.set(BALANCE_PREFIX + from, String(fromBal - amt));
    storage.set(BALANCE_PREFIX + to, String(toBal + amt));

    emit('Transfer', { from, to, value: String(amt) });
    return true;
  },
});

Look at that. No pragma. No contract Token is ERC20. No learning a new language. No weird implicit storage slots. No view / pure / external / public visibility modifiers to remember. No proxy contract pattern to write (yet). Just — functions. That run in a sandbox. That move tokens.

If you've ever written Solidity, you know you spend a nontrivial chunk of your time fighting the language itself. Storage layout gotchas. Checks-effects-interactions discipline. Reentrancy guards. Function visibility modifiers. I deployed my first token contract on AsentumChain and wrote the whole thing in maybe 15 minutes, most of which was deciding what to name the methods.

The moment the first balanceOf query came back as "1000000" for a contract I had written as a plain .js file on my desktop and shipped through the CLI — I'm not going to lie, I closed the terminal, went for a walk, and came back kind of emotional about it.

the reentrancy thing is low-key the best part

One of the decisions I made early is that inter-contract calls are asynchronous. Agoric-style. await E(otherContract).method(args). Inside a single contract, it's normal synchronous JavaScript — you can call helper functions, loop, do whatever. But when a contract calls OUT to another contract, it gets a promise back, and its current execution is paused until the callee has fully finished.

Why does this matter? Because reentrancy — the thing that drained The DAO, the thing every DeFi auditor spends hours hunting for in every protocol, the thing you have to write checks-effects-interactions discipline to avoid forever — is structurally impossible under this model. You cannot re-enter a contract while it's in the middle of its own state update. The callee runs to completion, as a separate invocation, with already-committed state, before control ever returns to the caller.

Solidity's synchronous model means you have to write that discipline on every single state-changing function forever. Get it wrong once, anywhere, and someone drains you. Async-by-default means the class of bug just. does. not. exist.

I've been mentioning this to a few people and the reaction is always the same: "wait, you can just make it impossible?" Yeah. You can just make it impossible. Languages are a choice. Most of the pain people associate with "writing smart contracts" is baked into the specific stack machine languages that got adopted in 2015 and has nothing to do with the fundamental difficulty of the problem.

the honest bit

Is JavaScript-as-contracts a silver bullet? Not quite. The determinism requires pinning Node/V8 versions across all validators — SES is strict but not bulletproof against engine drift. Per-opcode gas metering is still a TODO on my list — right now I charge per op-class (storage read, write, event emit) which is fine for DoS protection but under-prices pure-compute loops. The contract source is public on-chain by default, because verification-by-hash is the whole trust story, so if you want your contract's logic obscured, this chain is not for you.

But here's the thing. Every single one of those tradeoffs is a conscious call I'm making with my eyes open, for reasons I can articulate out loud. That is a completely different feeling from "I'm writing Solidity because that's what blockchains use, I guess."

I'm building this chain for two kinds of people:

  1. JavaScript devs who've been watching the crypto space from the outside for ten years going "this should be easier," and who were never going to learn Solidity to prove the point
  2. Anyone who'd like their contracts to not be quietly swapped out by a multisig three months after the audit that made them feel safe

If you're either of those people — or even if you just think this is interesting — I want to hear what you'd build with it.

— milkie

Don't miss the next entry.

Join the launch list and we'll send you a note whenever there's a new devlog entry, a research drop, or a real milestone.