Thank you for your interest in contributing to Voltaire (Ethereum primitives and cryptography library)!
We welcome contributions! This document provides guidelines and instructions for contributing to the project.
We welcome and encourage AI-assisted contributions. Using Claude, Copilot, ChatGPT, or other AI tools is perfectly fine - we care about quality, not how the code was written.
PRs without prompts will be rejected - no exceptions - unless:
- The change is trivial (typo fix, single-line change)
- You are a known trusted contributor
- Include ALL prompts used to generate the code in your PR description
- Provide a human-written summary explaining the "why"
- Review and test all AI-generated code before submitting
Example PR description:
## AI Prompts Used
### Prompt 1:
"Add validation for RLP encoded data to prevent buffer overflows"
### Prompt 2:
"Write tests for the new validation logic covering edge cases"
## Summary
Adds input validation for RLP decoding. Fixes #123.
## Testing
- ✅ zig build test passes
- ✅ Added new test cases for edge conditions
If your contribution is large, please open a discussion to chat about the change before doing the work.
- ❌ NO broken builds or failing tests
- ❌ NO stub implementations (
error.NotImplemented) - ❌ NO commented-out code (use Git for history)
- ❌ NO test failures - all tests must pass
- ❌ NO swallowing errors with empty catch blocks
- ❌ NO
std.debug.printin library code (this is a library, not an app) - ❌ NO
std.debug.assert(use proper error handling)
If in doubt, STOP and ask for guidance rather than stubbing or commenting out code.
- Read
CLAUDE.md- Critical development guidelines - Understand the architecture - Read
README.mdand seedocs/ - Check existing issues - Your change may already be discussed
- Run tests - Ensure everything works before making changes
Install the following dependencies:
- Zig 0.15.1 - https://ziglang.org/download/
- Rust (latest stable) - https://rustup.rs/
- Node.js (LTS) - https://nodejs.org/
- Bun (latest) - https://bun.sh/
git clone <repository-url>
cd voltaire
git submodule update --init --recursive
bun install
zig build
zig build test- Follow the coding standards in CLAUDE.md
- Write tests for all new functionality
- Ensure memory safety (explicit ownership, defer/errDefer)
- Use meaningful variable names
- Add inline documentation for complex logic
# Run all Zig tests
zig build test
# Run specific test filter
zig build -Dtest-filter=address
# Run all TypeScript tests (from repo root)
bun run test
# Run TypeScript tests with coverage
bun run test:coverage
# Optional umbrella step (if configured)
zig build test-all
# Quick validation
zig build check # Format + lint + typecheckCRITICAL: ALL tests must pass before submitting (both Zig and TypeScript if applicable).
- Write a clear PR description
- Reference related issues
- Disclose AI usage if applicable
- Ensure CI passes
// ✅ CORRECT - Explicit cleanup
const thing = try allocator.create(Thing);
defer allocator.destroy(thing);
// ❌ WRONG - Memory leak
const thing = try allocator.create(Thing);
// Missing defer!// ✅ CORRECT - Explicit error propagation
pub fn doSomething() !void {
const result = try riskyOperation();
if (result == null) return error.InvalidResult;
}
// ❌ WRONG - Swallowing errors
pub fn doSomething() void {
const result = riskyOperation() catch return; // NEVER DO THIS
}// ✅ CORRECT - Self-contained test
test "uint256 addition overflow" {
const a = primitives.uint256.max();
const b = primitives.uint256.fromInt(1);
const result = a.addWithOverflow(b);
try std.testing.expect(result.overflow);
}
// ❌ WRONG - No test for new functionality
pub fn newFeature() void {
// ... implementation without tests
}Extra scrutiny required for crypto changes:
- Constant-time operations - Prevent timing attacks
- Input validation - Check all inputs before processing
- Test vectors - Use known test vectors from specifications
- Reference implementations - Cross-validate against known-good implementations
- Security review - Tag maintainers for review
Example:
// ✅ CORRECT - Constant time comparison
pub fn constantTimeCompare(a: []const u8, b: []const u8) bool {
if (a.len != b.len) return false;
var result: u8 = 0;
for (a, b) |byte_a, byte_b| {
result |= byte_a ^ byte_b;
}
return result == 0;
}
// ❌ WRONG - Leaks timing information
pub fn timingUnsafeCompare(a: []const u8, b: []const u8) bool {
for (a, b) |byte_a, byte_b| {
if (byte_a != byte_b) return false; // Early exit leaks info!
}
return true;
}This library provides TypeScript bindings for JavaScript environments. When contributing TypeScript code:
The TypeScript layer has two types of implementations:
-
Pure TypeScript (
src/primitives/,ts/src/primitives/)- No native dependencies
- Works in any JavaScript environment
- Examples: ABI encoding, numeric conversions, bytecode analysis
-
FFI Wrappers (
src/crypto/)- Uses Bun FFI to call native Zig library
- Requires compiled native library
- Examples: Keccak-256, EIP-191
# Build Zig library first (required for FFI modules)
zig build
# Install TypeScript dependencies
cd src
bun install
# Run TypeScript tests
bun test
# Run specific test file
bun test crypto/keccak.test.tsTo add a new FFI wrapper for a Zig crypto function:
-
Add C API binding in
src/c_api.zig:export fn primitives_my_function(input: [*]const u8, len: usize, output: [*]u8) callconv(.C) void { const data = input[0..len]; const result = crypto.myFunction(data); @memcpy(output, &result); }
-
Create TypeScript wrapper in
src/crypto/my-function.ts:import { getCApiPath } from './utils'; import { dlopen, FFIType } from 'bun:ffi'; const lib = dlopen(getCApiPath(), { primitives_my_function: { args: [FFIType.ptr, FFIType.u64, FFIType.ptr], returns: FFIType.void } }); export function myFunction(data: Uint8Array): Uint8Array { const output = new Uint8Array(32); lib.symbols.primitives_my_function(data, data.length, output); return output; }
-
Add tests in
src/crypto/my-function.test.tsusing known test vectors -
Update documentation in TYPESCRIPT_API.md and README.md
- Use TypeScript strict mode (enabled in tsconfig.json)
- Prefer
Uint8Arrayfor byte data - Use
bigintfor large integers - Write comprehensive tests with known test vectors
- Document all public APIs with JSDoc comments
- Handle errors gracefully (throw descriptive errors)
// ✅ CORRECT - Use known test vectors
import { describe, test, expect } from 'bun:test';
describe('keccak256', () => {
test('matches NIST test vector', () => {
const input = 'abc';
const expected = new Uint8Array([/* known hash */]);
const result = keccak256(input);
expect(result).toEqual(expected);
});
});
// ❌ WRONG - No test vectors
test('keccak256 works', () => {
const result = keccak256('hello');
expect(result.length).toBe(32); // Too weak!
});zig build # Full build (Zig + TS typecheck + C libs)
zig build build-ts-native # Native FFI (.dylib/.so) - ReleaseFast
zig build build-ts-wasm # WASM - ReleaseSmall (size-optimized)
zig build build-ts-full # Complete TypeScript build (TS + native + WASM)
zig build crypto-wasm # Individual crypto WASM modules (tree-shaking)zig build test # All Zig tests
zig build test-ts # All TypeScript tests
zig build test-all # All tests (Zig + TypeScript + Go)
zig build test-integration # Integration tests
zig build test-security # Security testszig build format # Format Zig + TypeScript
zig build format-check # Check formatting
zig build lint # Lint TypeScript (auto-fix)
zig build lint-check # Check linting
zig build check # Quick validation (format + lint + typecheck)
zig build ci # Complete CI pipelinezig build bench # zbench Zig benchmarks
bun run bench # TS comparison benchmarks + generate BENCHMARKING.mdzig build example-keccak256
zig build example-abi
zig build example-secp256k1
zig build example-address
zig build example-eip712
zig build example-transactionbun run docs:dev # Mintlify dev (localhost:3000)zig build clean # Clean artifacts (keep node_modules)
zig build clean-all # Deep clean + node_modulesFuzz tests use Zig's built-in fuzzer (requires Linux). On macOS, use Docker.
# Run fuzzing for 5 minutes
docker run --rm -it -v $(pwd):/workspace -w /workspace \
ziglang/zig:0.15.1 \
zig build test --fuzz=300s
# With web UI
docker run --rm -it -v $(pwd):/workspace -w /workspace \
-p 6971:6971 \
ziglang/zig:0.15.1 \
zig build test --fuzz --port=6971Visit http://localhost:6971 for live coverage visualization.
zig build test --fuzz=300s # Run for 5 minutes
zig build test --fuzz # Run indefinitelyFuzz tests are colocated with implementations using .fuzz.zig extension:
src/primitives/Address/address.fuzz.zig
src/crypto/hash.fuzz.zig
src/precompiles/ecrecover.fuzz.zig
See .claude/commands/fuzz.md for comprehensive fuzzing documentation.
Voltaire includes an Advent-of-Code style evaluation suite that tests Claude's ability to use the Voltaire MCP server for solving Ethereum challenges.
# Set up RPC endpoint
export ETHEREUM_RPC_URL="https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"
# Run all MCP evaluation tests
bun run test:mcp
# Run specific test
bun run test:mcp -t "CryptoPunk"- Find block hash where first CryptoPunk minted
- Calculate total ETH burned via EIP-1559 in block range
- Verify secp256k1 signatures using Voltaire primitives
- Query NFT ownership at specific blocks
See tests/mcp-evals/README.md for detailed documentation.
We especially welcome contributions in:
- Test Coverage - More comprehensive unit tests (both Zig and TypeScript)
- Fuzz Testing - Additional fuzz test coverage
- Documentation - Improving code documentation
- Performance - Optimization opportunities (with benchmarks)
- Bug Fixes - Addressing any issues
- Platform Support - Testing on different platforms
- TypeScript FFI Bindings - Adding C API wrappers for remaining crypto functions (EIP-712, secp256k1, hash algorithms)
- TypeScript Primitives - Pure TypeScript implementations of additional Ethereum primitives
- Open a discussion for architecture questions
- Comment on relevant issues for feature discussions
- Tag maintainers for security-sensitive changes
- Automated checks - CI must pass
- Manual review - Maintainer review required
- Testing verification - Confirm tests are comprehensive
- Security review - Extra scrutiny for crypto code
- Merge - Squash and merge with clear commit message
By contributing, you agree that your contributions will be licensed under the same license as the project.
Thank you for helping make Voltaire more robust and secure! 🙏