Thanks for your interest in contributing. This guide covers everything you need to get set up, run tests, meet code style requirements, and submit a pull request.
You will need the following tools installed before you can build or test the contracts.
- Edition: 2021
- Target:
wasm32v1-none
Install the required target:
rustup target add wasm32v1-noneIf you don't have Rust installed, follow the official guide at https://www.rust-lang.org/tools/install.
v25.2.0 or higher is required.
cargo install --locked stellar-cliFull installation docs: https://developers.stellar.org/docs/smart-contracts/getting-started/setup
git clone https://github.com/your-org/stellarforge.git
cd stellarforge
cargo build --workspaceRun the full test suite across all workspace members:
cargo test --workspaceRun tests for a single contract using the -p flag:
cargo test -p forge-governor
cargo test -p forge-multisig
cargo test -p forge-oracle
cargo test -p forge-stream
cargo test -p forge-vestingAll tests must pass before you submit a PR.
We believe comprehensive tests are essential for smart contract reliability. Good tests prevent bugs, document expected behavior, and give contributors confidence when making changes.
Every contract function should have tests covering:
- Happy paths — Normal, expected usage with valid inputs
- Error paths — Invalid inputs, unauthorized access, and edge cases that should fail gracefully
- Boundary conditions — Limits, thresholds, and transition points (e.g., exactly at staleness boundary, one second past)
- State transitions — Verify state changes persist correctly (e.g., price updates overwrite previous values)
- Place tests in a
#[cfg(test)]module at the bottom oflib.rs - Name test functions descriptively:
test_<action>_<condition>_<expected_result>- Good:
test_submit_price_with_zero_value_rejected - Good:
test_get_price_at_exact_staleness_boundary_succeeds - Avoid:
test_price,test_error_case
- Good:
- Use a
setup()helper function to reduce boilerplate - Group related tests with comments (e.g.,
// ── Staleness boundary tests ──)
Soroban provides powerful testing utilities. Key patterns:
use soroban_sdk::{
testutils::{Address as _, Ledger},
Env,
};
// Mock all authorization checks
env.mock_all_auths();
// Manipulate ledger time for staleness/expiry tests
env.ledger().with_mut(|l| l.timestamp = 1000);
// Generate test addresses
let admin = Address::generate(&env);
// Test error cases with try_ methods
let result = client.try_submit_price(&base, "e, &0);
assert_eq!(result, Err(Ok(OracleError::InvalidPrice)));/// Verify that submitting a new price for an existing pair overwrites the old one.
/// This ensures stale prices are not retained.
#[test]
fn test_price_update_overwrites_previous_price() {
let env = Env::default();
env.mock_all_auths();
let (_, client) = setup(&env);
let base = Symbol::new(&env, "XLM");
let quote = Symbol::new(&env, "USDC");
// Submit initial price at timestamp 1000
env.ledger().with_mut(|l| l.timestamp = 1000);
let initial_price = 10_000_000i128;
client.submit_price(&base, "e, &initial_price);
// Verify initial price is stored
let data = client.get_price(&base, "e);
assert_eq!(data.price, initial_price);
assert_eq!(data.updated_at, 1000);
// Submit new price for the same pair at timestamp 2000
env.ledger().with_mut(|l| l.timestamp = 2000);
let new_price = 15_000_000i128;
client.submit_price(&base, "e, &new_price);
// Verify get_price() returns the new price, not the old one
let data = client.get_price(&base, "e);
assert_eq!(data.price, new_price, "Expected new price to overwrite old price");
assert_eq!(data.updated_at, 2000, "Expected timestamp to be updated");
}This test demonstrates:
- Clear documentation explaining what's being tested
- Descriptive variable names and comments
- Testing both the action and its side effects
- Explicit assertions with helpful failure messages
- Time manipulation to test state changes
- If you're fixing a bug, add a test that would have caught it
- If you're adding a feature, test both success and failure cases
- If you're modifying existing behavior, update related tests
- Run
cargo test -p <contract-name>frequently during development
cargo fmt --allThis must produce no changes. Run it before committing.
cargo clippy --all-targets -- -D warningsThis must produce zero warnings.
- New public functions and types require
///doc comments. - No
unsafecode is permitted in any contract. - No external crate dependencies beyond
soroban-sdkare permitted without prior discussion with maintainers.
A pre-commit hook can automatically check formatting and linting before each commit, catching issues early and saving CI time.
Quick Setup (using the provided template):
cp scripts/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commitManual Setup:
- Create the hook file:
touch .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit- Add the following script to
.git/hooks/pre-commit:
#!/bin/bash
echo "Running pre-commit checks..."
# Check formatting
echo "Checking code formatting..."
if ! cargo fmt --all -- --check; then
echo "❌ Code formatting check failed. Run 'cargo fmt --all' to fix."
exit 1
fi
# Run clippy
echo "Running clippy..."
if ! cargo clippy --all-targets -- -D warnings; then
echo "❌ Clippy found issues. Fix them before committing."
exit 1
fi
# Run tests (optional - can be slow for large projects)
# Uncomment the following lines if you want to run tests before each commit:
# echo "Running tests..."
# if ! cargo test --workspace; then
# echo "❌ Tests failed. Fix them before committing."
# exit 1
# fi
echo "✅ All pre-commit checks passed!"
exit 0- The hook is optional but highly recommended to catch issues before pushing
- If you need to bypass the hook in an emergency, use
git commit --no-verify - The test step is commented out by default since it can be slow; uncomment if desired
- Each contributor must set up the hook individually (hooks are not tracked by git)
- Fork the repository and create a feature branch off
main. - Make your changes, keeping commits logically atomic (or squash before opening the PR).
- Ensure all CI checks pass locally before requesting review:
cargo fmt --all— no changescargo clippy --all-targets -- -D warnings— zero warningscargo test --workspace— all tests pass
- Open a PR against
main. Your PR description must summarise what changed and why. - If your PR introduces a new contract or public API, include tests covering error paths and state transitions.
- At least one maintainer approval is required before a PR is merged.
By contributing, you agree that your contributions will be licensed under the MIT License.