npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bosorus/lisa-evm

v0.4.0

Published

TypeScript SDK for the LISA stack VM: program builder, ABI calldata, AST, compiler, encoder, and disassembler for the LISA runtime

Readme

@bosorus/lisa-evm

TypeScript SDK for LISA, an experimental stack-based virtual machine. This package builds LISA programs — a high-level program builder, ABI calldata, an AST, a compiler with symbolic labels, an encoder, and a decoder/disassembler — and emits plain program bytes.

It is a compiler, not an executor: it produces Uint8Array/hex program bytes and needs no deployed runtime, RPC, or chain configuration. Execution semantics are defined by the authoritative runtime, yul/LISA.yul, in the lisa-evm repository. This package mirrors that ISA; when the two disagree, the Yul runtime is right.

The standalone runtime is deployed on Ethereum mainnet at 0x7F6b058AAF78BC690f781F0914499C460Ff342D9; send compiled program bytes as calldata to execute them there.

Install

npm install @bosorus/lisa-evm

ESM only. Works in modern browsers (bundlers included) and Node.js >= 22. The only runtime dependency is @noble/hashes (Keccak-256 for ABI selectors, needed at program-build time).

Build a program

createProgram() writes programs in terms of values and calls instead of a manual stack; the builder resolves DUP/SWAP depths and stack cleanup for you and raises LoweringError rather than emitting broken bytecode:

import { createProgram, encodeCall, SELF } from '@bosorus/lisa-evm';

const p = createProgram();

const balance = p.staticCall(SELF, encodeCall('balanceOf(address)', [SELF])).uint256();
p.if(balance.gt(1000), () => {
  p.call(SELF, encodeCall('execute(uint256)', [balance]));
});

const attempt = p.tryCall(SELF, encodeCall('claim()'));
p.if(
  attempt.success,
  () => p.call(SELF, encodeCall('recordSuccess()')),
  () => p.call(SELF, encodeCall('recordFailure()')),
);

const program = p.compile(); // { bytes: Uint8Array; hex: string; ... }

if takes an optional third argument for the other path. Each branch body is its own scope: values it creates are popped before the two paths join, and touching one after the branch closed raises LoweringError:

p.if(
  balance.gt(1000),
  () => p.call(SELF, encodeCall('execute(uint256)', [balance])),
  () => p.call(SELF, encodeCall('queue(uint256)', [balance])),
);

call() and staticCall() are checked: a failed external call reverts the LISA program. tryCall() and tryStaticCall() continue and return a TryCallResult; its success is a runtime Value that works with p.if, revertUnless, and other value operations. The flag occupies a stack slot until its scope closes. Return data remains tracked per path: a result decoded before the if stays readable in a branch that makes no later call, and after the join only if neither branch called again. A later call does not invalidate an already captured success value. Failed calls may still return revert data; callers must not assume that failed-call returndata is a successful ABI return value.

Pre-encoded calldata drops straight into the same call path, and any aligned 32-byte return-data word is readable:

import { createProgram, rawCalldata, SELF } from '@bosorus/lisa-evm';

const p = createProgram();
// transfer(address,uint256) encoded elsewhere — no ABI parsing here.
p.call(SELF, rawCalldata('0xa9059cbb0000000000000000000000001111111111111111111111111111111111111111000000000000000000000000000000000000000000000000000000000000002a'));

const result = p.staticCall(SELF, rawCalldata('0xc45a815d'));
const secondWord = result.word(32);             // word at byte offset 32

An account's ETH balance needs no call and no contract in between:

import { createProgram, encodeCall, CALLER, SELF } from '@bosorus/lisa-evm';

const p = createProgram();
p.if(p.balance(CALLER).gt(10n ** 18n), () => {
  p.call(SELF, encodeCall('settle()'));
});

The address accepts anything word-shaped — a Value, a hex address, bytes. Like GAS and SELFBALANCE, p.balance(...) is an expression rather than a stack slot: it lowers where the value is used, so a balance checked in a condition costs no slot and each use re-reads the account.

An invariant worth aborting on gets a guard, and hashing bytes that travel with the program has a direct form:

import { createProgram, encodeCall, CALLER, SELF } from '@bosorus/lisa-evm';

const p = createProgram();
p.revertUnless(p.balance(CALLER).gt(10n ** 18n)); // revert instead of continuing
const digest = p.keccak('0xdeadbeef');
p.call(SELF, encodeCall('claim(uint256)', [digest]));

revertUnless emits the same JZ -> REVERT guard the builder emits for a failed call, and costs no slot; LISA's REVERT carries no revert data. keccak is a slot rather than an expression — the data is copied (BLOB) and hashed once per call, so reusing the Value duplicates the digest instead of re-copying and re-hashing, and the slot lives until its scope closes. Hashing data that only exists at runtime needs LISA's memory API, which the builder does not expose yet.

Inspect bytecode

import { compile, decode, disassemble } from '@bosorus/lisa-evm';
import { program, push, add, stop } from '@bosorus/lisa-evm/api';

const programBytes = compile(program(push(2), push(3), add, stop)).bytes;
disassemble(programBytes);   // ["PUSH1 0x2", "PUSH1 0x3", "ADD", "STOP"]
decode(programBytes);        // structured instructions (name, operand, offset, size)

Low-level subpaths

The AST layer the builder lowers into is available through documented subpath imports:

import { program, push, add, stop } from '@bosorus/lisa-evm/api';
import { compile } from '@bosorus/lisa-evm/compile';

compile(program(push(2), push(3), add, stop)).hex; // 0x010201032400

Available subpaths: api (AST constructors), ast (AST types), compile, encode, decode, isa (canonical opcode table), bytes (hex/byte helpers), expr, abi, builder. The root entry is the curated application-facing surface; internal plumbing (e.g. Keccak glue) stays out of the root.

Notes

  • Raw AST nodes go through emitUnsafe(...nodes), the escape hatch around the shadow stack. What it appends must leave every value the builder tracks at the same stack position with the same contents — keeping only the stack depth is not enough — and nothing checks it. The old emit name remains as a deprecated alias.
  • Programs are address-free by design: nothing here bakes in chain or runtime addresses. How program bytes reach a deployed runtime (calldata, an embedding wallet, DELEGATECALL) is an execution concern outside this package.
  • Repository development requirements (Node >= 23.6, Foundry) do not apply to consumers of this package.