Thank you for contributing to the MarketX smart contract. This guide covers everything you need to set up, build, test, and submit quality contributions.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup update stable
rustup default stablerustup target add wasm32-unknown-unknown # for cargo test / dev builds
rustup target add wasm32v1-none # for stellar contract buildcargo install stellar-cli --version 25
stellar --version# Build all contracts as optimised WASM artifacts
make build
# or directly:
stellar contract buildFor production-ready WASM artifacts with repository-standard optimization flags:
make build-prod
# or directly:
./scripts/build_wasm.sh# Run all unit and integration tests
make test
# or directly:
cargo testAll tests must pass before opening a PR. Add tests for every new code path — the existing suite in src/test.rs and tests/integration.rs shows the patterns to follow.
# Auto-format all source files
make fmt
# Check that formatting and compilation are clean (no changes made)
make checkmake check must succeed with zero warnings before opening a PR. The CI pipeline enforces this automatically.
This contract runs in a no_std WASM environment. Do not use:
std::string::String— usesoroban_sdk::Stringstd::vec::Vec— usesoroban_sdk::Vec- Heap allocations outside the Soroban SDK
- Any crate that requires
std
- Authenticate every caller of a state-changing function with
address.require_auth()orSelf::assert_admin(&env). - Read storage once per function call; avoid redundant reads.
- Emit an event for every observable state change.
- Use
?for early-return error propagation — neverunwrap()orpanic!()in production code paths.
- Validate all
Bytesfields against the relevant size constant before use:MAX_METADATA_SIZE(1 024 bytes) for the top-levelmetadatafieldMAX_DESCRIPTION_SIZE(256 bytes) for per-item and milestone descriptionsMAX_TRACKING_ID_SIZE(128 bytes) for shipping tracking IDsMAX_EVIDENCE_HASH_SIZE(128 bytes) for evidence and counter-evidence hashes
- Use the private
Self::validate_bytes_size(data, max)helper — it returnsErr(ContractError::MetadataTooLarge)on violation.
- Return errors via
Result<T, ContractError>. - Choose the most specific error variant. Never return a generic error when a specific one exists.
- Add a doc-comment to every new error variant explaining when it fires.
Error discriminants are part of the on-chain ABI and are stored in transaction results. Once an error code is assigned it must never be renumbered — doing so is a breaking change for all clients.
Rules:
- New errors go in an existing numeric gap (e.g., code 12 is currently free) or at the end of a related block.
- Document every new variant with a Rust doc-comment.
- Update
docs/error-codes.mdandsdk/error-codes.tswhen adding new codes so frontends can display human-readable messages.
- Fork the repository and create a feature branch:
git checkout -b feat/your-feature-name - Make your changes and ensure
make checkandmake testpass. - Open a Pull Request against the
mainbranch. - Link related issues in the PR description using GitHub closing keywords:
Closes #123 Resolves #456 - The maintainer will review and may request changes before merging.
| Prefix | When to use |
|---|---|
feat/ |
New functionality |
fix/ |
Bug fix |
refactor/ |
Code restructuring with no behaviour change |
docs/ |
Documentation only |
test/ |
Test additions or fixes |
chore/ |
Tooling, CI, dependency updates |
Before submitting a PR that touches core logic, verify:
- Every public state-changing function calls
require_auth()orassert_admin(). - All
Bytesinputs are validated against the appropriate size constant. - No
unwrap()orpanic!()in non-test code. - No unbounded loops over user-supplied data.
- Storage entries that should expire set an appropriate TTL or rely on
bump_escrow. - New error variants have doc-comments and are added to
docs/error-codes.md. -
make checkpasses with zero warnings. -
make testpasses with no failures.