Thank you for contributing to the Invoice Liquidity Network!
This guide covers everything you need to go from a fresh machine to an accepted pull request.
- Monorepo & Cross-Repo Workflow
- Commit Messages
- Changesets & Releases
- PR Size Guidelines
- Issue Assignment & Wave Participation
- Formatting
- Code Owners
- Smart-contract workflow: Environment Setup Β· Building Β· Testing Β· Code Style Β· PR Requirements Β· Review Process Β· Soroban Gotchas
ILN is developed across a small set of repositories. Most contributors touch only one, but anything user-facing usually spans two.
| Repository | What lives there |
|---|---|
ILN-Smart-Contract (this repo) |
Rust/Soroban contracts, the @iln/sdk TypeScript package, the indexer, the notifications service, scripts, and docs |
ILN-Frontend |
The web app that consumes @iln/sdk |
The TypeScript packages in this repo (sdk/, indexer/, notifications/,
tests/e2e/) are independent npm packages. They are intended to be driven
through the root Makefile, which prefers pnpm when available and
falls back to npm:
make install # install dependencies across all TS packages
make build # build the contracts (cargo) + the @iln/sdk package
make test # cargo test (Rust workspace)
make test-insurance # cargo test -p insurance_pool (insurance pool contract)
make test-e2e # end-to-end suite in tests/e2e
make lint # cargo fmt --check + clippy
make docs # regenerate the SDK API docs
make help # list every targetIf you have Turborepo or a pnpm workspace configured at the org root, the equivalent commands are
pnpm install,pnpm turbo build, andpnpm turbo testβ they fan the same scripts out across packages.
When an SDK change requires a matching frontend update (e.g. a new method or a changed signature), keep them releasable together:
- Land the SDK change first in this repo behind a new version. Add a changeset (see below) describing the public API change.
- Publish
@iln/sdk(handled by the release workflow on merge tomain). - Bump the dependency in
ILN-Frontendto the new SDK version and make the matching UI change in a separate PR there. - Link the two PRs to each other in their descriptions so reviewers can see the full change set.
Never make a breaking SDK change and rely on an unpublished local build in the frontend β every PR must build against published, versioned packages.
This project uses Conventional Commits so that the changelog can be generated automatically with make changelog.
Format: <type>(<optional scope>): <description>
| Type | When to use |
|---|---|
feat |
A new feature or contract function |
fix |
A bug fix |
refactor |
Code change that neither fixes a bug nor adds a feature |
perf |
Performance improvement |
test |
Adding or updating tests |
docs |
Documentation only changes |
build |
Build system or external dependency changes |
ci |
CI configuration and workflow changes |
chore |
Tooling or housekeeping that doesn't touch src |
The optional scope tells readers (and the changelog) which package changed. Use the package directory name:
| Scope | Area |
|---|---|
contracts (or a crate name: invoice_liquidity, iln_governance, iln_distribution, reputation_bonus) |
Soroban contract code |
sdk |
The @iln/sdk TypeScript package |
indexer |
The event indexer service |
notifications |
The notification service |
scripts |
Operational / deployment scripts |
docs |
Documentation |
ci |
Workflows and pipeline config |
Examples:
feat(governance): add quorum requirement for proposal passing
fix(sdk): handle missing allowance in fundInvoice
docs(sdk): write comprehensive integration guide
test(indexer): add reputation endpoint coverage
chore(scripts): add contract health check monitoring script
Breaking changes must include BREAKING CHANGE: in the commit footer:
feat(sdk)!: rename fund_invoice to fund
BREAKING CHANGE: fund_invoice has been renamed to fund in the invoice_liquidity contract.
Version management for the published TypeScript packages uses
Changesets. Any PR that changes the
public surface of a published package (today that's @iln/sdk, and soon @iln/cli)
must include a changeset so the version bump and changelog entry are generated
automatically.
# From the repo root, after staging your code change:
npm run changesetThe wizard asks which packages changed and whether the bump is patch
(bug fix), minor (backwards-compatible feature), or major (breaking
change), then writes a markdown file under .changeset/. Commit that file with
your change.
Example commit message:
feat: add Changesets for coordinated SDK and CLI version management
- A PR with no changeset is fine for changes that don't affect a published package (contract-only work, docs, CI).
- On merge to
main, the release workflow consumes pending changesets, bumps versions, updatesCHANGELOG.md, and publishes.
Rust crate versions and the contract changelog continue to be managed with
make changelog (git-cliff) β see the Commit Messages
section.
Prefer small, focused PRs β they get reviewed faster and merged sooner.
- Aim for under ~400 lines of diff (excluding generated files, lockfiles, and snapshots). Larger changes are fine when they're mechanical, but flag them in the description.
- One logical change per PR. Don't mix a refactor with a feature, or a contract change with an unrelated docs cleanup.
- Split large efforts into a reviewable sequence: scaffolding β core logic β tests β docs, each as its own PR where practical.
- If a PR must be large (e.g. a new contract), call it out up front and add a
reviewer guide in the description ("start with
X, thenY").
Contributions are organised into Waves β time-boxed batches of issues.
- Find an issue. Browse open issues; those tagged for the current Wave are
labelled accordingly. Good first issues are labelled
good first issue. - Get assigned before you start. Comment on the issue to request it and wait for a maintainer to assign you, so two people don't build the same thing. One active issue per contributor at a time unless told otherwise.
- Reference the issue in your branch, commits, and PR. Close it from the PR
with a
Closes #<n>footer. - Stay responsive. If an assigned issue goes quiet for several days a maintainer may unassign it so someone else can pick it up.
- Ask early. Use a GitHub Discussion or the issue thread if scope is unclear β clarifying before coding saves a round of review.
All code must be formatted with rustfmt before committing. CI will reject unformatted code.
cargo fmt --allProject-specific settings are in rustfmt.toml (max_width = 100, edition = "2021").
This repo uses a CODEOWNERS file to automatically request reviews from the right team on every PR.
| Path | Owner |
|---|---|
contracts/ |
@Keengfk/contracts-team |
docs/ |
@Keengfk/docs-lead |
scripts/, .github/workflows/ |
@Keengfk/devops |
SECURITY.md |
@Keengfk/security-lead |
| everything else | @Keengfk/maintainers |
CODEOWNER approval is required before merging (enforced via branch protection on main). To enable this on a new repo, go to Settings β Branches β Branch protection rules and check Require review from Code Owners.
The sections below cover the Rust/Soroban contribution loop end to end:
- Environment Setup
- Building the Contracts
- Running Tests
- Code Style
- PR Requirements
- Review Process
- Soroban-Specific Gotchas
# Install rustup if you don't have it
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
# Minimum supported version: 1.74
rustup update stable
# Add the WASM target used by Soroban
rustup target add wasm32v1-none
# Add formatting and linting components
rustup component add rustfmt clippycargo install --locked stellar-cli --features opt
stellar --versionRe-run the install command to upgrade an existing installation.
git clone https://github.com/Invoice-Liquidity-Network/ILN-Smart-Contract.git
cd ILN-Smart-ContractFor a more detailed walkthrough (testnet account setup, troubleshooting) see
docs/developer-quickstart.md.
# Debug build (native, fast β used for tests)
cargo build
# Optimised WASM build (required before deployment)
cargo build --release --target wasm32-unknown-unknown
# Alternative Soroban-specific optimized build
# `cargo build-wasm` is a workspace alias defined in `.cargo/config.toml`.
# It builds optimized WASM to the Soroban-specific target:
# cargo build --target wasm32v1-none --release
# or use the Makefile shortcut:
# make soroban-optimizeWASM output lands in target/wasm32v1-none/release/*.wasm.
The
build-wasmalias is defined in.cargo/config.toml.
The release profile enables LTO andopt-level = "z"β typical output is 10β80 KB per contract.
# Entire workspace
cargo test
# Single contract
cargo test -p invoice_liquidity
cargo test -p iln_governance
cargo test -p iln_distribution
cargo test -p reputation_bonus
cargo test -p insurance_pool
# Useful flags
cargo test -p invoice_liquidity -- --nocapture # show stdout
cargo test -p invoice_liquidity test_name # filter by nameTests run on your native architecture via soroban-sdk test utilities β no WASM build needed.
cargo test -p iln_fuzzProperty tests generate thousands of random cases and may take a few minutes. To skip them during rapid iteration:
cargo test -p invoice_liquidity -- --skip prop_
# or limit case count
PROPTEST_CASES=100 cargo test -p invoice_liquidityCI enforces β₯ 95 % line coverage on invoice_liquidity using
cargo-tarpaulin. Run it locally
before pushing if you touch that crate:
cargo install cargo-tarpaulin --locked
cargo tarpaulin -p invoice_liquidity --fail-under 95All code must be formatted with rustfmt using the workspace defaults:
cargo fmt --allCI will reject PRs with formatting differences.
Zero Clippy warnings are required:
cargo clippy --all-targets -- -D warningsFix every warning before opening a PR. Do not use #[allow(...)] to silence
warnings without a comment explaining why.
- Keep functions small and single-purpose.
- Prefer explicit error types over
unwrap/expectin contract code. - Document public functions with a
///doc comment. - Avoid introducing new dependencies without discussion in an issue first.
<type>/<short-description>
Examples: feat/multi-token-support, fix/overflow-in-discount, docs/contributing.
Follow the Conventional Commits spec:
<type>(<optional scope>): <short summary>
[optional body]
[optional footer β e.g. Closes #101]
Common types: feat, fix, docs, refactor, test, chore.
Example:
docs: add CONTRIBUTING.md for smart contract contributors
Covers env setup, build, test, style, PR process, and Soroban gotchas.
Closes #101
-
cargo fmt --allβ no diff -
cargo clippy --all-targets -- -D warningsβ zero warnings -
cargo testβ all tests pass -
cargo test -p iln_fuzzβ fuzz suite passes -
cargo test -p insurance_poolβ insurance pool tests pass (ifinsurance_poolwas modified) - Coverage β₯ 95 % if
invoice_liquiditywas modified - New behaviour is covered by tests
-
cargo build-wasmsucceeds (required for any contract change) - PR description explains what changed and why
- Related issue linked in the PR description or footer (
Closes #<n>)
- Open a PR against
mainon the upstream repo (Invoice-Liquidity-Network/ILN-Smart-Contract). - CI runs automatically:
test β clippy β benchmarks β coverage. All jobs must be green before review begins. - At least one maintainer approval is required to merge.
- Address review comments with new commits (do not force-push during review).
- A maintainer will squash-merge once approved.
Soroban uses Wasm 2.0 with no WASI. Always use:
rustup target add wasm32v1-none
cargo build --target wasm32v1-none --releaseUsing the old wasm32-unknown-unknown target will produce a binary that the
Stellar runtime rejects.
Contract crates use #![no_std]. Use soroban-sdk types (Vec, Map,
String, β¦) instead of std equivalents. The std crate is only available
in test code gated behind #[cfg(test)].
Unit tests run on native via the SDK's mock environment. Always do a
cargo build-wasm before deploying to confirm the WASM compiles cleanly β
some no_std violations only surface at WASM compile time.
See docs/developer-quickstart.md Β§ 7 for the
full deploy workflow. The live testnet contract ID is:
CD3TE3IAHM737P236XZL2OYU275ZKD6MN7YH7PYYAXYIGEH55OPEWYJC
The scripts/check_benchmark_regression.sh script compares instruction counts
against stored baselines for invoice_liquidity and iln_governance. CI
runs it as a warning-only step, but a large regression will be flagged during
review. Run it locally after performance-sensitive changes:
bash scripts/check_benchmark_regression.shOpen a GitHub Discussion or comment on the relevant issue.
Dependabot is configured to automatically submit PRs to update dependencies. Maintainers should review and merge them as appropriate.