Thank you for contributing to the BettaPay Soroban smart contracts. This guide covers workspace setup, development workflow, and testing expectations so you can get productive quickly.
- Prerequisites
- Workspace Configuration
- Getting Started
- Development Workflow
- Upgrades and Storage Migrations
- Testing
- Building WASM Binaries
- Optional: Soroban CLI Scripts
- Pull Request Checklist
- Commit Message Conventions
- Reporting Issues
Install the following before working on this repository:
| Tool | Purpose | Notes |
|---|---|---|
| Rust | Build and test contracts | Version pinned in rust-toolchain.toml (currently 1.85.0) |
wasm32-unknown-unknown target |
Compile Soroban WASM | Installed automatically via rust-toolchain.toml |
| Soroban CLI | Deploy and simulate on testnet | Required only for scripts/ workflows |
Clone the repository and enter the workspace root:
git clone https://github.com/Betta-Pay/BettaPay-Contract.git
cd BettaPay-ContractRustup reads rust-toolchain.toml on first cargo invocation and installs the correct toolchain and WASM target.
This repository is a Cargo workspace containing two independently deployable Soroban contracts and a shared library crate. Understanding the config files helps when adding dependencies, running targeted builds, or debugging CI failures.
The workspace root ties the contracts and shared crate together:
[workspace]
members = ["settlement_contract", "governance_contract", "bettapay_common"]
resolver = "2"
[workspace.package]
edition = "2021"
license = "MIT"
publish = false
[workspace.dependencies]
soroban-sdk = "21.7.7"
bettapay_common = { path = "bettapay_common" }| Section | Purpose |
|---|---|
members |
Lists each crate in the workspace |
resolver = "2" |
Uses Cargo's dependency resolver v2 (required for edition 2021) |
[workspace.package] |
Shared metadata inherited by member crates |
[workspace.dependencies] |
Single source of truth for shared dependency versions |
Both contracts reference the SDK with soroban-sdk = { workspace = true }, and the shared bettapay_common crate with bettapay_common = { workspace = true }, so version bumps happen in one place.
Pins the Rust toolchain for reproducible builds across local machines and CI:
[toolchain]
channel = "1.85.0"
targets = ["wasm32-unknown-unknown"]channel— exact Rust version used for compilation and teststargets— ensures the WASM compilation target is available
Each contract under settlement_contract/ and governance_contract/ is a cdylib crate:
[lib]
crate-type = ["cdylib"]
[dependencies]
soroban-sdk = { workspace = true }
[dev-dependencies]
soroban-sdk = { workspace = true, features = ["testutils"] }cdylib— produces a dynamic library suitable for Soroban WASM outputtestutilsfeature — enabled only in dev-dependencies for in-memory contract tests
BettaPay-Contract/
├── Cargo.toml # Workspace root
├── Cargo.lock # Locked dependency graph (committed)
├── rust-toolchain.toml # Pinned Rust + WASM target
├── settlement_contract/ # Merchant registration, fee splits, payment refs
│ ├── Cargo.toml
│ ├── src/lib.rs
│ └── test_snapshots/ # Soroban test snapshot artifacts
├── governance_contract/ # Fee config, anchor registry, system params
│ ├── Cargo.toml
│ ├── src/lib.rs
│ └── test_snapshots/
├── bettapay_common/ # Shared types, events, storage helpers, error codes
│ ├── Cargo.toml
│ └── src/lib.rs
└── scripts/
├── deploy_testnet.sh # Build + deploy both contracts to testnet
└── simulate.sh # Local deploy + init for simulation
After cloning, verify your environment:
# Confirm toolchain (should report 1.85.0)
rustc --version
# Install dependencies and compile host targets
cargo build
# Build WASM (required before first test run — see Testing)
cargo build --target wasm32-unknown-unknown --release
# Run the full test suite
cargo test --workspaceIf cargo prompts to install the toolchain, accept — rust-toolchain.toml handles the rest.
- Find or open an issue — check open issues before starting work.
- Create a feature branch from
main:git checkout main git pull origin main git checkout -b your-name/short-description
- Make focused changes — keep PRs scoped to a single concern (one contract fix, one feature, or one docs change).
- Run local checks before pushing (see Testing and Pull Request Checklist).
- Open a pull request against
mainwith a clear description and test plan.
Contract logic is tested with Soroban's in-memory Env in each crate's #[cfg(test)] mod tests block inside src/lib.rs.
governance_contract test compilation embeds the release WASM via include_bytes!, so build WASM once before the first test run:
cargo build --target wasm32-unknown-unknown --release
cargo test --workspacecargo test -p settlement_contract
cargo test -p governance_contractcargo test -p settlement_contract merchant_lifecycle_uses_canonical_topicsSome tests write JSON snapshots under test_snapshots/tests/. If you intentionally change contract behavior that affects emitted events or storage layout, update snapshots as part of your PR and explain the change in the PR description.
GitHub Actions (.github/workflows/auto-merge.yml) runs the contributor gate on every pull request:
make allThis deterministically runs formatting, workspace compilation, Clippy with all targets and features, workspace tests, script smoke tests, WASM optimization, and the deployed-artifact size check. Run the same command locally to avoid CI failures.
Optimized WASM artifacts are required for deployment:
make optimizeOutput paths:
target/optimized/settlement_contract_opt.wasmtarget/optimized/governance_contract_opt.wasm
Build a single contract:
cargo build --target wasm32-unknown-unknown --release -p settlement_contractDeployment and simulation scripts live in scripts/ and require the Soroban CLI.
bash scripts/simulate.shDeploys both contracts, initializes admin, and writes contract IDs to .soroban/ (gitignored).
bash scripts/deploy_testnet.shEnvironment variables (all optional, with defaults for testnet):
| Variable | Default |
|---|---|
SOROBAN_RPC_URL |
https://soroban-testnet.stellar.org |
SOROBAN_NETWORK_PASSPHRASE |
Test SDF Network ; September 2015 |
BETTAPAY_IDENTITY |
bettapay-admin (deploy script) |
SOROBAN_SOURCE |
bettapay-sim (simulate script) |
Never commit Soroban identity keys or .soroban/ directory contents.
Before requesting review, confirm:
-
cargo fmt --all -- --checkpasses -
cargo clippy --workspace --all-targets --all-features -- -D warningspasses -
cargo test --workspacepasses -
bash scripts/tests/tooling_smoke_test.shpasses -
make wasm_sizesucceeds - New behavior has corresponding unit tests
- Snapshot changes (if any) are intentional and documented
- PR description references the related issue (e.g.
Closes #125)
Use concise, descriptive messages in the imperative mood:
docs: create CONTRIBUTING.md guide
fix(settlement): reject zero-address admin transfer
test(governance): add fee bps boundary coverage
Prefixes: feat, fix, test, docs, refactor, chore, tooling.
Use the GitHub issue templates when filing bugs or feature requests:
- Bug Report — include reproduction steps, expected vs actual behavior, and environment details (network, Rust/soroban-cli versions, commit hash).
- Feature Request — describe the problem, proposed solution, and affected sub-system.
For contract-specific work, select Smart Contracts (BettaPay-Contract) in the subsystem dropdown.