Skip to content

Latest commit

 

History

History
721 lines (501 loc) · 25.5 KB

File metadata and controls

721 lines (501 loc) · 25.5 KB

Contributing to StellarTrustEscrow

This guide is the fastest path from clone to first PR. It covers local setup, testing, linting, the review process, and how to find newcomer-friendly issues.

For the canonical clone-to-running-app walkthrough, use Local development setup.

Table of Contents

Prerequisites

Install these before you start:

Tool Version Why it is needed
Node.js 20 LTS recommended, 18+ supported Frontend, backend, linting, and Jest/Playwright tests
npm Bundled with Node.js Workspace installs and scripts
Rust 1.74+ Soroban smart contracts
wasm32-unknown-unknown target Latest Contract builds
Visual Studio Build Tools (Windows only) Current Required for native Rust linking on Windows
Soroban CLI 21+ Local contract workflows
Docker Desktop or Docker Engine Latest Fast local Postgres and full-stack smoke tests
PostgreSQL 14+ if not using Docker Backend development and Prisma migrations
Git Latest Branching and pull requests
Playwright browsers Current Frontend end-to-end tests

Recommended install commands:

rustup toolchain install stable
rustup target add wasm32-unknown-unknown
cargo install --locked --force soroban-cli

Soroban Development Environment

This section covers everything specific to the Rust/Soroban layer. Skip it if you are only working on the frontend or backend.

Rust toolchain and Soroban CLI

# Install stable Rust (1.74+ required)
rustup toolchain install stable
rustup default stable

# Add the WASM compilation target
rustup target add wasm32-unknown-unknown

# Install the Soroban CLI (pin to a known-good version)
cargo install --locked soroban-cli --version 21.0.0

Verify:

rustc --version        # rustc 1.74.0 or later
soroban --version      # soroban 21.x.x

Configure a Stellar testnet identity

# Generate a new keypair and fund it from Friendbot
soroban keys generate --global contributor --network testnet
soroban keys fund contributor --network testnet

Workspace structure

The Cargo workspace (Cargo.toml at the repository root) contains four contract crates:

Crate Path Purpose
stellar-trust-escrow-contract contracts/escrow_contract Core milestone escrow logic
stellar-trust-governance contracts/governance On-chain governance and voting
stellar-trust-insurance-contract contracts/insurance_contract Dispute insurance pool
stellar-trust-escrow-extensions contracts/escrow_extensions Optional escrow add-ons

All four share a single [profile.release] in the root Cargo.toml:

[profile.release]
opt-level        = "z"
overflow-checks  = true   # integer overflow panics instead of wrapping — critical for financial logic
debug            = 0
strip            = "symbols"
debug-assertions = false
panic            = "abort"
codegen-units    = 1
lto              = true

overflow-checks = true is intentional. Any arithmetic that would silently wrap in a standard release build will instead abort the contract, preventing fund-accounting bugs. Do not disable it.

Running contract tests

Run tests for a single crate to keep feedback fast:

# Core escrow contract
cargo test -p stellar-trust-escrow-contract

# Governance contract
cargo test -p stellar-trust-governance

# Escrow extensions
cargo test -p stellar-trust-escrow-extensions

# All crates at once
cargo test --workspace

Run a specific test by name:

cargo test -p stellar-trust-escrow-contract test_approve_milestone_o1_completion_check

Soroban test harness patterns

Soroban tests use an in-process mock environment rather than a live network. The patterns below appear throughout the test suite.

Env::default() — creates an isolated in-memory Soroban environment:

let env = Env::default();

mock_all_auths() — bypasses require_auth() checks so tests can call any function without real signatures:

env.mock_all_auths();

Call this once at the top of a test. Remove it if you are specifically testing authorisation failures.

Address::generate(&env) — generates a deterministic test address:

let client     = Address::generate(&env);
let freelancer = Address::generate(&env);

env.ledger().with_mut() — advances the ledger clock to simulate time passing:

// Jump forward 7 days
env.ledger().with_mut(|l| {
    l.timestamp += 7 * 24 * 60 * 60;
});

Use this to test deadline expiry, timelock release, and recurring payment scheduling.

A minimal test skeleton:

#[test]
fn test_example() {
    let env = Env::default();
    env.mock_all_auths();

    let client     = Address::generate(&env);
    let freelancer = Address::generate(&env);
    // ... register contract, call functions, assert state
}

15-Minute Quickstart

This path assumes Node, Rust, Docker, and Git are already installed.

1. Fork and clone

git clone https://github.com/YOUR_USERNAME/stellar-trust-escrow.git
cd stellar-trust-escrow
git remote add upstream https://github.com/barry01-hash/stellar-trust-escrow.git

2. Install workspace dependencies

npm ci

3. Start Postgres

Use Docker for the database even if you run the app locally:

docker compose up -d postgres

4. Configure local environment files

cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env.local

PowerShell equivalent:

Copy-Item backend/.env.example backend/.env
Copy-Item frontend/.env.example frontend/.env.local

Update these values in backend/.env for local development:

DATABASE_URL=postgresql://user:password@localhost:5432/stellar_escrow
DIRECT_URL=postgresql://user:password@localhost:5432/stellar_escrow
ALLOWED_ORIGINS=http://localhost:3000
FRONTEND_URL=http://localhost:3000

frontend/.env.local usually only needs:

NEXT_PUBLIC_API_URL=http://localhost:4000

5. Prepare the database

npm run db:generate -w backend
npm run db:migrate -w backend

6. Start backend and frontend

Run these in separate terminals:

npm run dev -w backend
npm run dev -w frontend

Open http://localhost:3000.

7. Optional: build the contracts locally

cargo build -p stellar-trust-escrow-contract --target wasm32-unknown-unknown
cargo build -p stellar-trust-insurance-contract --target wasm32-unknown-unknown

Development Workflow

Branch naming

Branches must follow this pattern (enforced by the pre-push hook):

<type>/<short-description>
Prefix When to use
feat/ New functionality
fix/ Bug fix
refactor/ Code improvement, no behaviour change
docs/ Documentation only
test/ Tests only
chore/ Tooling, dependencies, config
hotfix/ Urgent production fix branched from main
release/ Release preparation (version bump, CHANGELOG)

Examples:

feat/wallet-retry-logic
fix/backend-health-route
docs/contributor-onboarding
chore/upgrade-prisma-5

Keep the description short, lowercase, and hyphen-separated. No ticket numbers in the branch name — link the issue in the PR instead.


Commit message format — Conventional Commits

Every commit must follow Conventional Commits:

<type>(<scope>): <short summary>

<optional body — explain WHY if the diff doesn't make it obvious>

<optional footer>
BREAKING CHANGE: <description>
Closes #<issue>

Type must be one of:

Type When to use
feat New feature visible to users or callers
fix Bug fix
perf Performance improvement
security Security fix or hardening
refactor Code restructuring, no behaviour change
test Adding or fixing tests
docs Documentation only
chore Build process, tooling, dependency updates

Scope is optional but encouraged — use the layer or domain (backend, contracts, frontend, mobile, webhooks, auth, etc.).

Subject line rules:

  • Imperative mood: "add pagination" not "adds pagination" or "added pagination"
  • No capital first letter
  • No trailing period
  • 72 characters max

Examples:

feat(backend): add cursor pagination to /api/escrows
fix(contracts): prevent integer overflow in milestone release
docs: expand PR lifecycle section in CONTRIBUTING
chore(deps): upgrade @stellar/stellar-sdk to 12.1.0
test(backend): add dispute resolution edge cases

If a commit introduces a breaking change, add a BREAKING CHANGE: footer:

feat(api)!: rename client_address to clientAddress in all responses

BREAKING CHANGE: all API consumers must update field references.
Closes #200

The ! after the type is shorthand — the footer is still required.


Typical flow

# 1. Branch from develop
git checkout develop
git pull upstream develop
git checkout -b feat/my-feature

# 2. Make changes, then run the relevant checks
#    (see Testing All Layers and Code Style sections below)

# 3. Commit
git add <files>
git commit -m "feat(backend): add my feature"

# 4. Push
git push -u origin feat/my-feature

Open the pull request on GitHub, targeting develop. Use the PR template — filling it in completely is a requirement, not a suggestion.

Testing All Layers

Run the checks that match the layer you touched. If your PR crosses multiple layers, run all of them.

Smart contracts

Run the full workspace:

cargo test --workspace

Run a single crate for faster iteration:

cargo test -p stellar-trust-escrow-contract
cargo test -p stellar-trust-governance
cargo test -p stellar-trust-escrow-extensions
cargo test -p stellar-trust-insurance-contract

Run a specific test by name:

cargo test -p stellar-trust-escrow-contract <test_name>

For deeper contract verification on macOS, Linux, or WSL:

bash scripts/test-contract.sh --gas --coverage

End-to-end simulation scripts

scripts/simulate/ contains self-contained shell scripts that deploy the escrow contract to Soroban testnet and walk through real lifecycle scenarios. Use them to verify behaviour end-to-end or to demonstrate the contract to integrators.

Script Scenario
happy-path.sh Create escrow → add milestones → freelancer submits → client approves → funds released
dispute-resolution.sh Escrow disputed by client → arbiter rules entirely in client's favour → full refund
expiry-refund.sh Escrow with a short deadline → freelancer misses it → expire_escrow refunds client
mutual-cancellation.sh Client requests cancellation → freelancer consents → immediate settlement

Prerequisites:

# Install the Stellar CLI (includes Soroban support)
cargo install --locked stellar-cli
# or, for older installs:
cargo install --locked soroban-cli --version 21.0.0

Running a simulation:

Each script is self-contained — it generates fresh key-pairs, funds them from Friendbot, builds the WASM, and deploys the contract from scratch. No existing credentials or .env file needed.

# Make scripts executable (first time only)
chmod +x scripts/simulate/*.sh

# Full happy-path lifecycle
bash scripts/simulate/happy-path.sh

# Arbiter dispute resolution
bash scripts/simulate/dispute-resolution.sh

# Expiry with automatic refund (waits ~60 s for deadline to pass)
bash scripts/simulate/expiry-refund.sh

# Mutual cancellation by both parties
bash scripts/simulate/mutual-cancellation.sh

Each script prints a numbered step log and a summary table showing contract ID, escrow ID, outcome, and a Stellar Expert link for on-chain inspection. Expected runtime: under 2 minutes per script.

Network configuration:

By default, all scripts target Soroban testnet. Override with environment variables:

SOROBAN_NETWORK=testnet \
SOROBAN_RPC_URL=https://soroban-testnet.stellar.org \
SOROBAN_NETWORK_PASSPHRASE="Test SDF Network ; September 2015" \
bash scripts/simulate/happy-path.sh

PRs that touch contract logic must include at least one new test. Use Env::default() and mock_all_auths() (see Soroban test harness patterns above). Time-sensitive behaviour must be covered with env.ledger().with_mut().

Backend

npm run test -w backend

Database-related backend changes should also include:

npm run db:migrate:status -w backend

Frontend

npm run test:unit -w frontend
npm run test:integration -w frontend
npm run test:a11y -w frontend

Install Playwright browsers once before the first end-to-end run:

cd frontend
npx playwright install --with-deps chromium firefox

Then run:

npm run test:e2e -w frontend

Helpful root shortcuts

npm run test
npm run test:all

npm run test covers frontend and backend. npm run test:all adds the Rust workspace tests and a frontend production build.

Code Style and Linting

JavaScript and TypeScript

npm run lint
npm run format

Rust contracts

cargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warnings

All lint checks

npm run lint:all

Notes:

  • ESLint and Prettier cover the JS and TS codebase.
  • Husky is installed, but you should still run the relevant checks yourself before pushing.
  • Keep PRs focused. If you touch contracts and frontend together, explain why in the PR.

Pull Request Process

Before you open a PR

  • Claim or create an issue first. Leave a comment so no one duplicates your work.
  • Keep the branch scoped to one logical change. If you find an unrelated bug while working, fix it in a separate branch.
  • Run all relevant checks for the layer(s) you touched (see Testing All Layers and Code Style and Linting).
  • Update documentation when behaviour changes. If you add an endpoint, update docs/api/. If you change an env variable, update README and .env.example.

PR lifecycle

Draft  →  Ready for Review  →  Approved  →  Merged

Draft — open as a draft as soon as the branch exists if you want early visibility or async feedback before the work is done. Drafts do not trigger maintainer review.

Ready for Review — convert to "Ready for review" only when:

  • All checklist items in the PR template are ticked
  • CI is green (or you have explained a known transient failure)
  • You have resolved or replied to every comment from the draft phase

Approved — at least one maintainer must approve. For changes to contract logic, two approvals are required. Approval does not mean merge — it means the reviewer is satisfied. The author does the merge after approval.

Merged — always merge into develop, never directly into main. Use the "Squash and merge" strategy for feature and fix branches so the commit history on develop stays clean and follows Conventional Commits. Use "Merge commit" for release/ branches so the merge point is visible.

Target branch

Branch type Target
feat/, fix/, refactor/, docs/, test/, chore/ develop
release/vX.Y.Z main
hotfix/ main and back-merged into develop

Never open a PR directly against main unless it is a release or hotfix.

What every PR must include

Requirement Details
Passing CI All checks must be green. Do not ask for review with a red pipeline.
Tests New features need tests. Bug fixes need a regression test. Refactors with no behaviour change are exempt — state this explicitly.
Documentation Update any affected doc, README section, or .env.example. Link the relevant file in the PR body.
CHANGELOG entry Add a line under ## [Unreleased] for any user- or caller-visible change. See Versioning Policy.
Linked issue Closes #<number> in the PR body.
Filled-in template Every section of the PR template must be completed — do not delete sections and leave them blank.

Addressing review feedback

  • Push follow-up commits to the same branch — do not close and reopen the PR.
  • Resolve a comment thread only after the requested change is made; let the reviewer re-check.
  • If you disagree with feedback, reply with reasoning. Maintainers can be wrong.
  • Mark trivial acknowledgements ("good catch, fixed") with a thumbs-up rather than a new comment to keep the thread readable.

Review expectations by change type

Change type Expectation
Documentation only Commands must be tested locally; no broken links
Backend / API Tests covering the new or changed behaviour; migration status verified
Contract logic Two approvals; new Soroban test using Env::default() + mock_all_auths()
Frontend / UI Screenshots or short screen recording included
Breaking change Clearly labelled in the PR title (! suffix on type) and body; CHANGELOG updated

Finding a First Issue

Use GitHub labels to find a good starting point:

Label What it usually means
good-first-issue Beginner-friendly tasks with a clear path to completion
documentation Docs cleanups, onboarding, examples, and guides
frontend Next.js UI, accessibility, and interaction work
backend API, services, Prisma, and operational tooling
smart-contract Rust and Soroban work
testing Unit, integration, accessibility, or end-to-end coverage

Useful searches:

  • Good first issues: https://github.com/barry01-hash/stellar-trust-escrow/issues?q=is%3Aopen+is%3Aissue+label%3A%22good-first-issue%22
  • Documentation issues: https://github.com/barry01-hash/stellar-trust-escrow/issues?q=is%3Aopen+is%3Aissue+label%3Adocumentation
  • Help wanted: https://github.com/barry01-hash/stellar-trust-escrow/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22

If you want an issue, leave a comment so maintainers know it is in progress.

OS Notes

  • Linux and macOS: native setup is straightforward.
  • Windows: use PowerShell for npm and Docker commands. Install Visual Studio Build Tools for native Rust builds, or use WSL if you want Linux-style contract tooling and bash-based helper scripts like scripts/test-contract.sh.
  • Docker Desktop works well for local Postgres on all three platforms.

Troubleshooting

npm ci fails early

Make sure you are on Node 18+ and rerun from the repository root.

Prisma cannot connect

Confirm Docker Postgres is running:

docker compose ps postgres

Then verify DATABASE_URL and DIRECT_URL both point at the same local instance unless you intentionally use separate pooled and direct connections.

Rust contract builds fail on Windows

Install Visual Studio Build Tools with the C++ workload, or run the Rust contract commands inside WSL.

Frontend cannot reach the backend

Check that:

  • backend is running on port 4000
  • NEXT_PUBLIC_API_URL=http://localhost:4000
  • ALLOWED_ORIGINS includes http://localhost:3000

Playwright tests fail before opening a browser

Install browsers first:

cd frontend
npx playwright install --with-deps chromium firefox

Questions are welcome in the issue tracker or pull request discussion. Small first contributions are absolutely fine.


Versioning Policy

This project uses Semantic Versioning (MAJOR.MINOR.PATCH).

What triggers each version component

Component When to increment Examples
MAJOR Breaking change — existing integrations must change to upgrade Renamed API field, removed endpoint, contract storage migration, changed JWT format
MINOR New backward-compatible feature New endpoint, new optional field, new contract function that doesn't break existing callers
PATCH Bug fix or internal improvement that doesn't change the API contract Fixed off-by-one in pagination, improved error message, dependency security patch

A breaking change is any change that requires callers to update their code or data to continue working. When in doubt, treat it as breaking.

CHANGELOG maintenance

  • The CHANGELOG is updated manually as part of every PR that changes behaviour.
  • Every entry goes under ## [Unreleased] until a release is cut.
  • Follow the Keep a Changelog format: subsections are Added, Changed, Deprecated, Removed, Fixed, Security.
  • Include the PR or issue number in parentheses: - Added foo bar (#123).
  • Do not auto-generate the CHANGELOG from commit messages — the audience is integrators, not Git history readers.

Release process

  1. Create a release/vX.Y.Z branch from develop.
  2. Move all entries from ## [Unreleased] to a new ## [X.Y.Z] - YYYY-MM-DD section in CHANGELOG.md.
  3. Update the comparison link at the bottom of CHANGELOG.md.
  4. Open a PR targeting main. Title: release: vX.Y.Z.
  5. After merge, create a Git tag: git tag -a vX.Y.Z -m "Release vX.Y.Z" and push it.
  6. Publish a GitHub Release using the CHANGELOG section as the body.
  7. The .github/workflows/release.yml workflow validates that the CHANGELOG contains an entry for the tag being released.

CHANGELOG entry format example

## [3.0.0] - 2026-09-01

> **Breaking change:** Renamed `client_address` to `clientAddress` in all API responses.

### Added

- `GET /api/escrows/:id/timeline` — ordered list of on-chain events for an escrow (#201)

### Changed

- Renamed `client_address` field to `clientAddress` in escrow API responses (#200)

### Fixed

- Cursor pagination no longer skips the last record on page boundaries (#199)

### Security

- Upgraded `@stellar/stellar-sdk` to address CVE-2026-XXXX (#198)