
A very fast EVM written in Zig
Guillotine is a new EVM implementation built in Zig by @FUCORY, the creator of Tevm. Itβs designed for:
- πΈοΈ Browser-readiness
- β‘ Extreme speed
- π¦ Minimal bundle size
- π elegance and modularity
Despite its early status, it's already very fast and vrey tiny.
Weβre wrapping up the Alpha release. We will be testing vs all ethereum hardforks and doing extensive benchmarking. Expect benchmarks and bundle size reports within a week. Stay tuned!
π₯ Official benchmarks and bundle size reports will be included with the Alpha drop. You can expect:
- β±οΈ Best in class Performance
- πͺΆ Significant bundle size reduction
Compared to other EVM implementations
Guillotine is a modular Ethereum stack in Zig:
primitives
β Low-level Ethereum utilities (like Alloy or Ethers.js in Zig)compilers
β Zig bindings for the Foundry compiler (Rust)crypto
β π§ͺ Zig-based crypto lib (unaudited)devtool (WIP)
β Native app (Zig + Solid.js) β a future local-first Tenderlyprovider (WIP)
β HTTP-based Ethereum provider
Once stable, Guillotineβs WASM build will replace the current JavaScript EVM in Tevm. Upgrades include:
- π Up to 1000x performance boost
- π 300KB (75%) bundle size reduction
- π§ Foundry-compatible compiler support
- π§± Cross-language bindings (primitives, compiler, provider)
It also unlocks Solidity and Vyper compatibility for the tevm
compiler.
- ποΈ Fast & Small β Zig = uncompromising performance and minimal footprint
- π§© C FFI Compatible β Use it from Python, Rust, Go, Swift, etc.
- π₯οΈ Multi-target builds β Native + WASM (x86 / ARM)
- ποΈ Builder pattern β Intuitive API for managing VM execution
- π§ͺ Reliable β Unit, integration, fuzz, E2E, and benchmark test suite
zig fetch --save git+https://github.com/evmts/Guillotine#main
Add to build.zig.zon
:
.dependencies = .{
.guillotine = .{
.url = "git+https://github.com/evmts/Guillotine#main",
.hash = "<hash from zig fetch>",
},
},
- π οΈ Zig v0.14.1 or later
- π¦ Rust toolchain β for BN254 native precompiles (will be replaced with Zig #1)
const std = @import("std");
const Evm = @import("guillotine").Evm;
const primitives = @import("guillotine").primitives;
pub fn main() !void {
const allocator = std.heap.page_allocator;
var memory_db = Evm.MemoryDatabase.init(allocator);
defer memory_db.deinit();
const db_interface = memory_db.to_database_interface();
var vm = try Evm.Evm.init(allocator, db_interface);
defer vm.deinit();
const bytecode = [_]u8{
0x60, 0x2A, 0x60, 0x00, 0x52,
0x60, 0x20, 0x60, 0x00, 0xF3,
};
const contract_address = primitives.Address.from_u256(0x1234);
var contract = Evm.Contract.init_at_address(
contract_address,
contract_address,
0,
100_000,
&bytecode,
&[_]u8{},
false,
);
defer contract.deinit(allocator, null);
try vm.state.set_code(contract_address, &bytecode);
const result = try vm.interpret(&contract, &[_]u8{});
defer if (result.output) |output| allocator.free(output);
std.debug.print("Execution status: {}\n", .{result.status});
if (result.output) |output| {
std.debug.print("Return value: {}\n", .{
std.mem.readInt(u256, output[0..32], .big)
});
}
}
- π§Ό Zero Allocation Philosophy β Allocates once, avoids reallocations
- π Explicit Error Handling β All errors are typed and recoverable
- π§© Modular Boundaries β Clear interfaces between components
- π§ͺ Test Everything β Coverage across all levels
- π οΈ Optimized for Size & Speed β
comptime
wherever it counts
Address | Name | Native | WASM | Implementation |
---|---|---|---|---|
0x01 |
ECRECOVER | β | β | Pure Zig |
0x02 |
SHA256 | β | β | Pure Zig |
0x03 |
RIPEMD160 | β | β | Pure Zig |
0x04 |
IDENTITY | β | β | Pure Zig |
0x05 |
MODEXP | β | β | Pure Zig |
0x06 |
ECADD (BN254) | β | β | Pure Zig |
0x07 |
ECMUL (BN254) | β | Rust (to be replaced) | |
0x08 |
ECPAIRING (BN254) | β | Rust (partial) | |
0x09 |
BLAKE2F | β | β | Pure Zig |
0x0a |
KZG_POINT_EVALUATION | β | β | C-KZG-4844 |
π§ͺ Crypto implementations live in src/crypto
.
We welcome contributions of all kinds!
See our Contributing Guide to get started.
For external projects that want to integrate Guillotine as a library dependency.
β οΈ Note: The requirements below are temporary and will be removed in upcoming releases as we migrate to pure Zig implementations.
Guillotine currently uses a Rust-based BN254 elliptic curve implementation for production-grade scalar multiplication and pairing operations:
- Location:
src/bn254_wrapper/
- Dependencies: arkworks ecosystem (ark-bn254, ark-ec, ark-ff, ark-serialize)
- Build: Static library built from Rust using cargo
- Status: Will be replaced with pure Zig implementation
KZG commitment library for EIP-4844 blob transactions:
- Dependency: ethereum/c-kzg-4844
- Purpose: KZG point evaluation precompile (0x0a)
- Status: May be replaced with pure Zig implementation
Linux:
dl, pthread, m, rt
macOS:
Security framework, CoreFoundation framework
These system library requirements will be eliminated when Rust dependencies are removed.
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Add Guillotine as dependency
const guillotine = b.dependency("guillotine", .{
.target = target,
.optimize = optimize,
});
// Get Guillotine modules
const evm_mod = guillotine.module("evm");
const primitives_mod = guillotine.module("primitives");
// Your executable
const exe = b.addExecutable(.{
.name = "your-app",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// Link Guillotine modules
exe.root_module.addImport("evm", evm_mod);
exe.root_module.addImport("primitives", primitives_mod);
// Link required libraries (automatically handled by Guillotine modules)
// The bn254_wrapper and c-kzg-4844 libraries are linked automatically
// when you import the evm module
b.installArtifact(exe);
}
.{
.name = "your-project",
.version = "0.1.0",
.dependencies = .{
.guillotine = .{
.url = "https://github.com/evmts/Guillotine/archive/<commit-hash>.tar.gz",
.hash = "<hash>",
},
},
.paths = .{""},
}
For projects that only need specific Guillotine functionality:
// Use only primitives (Address, Hex, RLP, etc.)
const primitives = b.dependency("guillotine", .{}).module("primitives");
exe.root_module.addImport("primitives", primitives);
// Use only EVM execution (includes all dependencies)
const evm = b.dependency("guillotine", .{}).module("evm");
exe.root_module.addImport("evm", evm);
-
Automatic Linking: When you import the
evm
module, all required libraries (BN254 wrapper, c-kzg-4844, system libraries) are automatically linked. -
Cross-Platform: The build system automatically detects the target platform and links appropriate system libraries (Security/CoreFoundation on macOS, dl/pthread/m/rt on Linux).
-
WASM Compatibility: For WASM targets, BN254 operations use placeholder implementations. Full zkSNARK support requires host environment integration.
-
Memory Management: All Guillotine operations require an allocator. Use
std.testing.allocator
for tests or your application's allocator for production. -
Future Simplification: Integration will become much simpler once pure Zig implementations replace the current Rust dependencies, eliminating the need for Rust toolchain and system library requirements.
- Signal 4 (Illegal Instruction): Ensure all system libraries are properly linked. This typically occurs when BN254 operations fail due to missing dependencies. Note: This issue will be resolved when pure Zig implementations are complete.
- Build Failures: Verify Zig version compatibility (0.14.1+ required) and ensure Rust toolchain is available for BN254 wrapper compilation. Note: Rust toolchain requirement will be removed in future releases.
- Import Errors: Use the module system rather than direct file imports. Import
evm
andprimitives
modules as shown above.
MIT License. Free for all use. π
- ποΈ Ethereum Foundation and OP RPGF β for funding support
- βοΈ Zig Community β for an incredible systems programming language
- π§ @SamBacha β for the name Guillotine
- π¬ Tevm Telegram β for community feedback and direction