Wormhole is an open-source project licensed under the permissive Apache 2 license. Contributions are greatly appreciated and will be reviewed swiftly.
Wormhole is a mission-critical, high-stakes project. We optimize for quality over quantity. Design processes and code reviews are our most important tools to accomplish that.
Due to the high-stakes nature of making changes to Wormhole, we have a strict policy on who can contribute to the project. Typically, we only accept contributions from people who we already trust and/or who have a history of contributing to the project. This policy is in place to ensure that we maintain the security and stability of project, including its CI infrastructure.
The right way to get started with contributing is to reach out to us directly, introduce yourself, and ask for guidance on how to contribute to the project. Drive-by pull requests will be closed without review or explanation. Typically, we already have a good visibility into existing bugs and feature requests, and our preference to to have our own developers and their LLMs implement the changes.
In general, we will not upstream changes to the project unless we have a tangible need for them.
Security-related issues must be reported via our bug bounty program. See SECURITY.md for more information. We do not accept security-related PRs from external contributors, including from bug bounty reporters, regardless of whether or not the reporter has found a valid issue.
The above policy applies to all projects in the Wormhole ecosystem maintained by the Wormhole Foundation.
Before contributing code, read SAFETY_CRITICAL_CODE.md.
-
All new features must first be discussed in a GitHub issue before starting to implement them. For complex features, it can be useful to open a thread on GitHub Discussions.
-
Development happens on a long-lived development branch (
main). Every change going into a development branch is reviewed individually (see below). Release branches may be used to support in-the-wild releases of Wormhole. We aim to support at most two release branches at the same time. Changes can be cherry-picked from the development branch to release branches, but never from release branches to a development branch. -
Releases are first tested on a testnet.
-
Commits should be small and have a meaningful commit message. One commit should, roughly, be "one idea" and be as atomic as possible. A feature can consist of many such commits.
-
Where possible, use standard libraries, common dependencies, or SDK functions instead of re-implementing validation from scratch. This helps prevent duplication and drift for routine tasks, and helps the codebase stay lean and well-tested.
-
Feature flags and interface evolution are better than breaking changes and long-lived feature branches.
-
We optimize for reading, not for writing - over its lifetime, code is read much more often than written. Small commits, meaningful commit messages and useful comments make it easier to review code and improve the quality of code review as well as review turnaround times. It's much easier to spot mistakes in small, well-defined changes.
-
PRs that only correct typos or make minor wording adjustments will be rejected. Fixing typos alongside other non-trivial engineering work is welcome.
-
Pull requests that modify dependencies must be well-documented so that the benefits of updating can be weighed against security and compatibility concerns. Low-effort PRs that update dependencies without any documentation will be rejected.
Official documentation for the in-the-wild deployments lives on the Wormhole documentation website.
See DEVELOP.md for more information on how to run the development environment.
- Pin as much as possible: Versions, hashes, and dependencies
- Minimize attack surface: Fewer dependencies = fewer risks
- Verify integrity: Use lockfiles, dependency cooldowns, and checksums
Do not change the dependencies of the package.json by hand!
Instead:
- When initially installing OR pulling what has been changed:
npm ci. If you do not do this, you may not get exactly what is specified in the file, inadvertently update dependencies, or even pull exploits down to your machine! Never usenpm installfor this use case. - When needing to add or update a package:
npm i <package>@<version>. If you do not do this, you may inadvertently update other packages or fail to update the lock file. - When needing to remove a package:
npm r <package>. If you do not do this, you may inadvertently update other packages or fail to update the lock file.
Always commit your package-lock.json.
Using specific versions improves security because package versions cannot be overwritten after they are released.
- Copy in package.json and the lock file
- Then run
npm ci
# NOTE: Dockerfile must be pinned too
FROM node:18.19.0-alpine@sha256:12345...
WORKDIR /app
# Include package files
COPY package.json package-lock.json ./
# Use npm ci so that packages are not upgraded
RUN npm ci
...- Use
npm i <package>@<version>
# NOTE: Dockerfile must be pinned too
FROM node:18.19.0-alpine@sha256:12345...
# Pin global packages to specific versions
RUN npm install -g somepackage@1.2.3
...
Updating packages should not be done alongside other work. Instead, take the time to review dependency upgrades carefully, making a best effort to ensure that they are necessary and secure.
The answer is... maybe? The following things are needed in order to fully support a chain in Wormhole:
-
The Wormhole mainnet is governed by a DAO. Wormhole's design is symmetric - every guardian node needs to run a node or light client for every chain supported by Wormhole. This adds up, and the barrier to support new chains is pretty high. Your proposal should clearly outline the value proposition of supporting the new chain. Convincing the DAO to run nodes for your chain is the first step in supporting a new chain.
-
The chain needs to support smart contracts capable of verifying 19 individual secp256k1 signatures.
-
The smart contract needs to be built and audited. In some cases, existing contracts can be used, like with EVM-compatible chains.
-
Support for observing the chain needs to be added to guardiand.
-
Web wallet integration needs to be built to actually interact with Wormhole.
The hard parts are (1) convincing the DAO to run the nodes, and (2) convincing the core development team to either build the integration, or work with an external team to build it.
Please do not open a GitHub issue about new networks - this repository is only a reference implementation for Wormhole, just like go-ethereum is a reference implementation for Ethereum. Instead, reach out to the Wormhole Network.
Probably :-). At its core, Wormhole is a generic attestation mechanism and is not tied to any particular kind of communication (like transfers). It is likely that you can use the existing Wormhole contracts to build your own features on top of, without requiring any changes in Wormhole itself.
Please open a GitHub issue outlining your use case, and we can help you build it!
Run ./scripts/lint.sh -d format and ./scripts/lint.sh lint.
When making commits on Wormhole, it's advised to prefix the commit with the component name.
Example Component Names (generally the root folder of the change in the Wormhole repo):
- node
- ethereum
- sdk
- solana
Example Full Commit Text:
- sdk/js-proto*: 0.0.4 version bump
- node: docs for running a spy against mainnet
- node: Fix formatting with go 1.19
Example Full Commits:
- https://github.com/wormhole-foundation/wormhole/commit/5cc2c071572daab876db2fd82e9d16dc4c34aa11
- https://github.com/wormhole-foundation/wormhole/commit/eeb1682fba9530a8cd8755b53639ba3daefeda36
Resources for writing good commit messages:
- https://www.freecodecamp.org/news/how-to-write-better-git-commit-messages/
- https://cbea.ms/git-commit/
- https://reflectoring.io/meaningful-commit-messages/
Make use of tools like Go's doclinks and Rust's intra-doc links to cross-correlate comments, especially when a field or structure is used 'at a distance' and modifying it could cause side-effects for other parts of the codebase.
Comments should be full sentences in the present tense.
Avoid referring to specific line numbers that may become stale. Avoid phrasing comments as changelogs e.g. "The function now returns an error instead of a string".
Go code should follow the Go Doc Comments standard.
TypeScript code should follow the TSDoc standard.
You must format your code with goimports before submitting.
You can install it with go install golang.org/x/tools/cmd/goimports@latest and run it with goimports -d ./.
You can enable goimports in VSCode with the following in your settings.json.
"go.useLanguageServer": true,
"go.formatTool": "goimports",
"[go]": {
"editor.defaultFormatter": "golang.go",
"editor.formatOnSaveMode": "file",
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.organizeImports": true
}
},We believe automated tests ensure the integrity of all Wormhole components. Anyone can verify or extend them transparently and they fit nicely with our software development lifecycle. This ensures Wormhole components operate as expected in both expected and failure cases.
Places to find out more about existing test coverage and how to run those tests:
- Guardian Node
- Tests:
./node/**/*_test.go - Run:
cd node && make test
- Tests:
- Ethereum Smart Contracts
- Tests:
./ethereum/test/*.[js|sol] - Run:
cd ethereum && make test
- Tests:
- Solana Smart Contracts
- Tests:
./solana/bridge/program/tests/*.rs - Run:
cd solana && make test
- Tests:
- Cosmwasm Smart Contracts
- Tests:
./cosmwasm/test/* - Run:
cd cosmwasm && make test
- Tests:
- Algorand Smart Contracts
- Tests:
./algorand/test/* - Run:
cd algorand && make test
- Tests:
The best place to understand how we invoke these tests via GitHub Actions on every commit can be found via ./.github/workflows/*.yml and the best place to observe the results of these builds can be found via https://github.com/wormhole-foundation/wormhole/actions.
Tests should be written to be extensible and to cover as much of the expected behavior as possible, especially failure cases. Tests added to the repo are expected to prove their error cases, not just the happy path.
Where possible, real example data should be used as the test input rather than hand-crafted data. For example, a real RPC response with on-chain can be saved in-line and tested against a parser.
For Go specifically:
- Use table-driven tests to test multiple inputs in an extensible way.
- Be intentional when using require vs assert.
- Require halts the test and hides all subsequent logging, whereas multiple failing asserts will be logged.
- So, require should be used carefully for setting up e.g. a mock or a test database that is required for the test to run. Asserts should be used for checking actual properties and business logic.
- Use
t.Parallel()for tests that can safely be run in parallel. - Use
testing.Short()to gate tests that are more integration-like or that otherwise are slow. - Use
t.Helper()for any utility/helper functions that are not themselves explicitly tests that you do not want to see in the error traceback.
- Hard-coded sleeps
- Live, external network calls
Refer to [Makefile] for instructions on checking on and updating code coverage.
When adding new unit tests, please update the code coverage metrics.