Thank you for your interest in contributing to QuorumCredit! We welcome contributions from developers, researchers, and DeFi enthusiasts.
To ensure a smooth collaboration process, please follow these guidelines.
When creating a new branch, please use one of the following prefixes followed by the issue number or a short description:
| Prefix | Purpose | Example |
|---|---|---|
feat/ |
New features | feat/163-add-contributing-guide |
fix/ |
Bug fixes | fix/issue-55-auth-error |
docs/ |
Documentation changes | docs/update-readme-yield |
refactor/ |
Code refactoring | refactor/optimize-vouch-loop |
test/ |
Adding/updating tests | test/add-slash-coverage |
We follow the Conventional Commits specification for our commit messages:
type: description
Common types include:
feat: A new featurefix: A bug fixdocs: Documentation only changesstyle: Changes that do not affect the meaning of the code (white-space, formatting, etc.)refactor: A code change that neither fixes a bug nor adds a featuretest: Adding missing tests or correcting existing tests
Example: feat: add user authentication to request_loan
- Fork the repository and create your branch from
main. - Code: Implement your changes.
- Test: Ensure all tests pass locally (see Testing below).
- Style: Run formatting tools (see Style Guide below).
- PR: Open a Pull Request against the
mainbranch.- Provide a clear description of the change.
- Link any related issues (e.g.,
Resolves #163).
All contributions must pass existing tests. Before submitting your PR, run the following:
# Run all Soroban contract tests
cargo test
# Run tests with output for debugging
cargo test -- --nocaptureIf you are adding a new feature, please include corresponding test cases in src/lib.rs.
We follow standard Rust formatting conventions. Please run the following before committing:
cargo fmt --allHappy Coding! 🚀
QuorumCredit uses an M-of-N multisig model for all privileged operations (slash, config, treasury, pause, upgrade). A single compromised key cannot unilaterally act.
admins: Vec<Address>— the registered admin set (N keys)admin_threshold: u32— minimum signatures required (M)- Every admin function takes
admin_signers: Vec<Address>and callsrequire_admin_approval, which verifies:admin_signers.len() >= admin_threshold- Every signer is in the registered
adminsset - Every signer has authorised the transaction (
require_auth)
| Deployment | Recommended setup |
|---|---|
| Testnet / staging | 1-of-1 (single key, for speed) |
| Mainnet small team | 2-of-3 |
| Mainnet production | 3-of-5 |
A good rule of thumb: threshold = floor(N/2) + 1 (simple majority).
- Use hardware wallets (Ledger, Trezor) for every admin key — never hot wallets.
- Geographic separation — store key backups in physically separate, offline locations.
- No key sharing — each admin controls exactly one key; never share private keys.
- Rotate keys periodically using
rotate_admin. Rotation requires the current quorum to sign, so a single compromised key cannot rotate itself in. - Verify after rotation — always call
get_admins()after any key management operation to confirm the expected set. - Never reuse compromised keys — if a key is suspected compromised, rotate it out immediately using the remaining quorum.
- Keep threshold satisfiable —
remove_adminis blocked if it would make the threshold unreachable; plan key ceremonies before removing admins.
| Function | Description |
|---|---|
add_admin(signers, new_admin) |
Add a new admin; threshold unchanged |
remove_admin(signers, admin) |
Remove an admin; blocked if threshold would become unsatisfiable |
rotate_admin(signers, old, new) |
Atomically swap one key for another; count and threshold preserved |
set_admin_threshold(signers, n) |
Update the quorum threshold |
set_config(signers, config) |
Full config update including admins and threshold |
All of the above require the current quorum to sign.