Contributing · How-to guide
Change the runtime without breaking its contracts
Start a contribution by identifying the observable behavior that should change and the invariant that must survive. A transaction-path change may affect signatures, ordered execution, shared account state, replay and RPC output at once. Tracing that path before editing is more useful than choosing a module from its name alone.
The maintained Rust crate is runtime/; the production node is runtime/src/bin/svm_vm.rs. The TypeScript SDK and public CLI live under sdk/singularity-web3/ and sdk/singularity-cli/. Use the repository map for the remaining components.
Starting point: a Git checkout you can build, familiarity with the affected language, and a specific observable change. The task is to produce a focused reviewable diff with evidence for the changed behavior; selecting the actual test is part of that task.
Establish a working checkout
A plain clone includes the first-party gevm sources. There are no Git submodules to initialize. Rust 1.93.0, rustfmt and Clippy are pinned in the root toolchain file. Default runtime features include GPU dependencies; CPU development commands need --no-default-features. A CPU build is useful for appropriate code and tests, but does not establish that the GPU execution paths are available.
From the repository root, contributors can enable the version-controlled hooks:
./scripts/setup-hooks.shThis changes the checkout's Git hook configuration. The pre-commit hook formats Rust when necessary and re-stages only paths already staged; it also checks for malformed prose additions to Cargo lockfiles. The commit-message hook invokes the contextual-commit validator. Read its current requirements when preparing a substantive change. Hook setup, pre-commit behavior.
Run Cargo commands from runtime/. Use plain cargo fmt, never cargo fmt --all: the broader traversal rewrites vendored third-party sources under deps/commonware-rohu-paplet/. The imported runtime/deps/gevm/ tree is first-party source. Preserve unrelated changes and inspect the diff after formatting.
Follow the affected contract
| Change | Trace before implementation | Evidence the review needs |
|---|---|---|
| Transaction parsing or signatures | Public wire decoder → signing bytes → admission → replay | Canonical acceptance/rejection vectors and stable identity |
| Native program or VM call | Authority extraction → dispatch → child journal → final writeback | Unauthorized, alias, rollback and successful composition cases |
| Account state or storage | Overlay → commit preparation → WAL/drain → recovery | Ordered writes, deletion handling and interrupted recovery |
| Consensus or block hashing | Proposal encoding → validation → digest → execution | Golden vectors, version impact and coordinated rollout plan |
| HILSA evidence or settlement | Feed/verifier → registry policy → committed snapshot → destination | Trust downgrade, replay and enforcement boundaries |
| SDK, CLI or RPC | Parser/types → transport → server handler → output/error | Executed interface examples against the intended version |
Start with CodeGraph when the checkout is indexed. Follow the important callers and tests, then inspect source where a comment or design note contradicts the implementation. The architecture overview provides the shared mental model; the HILSA hub and Bombil chapter explain their particular boundaries.
Validate the smallest meaningful behavior first
A focused CPU Rust unit test can be run as:
cd runtime
cargo test --no-default-features --lib exact_test_name -- --exact
cargo fmt -- --checkReplace exact_test_name with its complete Rust test path, including module prefixes. A filter that selects zero tests is not proof of behavior. For an integration target, use its filename without .rs:
cargo test --no-default-features --test target_name test_nameThese are command templates, not tests executed for this documentation. Some integration tests need GPU features, external fixtures or explicit ignored-test selection. Use the validation strategy to choose the correct suite. Never modify application behavior simply to make a documentation example pass.
For the TypeScript packages, the manifests define npm run typecheck, npm test and npm run build. Run them from the package being changed after resolving its dependencies. CLI source and registry versions can differ, so a successful check of an installed release does not validate unpublished source. SDK manifest, CLI manifest.
Solidity contracts have their own Forge tests under contracts/evm/. Fuzz targets are local under runtime/fuzz/fuzz_targets/; repository policy does not add fuzz jobs to GitHub Actions.
Preserve the CI execution model
Required CPU CI builds one Nextest archive with the quick Cargo profile, no default features, library tests and integration tests. Consumer shards download and execute that archive. Deterministic simulation seeds reuse it; they do not compile a second simulation graph. Doctests remain with the producer because stable Nextest does not archive them. CPU archive producer.
Do not reintroduce Cargo compilation into consumer shards or narrow filters to hide failures. GPU validation and specialized toolchain/profile checks have distinct needs; preserve equivalent coverage when reorganizing jobs. The conformance path detects tests whose names end in _conformance_; an empty corpus must remain a successful no-op without compiling the release graph. See repository contribution rules.
An execution-path change must also preserve the separation from RPC, indexing and explorer work. The hot-path boundary test rejects unapproved dependencies on those modules; its small explicit allowlist explains the deliberate exceptions. Moving expensive read-side synthesis into block execution changes latency and liveness, even if its output is correct. Boundary guard.
Treat compatibility changes as deployments
Changing a hash preimage, instruction meaning, account codec or cross-VM contract may require a protocol-version change. The digest golden test pins version and values together. Regenerating expected values is justified only after the new behavior has been reviewed; it is not a repair for an unexplained mismatch.
Current protocol-version metadata does not enforce a Simplex block-version gate. Breaking changes therefore need an externally coordinated validator cutover, not merely an on-chain upgrade proposal or a rolling binary replacement. Protocol-version contract, golden-vector checks. Follow deployment and upgrades for the operational consequences.
Make the result reviewable
Describe the concrete trigger and resulting behavior, relevant failure cases, tests actually executed and any unverified environment. Keep source changes, fixtures and documentation aligned. Use the repository's Conventional Commit and contextual-message requirements.
Repository policy uses merge commits and requires stacked changes to be retargeted after their base lands. Remote settings and branch state can change, so verify the actual target and ancestry before considering a stack delivered.
Before requesting review, inspect the final diff, verify that the selected tests ran at least one relevant case, and record any environment you could not exercise. The result should let a reviewer identify the changed behavior, authority, state transition and compatibility consequences without reconstructing your investigation.