Thanks for your interest in contributing to Lodestar. It's people like you that push the Ethereum ecosystem forward.
If you're reporting a bug or have a feature request, create a new issue.
Important
Please note that trivial, non-code contributions such as spelling, grammar, typos, corrections, comments and link fixes are not acceptable pull requests. Although we appreciate the effort to fix these valid concerns, it is not practical for us to run our CI systems to accommodate minor external contributions which generate minimal value for the purpose of contribution/airdrop farming. It would be appreciated for you to open up an issue instead for our team to aggregate these types of contributions into a batch commit.
If you wish to contribute code:
- Make sure you're familiar with our contribution guidelines (this document)!
- Before starting on any code, make sure to leave a comment stating your intention in the issue you are interested in or on our Discord. We would prefer to have some form of human-to-human interaction before you contribute any code, especially since AI usage is commonplace today.
- Create your own fork and make the necessary changes. Test your changes locally first. See Developer Usage below.
- Make an open pull request when you're ready for it to be reviewed. See Pull request etiquette for more information.
Important
The Lodestar team uses AI heavily in our work, but we have strict rules for AI contributions.
-
All AI usage in any form must be disclosed. You must state the tool you used (e.g. Claude Code, Cursor, Amp) along with the extent that the work was AI-assisted.
-
PR descriptions, code changes, issues and discussions can use AI assistance but must have a full human-in-the-loop. This means that any content generated with AI must have been reviewed and edited by a human before submission. AI is very good at being overly verbose that distracts from the main point. Humans must do their research and trim this down. Our team appreciates and takes all contributions seriously, so AI output, if left unedited, is disrespectful of our time and effort.
-
The human-in-the-loop must fully understand all code. If you can't explain what your changes do and how they interact with the greater system, do not contribute to this project.
-
Bad AI-generated PRs/issues will be de-prioritized or closed. Bad contributions that are clearly AI (slop) will be de-prioritized or closed without warning. We love to help developers learn about Lodestar/Ethereum and grow. If you're interested in that then use AI responsibly.
Please remember that Lodestar is maintained by a small team of humans. Every discussion, issue and pull request is read and reviewed by the team. It is rude and disrespectful to attempt contributions with low-effort work, since it puts the burden of validation on the maintainer.
We currently host all zig packages and napi bindings in this repository as a monorepo. See src/ for a list of packages and bindings/ for a list of napi bindings hosted in this repository.
We follow a modified version of TIGERSTYLE loosely.
Before opening a PR, please make sure all tests pass.
To do that, download the spec tests and era files used in testing:
# Download vectors pinned by build.zig.zon
zig build run:download_spec_tests
# Download era files
zig build run:download_era_files
# Generate test sources
zig build run:write_spec_tests
zig build run:write_ssz_generic_spec_tests
zig build run:write_ssz_static_spec_tests
zig build run:write_bls_spec_testsTests live beside the code they cover, and a module holds at most one test block. A single
inline test works as a usage example. As soon as there is a second, the tests are a suite and move
into a sibling <module>_test.zig, wired back from the module itself:
// src/clock/slot_math.zig
// ... implementation ...
test {
_ = @import("slot_math_test.zig");
}// src/clock/slot_math_test.zig
const std = @import("std");
const slot_math = @import("slot_math.zig");
const slotAtMs = slot_math.slotAtMs;
test "slotAtMs returns genesis slot at genesis" {
// ...
}A few things to keep in mind:
- Name the test file after its module in snake_case.
Node.zigpairs withnode_test.zig. - Put the
test { _ = @import(...); }block in the module under test rather than in the packageroot.zig, so moving or deleting a module carries its tests along with it. - Do not mark a declaration
pubjust so a test can reach it from the sibling file. Tests that deliberately exercise private internals belong inline;src/cpu_count.zigandsrc/state_transition/load_state.zigare examples.
If you created new unit tests, you can run them individually.
For example, if you made a new unit test in the ssz package:
zig build test:ssz -Dtest:ssz.filters="my full test name"If you made changes that affect spec relevant behavior, run:
zig build test:spec_tests -Dpreset={mainnet,minimal}And run all other tests:
zig build testAnd format all files:
zig fmt .tidy is a custom linter for repo-wide rules that a formatter cannot express, currently covering
test layout and test discovery. CI runs it, and so should you:
zig build test:tidyIt takes its file list from git ls-files, so run it after git add when you add new files.
If you made a change to the bindings, make sure the bindings tests pass:
pnpm install
# Build bindings
zig build build-lib:bindings
# Build for a specific preset through package scripts
pnpm prepare-mainnet
pnpm prepare-minimal
# Run binding tests
pnpm test
# Run Biome
pnpm lint
pnpm exec biome check --write .Branch Naming
If you are contributing from this repository prefix the branch name with your GitHub username (i.e. myusername/short-description).
Pull Request Naming
Pull request titles must be:
- Adhering to the conventional commits spec
- Short and descriptive summary
- Written in imperative present tense
- Not end with a period
For example:
- refactor(bindings): use owned typed arrays for BLS outputs
- chore: remove merge transition code
Pull Request Etiquette
- Pull requests should remain as drafts when they are not ready for review by maintainers. Open pull requests signal to the maintainers that it's ready for review.
- If your pull request is no longer applicable or validated to fix an issue, close your pull request.
- If your pull request is fixable and needs additional changes or commits within a short period of time, switch your pull request into a draft until it's ready.
- Otherwise, close your pull request and create a new issue instead.
To maintain code quality, improve collaboration, and ensure clarity in large or complex changes, we follow these guidelines when opening pull requests (PRs). Depending on the nature of the change, PRs fall into three categories:
If the PR contains a self-contained and complete feature or bug fix that does not require major refactoring or cross-team discussions, then:
- Fill in the PR template. Motivation is never empty: state the problem at whatever length it needs, quote the error or log, and link the issue or discussion. Description is one sentence saying what the PR does, with further detail appropriate to the change.
- Keep simple changes brief. Use bullets, tables, or short subsections when they clarify before/after behavior, tradeoffs, or validation. Include the context needed to review the change in the PR description, and link CI instead of repeating routine output.
- If the PR modifies critical code paths, add references to relevant issues, benchmarks, or related discussions.
- Ensure the PR adheres to our standard PR etiquette and commit message guidelines.
If the PR involves significant code refactoring, structural changes, or fundamental modifications where team input is needed:
- Create a GitHub issue or Discord Thread before writing code.
- Outline the problem, your proposed approach, and any alternative solutions.
- Request feedback and build consensus with the team.
- Summarize the outcome and link the issue in the PR description once consensus is reached.
- If changes affect multiple packages or require coordination with ongoing development, summarize any key decisions from the issue in the PR description.
If the PR introduces large-scale changes, affecting multiple areas of the codebase or requiring step-by-step integration:
- Document the feature first before opening any PR.
- Open a GitHub Discussion with a detailed technical proposal explaining the feature. This should include details like:
- Big picture explanation of how and why the feature will help or will change the codebase
- Rough outline of the code that will be implemented
- If a functional implementation is used, a brief description of what each function will do, possibly with a basic function signature if it is clear what will be needed
- Broad overview of how data will flow and integrate with the surrounding sub-systems
- Rough discussion of potential performance (CPU and memory) implications
- Share the document with the team and gather feedback before implementation.
- Create a feature branch to showcase the entire implementation. The idea will be to get this branch deployed on a feature group to test. Throughout the process, metrics will be analyzed to ensure there are no regressions.
- First merge any refactor work necessary to get
mainprepared for the feature. - Create a second empty feature branch from
mainafter the refactor work is merged. - Break down the implementation into smaller, manageable PRs that merge into the empty feature branch.
- Each PR should focus on a specific part of the feature.
This middle part of the review is focused on API, implementation overview and other high-level pieces but will be relatively limited as the API discussion,
analysis of the feature branch and full review on merge to
mainare the important steps. - Link the design document in each PR description so reviewers can always refer to the full scope.
- Merge the smaller PRs into the feature branch until the complete feature is ready for a final merge into the
mainbranch. This is the "formal review process" where several team members will likely get involved. Up to this point it's mostly peer review. The merge tomainis where details like naming, function signature, type definitions, etc will be scrutinized. This is also where metrics from the initial implementation branch will get a detailed, final analysis.