Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ dist
.tsbuildinfo
tsconfig.vitest-temp.json
tsconfig.build.tsbuildinfo
.zig-cache
.cursorrules
llm_refs
70 changes: 70 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ const trace = await traceState({
```typescript
import { createMemoryClient, http } from "tevm";
import { mainnet } from "tevm/common";

import { traceState } from "@polareth/evmstate";

// Initialize client
Expand Down Expand Up @@ -137,6 +138,7 @@ The `Tracer` class provides an object-oriented interface for reusing client inst
```typescript
import { createMemoryClient, http } from "tevm";
import { mainnet } from "tevm/common";

import { Tracer } from "@polareth/evmstate";

// Initialize client
Expand Down Expand Up @@ -363,6 +365,7 @@ For more control over the environment, you can provide your own Tevm client:
```typescript
import { createMemoryClient, http } from "tevm";
import { mainnet } from "tevm/common";

import { watchState } from "@polareth/evmstate";

// Create custom client with specific configuration
Expand Down Expand Up @@ -412,3 +415,70 @@ The library combines several techniques to provide comprehensive state analysis:
## License

MIT

## Development Setup (Building from Source)

If you want to contribute to `@polareth/evmstate` or build it from source, you'll need to set up your environment to compile both the TypeScript and the Zig code. The Zig code is compiled to WebAssembly (WASM) and called from TypeScript. Communication between TypeScript and Zig is handled using JSON strings (can be improved later with msgpack or flatbuffers).

### Prerequisites

1. **Node.js and pnpm**:

- Node.js (LTS version recommended).
- [pnpm](https://pnpm.io/installation) (This project uses pnpm for package management). You can also use npm or yarn, but pnpm is preferred.

2. **Zig Compiler**:
- Install the Zig compiler (latest stable version recommended). Follow the official installation guide: [Zig Installation](https://ziglang.org/learn/getting-started/)
- Ensure `zig` is available in your system's PATH.

### Build Steps

1. **Clone the Repository**:

```bash
git clone https://github.com/polareth/evmstate.git
cd evmstate
```

2. **Install Dependencies**:

```bash
pnpm install
```

3. **Compile Zig to WASM**:
Compile the Zig source code (in `src/zig/`) into a WebAssembly module:

```bash
pnpm build:zig
```

This will output the WASM file to `dist/wasm/`.

4. **Build the library**:
Compile both the TypeScript and Zig code:
```bash
pnpm build
```
This command bundles the TypeScript code and ensures the Zig WASM module (expected in `dist/wasm/`) can be loaded, into the final distributable files in the `dist/` directory.

### Development Workflow

For active development, you can use watch scripts that automatically rebuild parts of the project when files change:

- **Watch Zig and TypeScript tests**:

```bash
pnpm dev
```

This will start parallel watchers for:

- Zig source files (`.zig` files): Recompiles the WASM module and runs the tests.
- TypeScript tests (`.test.ts` files): Runs the tests in watch mode.

- **Update zabi**:

```bash
zig fetch --save git+https://github.com/Raiden1411/zabi.git#zig_version_0.14.0
```
75 changes: 75 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
// Standard optimization options. This will respect -Doptimize= from the command line.
// We'll use -Doptimize=ReleaseSmall in the package.json script.
const optimize = b.standardOptimizeOption(.{});

// --- Zabi Dependency ---
// Add zabi as a dependency for the build.
// The .{} for the second argument means we are not overriding target or optimize for the dependency itself here.
const zabi = b.dependency("zabi", .{});

// Explicitly define the target for our WASM build
const wasm_target = b.resolveTargetQuery(.{
.cpu_arch = .wasm32,
.os_tag = .freestanding,
});

// Create a module for our WASM library code.
// This points to your actual Zig code.
const wasm_lib_module = b.createModule(.{
.root_source_file = b.path("src/zig/lib.zig"), // Path to your main Zig library file
.target = wasm_target, // Target this module for WASM
.optimize = optimize, // Apply optimization
});

// Create the WebAssembly artifact using addExecutable
const wasm_exe = b.addExecutable(.{
.name = "evmstate", // The output filename will be evmstate.wasm
.root_module = wasm_lib_module, // Use the module we defined
// .target = wasm_target, // Target is implicitly taken from the root_module's target
// .optimize = optimize, // Optimize is implicitly taken from the root_module's optimize
});
wasm_exe.rdynamic = true; // Required for exported functions to be callable from JS
wasm_exe.entry = .disabled; // Crucial: we are building a library, not an application
wasm_exe.root_module.addImport("zabi-abi", zabi.module("zabi-abi"));

// Define the desired output path for the WASM file directly in the dist directory
const wasm_install_path = "dist/wasm/evmstate.wasm";

// Create a step to install the compiled WASM binary to the specified path.
// This handles the "copying" of the file.
const install_wasm_step = b.addInstallFile(wasm_exe.getEmittedBin(), wasm_install_path);

// Define a "wasm" step in the build graph.
// Running `zig build wasm` will execute this step.
const build_wasm_step = b.step("wasm", "Build the WebAssembly artifact and install it");
build_wasm_step.dependOn(&install_wasm_step.step);

// If you want `zig build` (with no arguments from project root)
// to also build and install the WASM by default:
b.default_step.dependOn(build_wasm_step);

// Get the native target for running tests.
// Pass .{} to use the host's native target.
const native_target = b.standardTargetOptions(.{});

// Add a test executable that includes all test blocks from lib.zig.
// Tests should generally run with the native target.
const main_tests = b.addTest(.{
.root_source_file = b.path("src/zig/lib.zig"), // Points to the file containing the tests
.target = native_target, // Compile tests for the native architecture
.optimize = optimize, // Use the same optimization mode as the rest of the build (can be overridden)
// .filter = b.option([]const u8, "test-filter", "Filter for tests to run"), // Optional: allow test filtering
});
// Make zabi available to your test code
// main_tests.root_module.addImport("zabi", zabi_module);

// Create a "test" step that runs the test executable.
const run_main_tests_step = b.addRunArtifact(main_tests);

// The `test` step will execute `run_main_tests_step`
const test_step = b.step("test", "Run unit tests for lib.zig");
test_step.dependOn(&run_main_tests_step.step);
}
52 changes: 52 additions & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
.{
// This is the default name used by packages depending on this one. For
// example, when a user runs `zig fetch --save <url>`, this field is used
// as the key in the `dependencies` table. Although the user can choose a
// different name, most users will stick with this provided value.
//
// It is redundant to include "zig" in this name because it is already
// within the Zig package namespace.
.name = .evmstate,

// This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication.
.version = "0.0.0",

// Together with name, this represents a globally unique package
// identifier. This field is generated by the Zig toolchain when the
// package is first created, and then *never changes*. This allows
// unambiguous detection of one package being an updated version of
// another.
//
// When forking a Zig project, this id should be regenerated (delete the
// field and run `zig build`) if the upstream project is still maintained.
// Otherwise, the fork is *hostile*, attempting to take control over the
// original project's identity. Thus it is recommended to leave the comment
// on the following line intact, so that it shows up in code reviews that
// modify the field.
.fingerprint = 0xa34aadc5b3670a97, // Changing this has security and trust implications.

// Tracks the earliest Zig version that the package considers to be a
// supported use case.
.minimum_zig_version = "0.14.0",

// This field is optional.
// Each dependency must either provide a `url` and `hash`, or a `path`.
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
// Once all dependencies are fetched, `zig build` no longer requires
// internet connectivity.
.dependencies = .{
.zabi = .{
.url = "git+https://github.com/Raiden1411/zabi.git?ref=zig_version_0.14.0#b91004e008f81b34aee13fd78f1a3c8cf597eac3",
.hash = "zabi-0.16.1-UB4phl86SABdBvFceyCgBARQTki8rNko7MLouY-TeGVQ",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
// For example...
//"LICENSE",
//"README.md",
},
}
Loading