This directory contains the Automated Payout Distribution System for the StellarEarn platform, implementing issue #24.
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32-unknown-unknown
# Install Soroban CLI (optional)
cargo install --locked soroban-cli# Build for testing
cargo build
# Build WASM for deployment
cargo build --target wasm32-unknown-unknown --releaseAfter a successful WASM build, generate and validate a provenance attestation:
node scripts/generate-provenance.js
node scripts/check-provenance.jsThis creates target/wasm32-unknown-unknown/release/earn_quest.wasm.provenance.json alongside the built artifact.
To build a release package with checksum metadata, run:
node scripts/package-release.jsThat command creates a release/ directory containing:
earn_quest.wasmearn_quest.wasm.provenance.jsonearn_quest.wasm.sha256earn_quest.wasm.metadata.json
After cloning, install the pre-commit hooks so cargo fmt, clippy, and fast unit
tests run automatically before every commit to contract files:
# From the repo root
bash scripts/install-hooks.sh
# Or from this directory via Make / just
make install-hooks
just install-hooksThe hook only fires when .rs or .toml files inside contracts/earn-quest/ are
staged — other commits are unaffected.
To run the same checks manually at any time:
make pre-commit-check # via Make
just pre-commit-check # via justTo bypass the hook in an emergency:
SKIP_CONTRACT_HOOKS=1 git commit -m "..."# Run all tests
cargo test
# Run fast unit tests only (same subset the pre-commit hook uses)
cargo test --lib
make test-fast
# Run with output
cargo test -- --nocapture
# Using Make
make test
make test-verbose
# Run cross-contract tests
cargo test --test test_cross_contractTargeted slices for the recent contract work:
# Escrow lifecycle and refunds
cargo test test_escrow
# Event topic/data layout for indexers
cargo test test_events
# Dispute record workflow and emitted events
cargo test test_dispute
# Cross-contract interface tests
cargo test test_cross_contract# Update test snapshots (174 files)
make snapshots
# Verify snapshots
make snapshots-verify
# Show statistics
make snapshots-statsSee SNAPSHOT_MANAGEMENT.md for complete documentation.
For full end-to-end integration testing against a real local Stellar standalone network with fixed, reproducible keypairs and contract IDs:
Prerequisites: Docker, Rust (wasm32-unknown-unknown target), and Stellar CLI.
# 1. Start local network, deploy contracts, write .env.local
make local-env-setup
# 2. Run end-to-end lifecycle integration tests
make local-env-test
# 3. Tear everything down when done
make local-env-cleanVariants:
# Skip Docker if you already have a local node running
make local-env-setup SKIP_DOCKER=1
# Run abbreviated test suite (skips escrow, XP, pause checks)
make local-env-test QUICK=1
# Print full CLI output for every test step
make local-env-test VERBOSE=1
# Print the deterministic keypairs without starting anything
./setup-local-env.sh --keys-onlyWhat the scripts do:
setup-local-env.sh— Pulls and starts thestellar/quickstartDocker image in standalone mode, creates 5 fixed deterministic keypairs (Admin, Creator, Verifier, Contributor, Oracle), funds them via local friendbot, builds and deploys theearn_questWASM, initialises the contract, and writes all IDs and keys to.env.localin the project root.verify-local-env.sh— Loads.env.localand exercises a full quest lifecycle on the live local network: quest registration → proof submission → verifier approval → reward claim → XP verification → pause/resume.
⚠️ The deterministic secrets insetup-local-env.share local development only. They are never used on testnet or mainnet.
See SNAPSHOT_MANAGEMENT.md for test snapshot management.
contracts/earn-quest/
├── src/
│ ├── lib.rs # Main contract implementation
│ ├── payout.rs # ⭐ Payout transfer logic
│ ├── storage.rs # Storage helpers
│ ├── types.rs # Data structures
│ └── errors.rs # Error definitions
├── tests/
│ └── test_payout.rs # Comprehensive payout tests
├── Cargo.toml # Dependencies and config
└── IMPLEMENTATION_SUMMARY.md # Detailed documentation
- Trustless reward transfers from contract escrow to users
- Integration with Stellar token standard
- Balance validation before transfers
pub fn claim_reward(
env: Env,
quest_id: Symbol,
submitter: Address,
amount: i128,
) -> Result<(), Error>Flow:
- User authentication
- Validate submission is approved or partially paid
- Validate requested amount against remaining reward balance
- Transfer the requested amount from escrow
- Update submission status to
PartiallyPaidorPaid - Emit claim event
InsufficientBalance- Contract lacks fundsAlreadyClaimed- Duplicate claim preventionInvalidSubmissionStatus- Wrong workflow stateTransferFailed- Token transfer errors
env.events().publish(
(symbol_short!("claimed"), quest_id),
submitter
);3 comprehensive tests - All passing ✅
- test_payout_success - Happy path validation
- test_insufficient_balance - Error handling
- test_double_claim_prevention - Security validation
running 3 tests
test test_insufficient_balance ... ok
test test_payout_success ... ok
test test_double_claim_prevention ... ok
test result: ok. 3 passed; 0 failed
Escrow tracking now uses a split-storage model so the hot path stays small and the accounting rules are easier to reason about:
EscrowBalancesstorestotal_deposited,total_paid_out,total_refunded,is_active, anddeposit_countEscrowMetastores the colder fields:depositor,token, andcreated_at- The available balance is always computed with the same formula:
total_deposited - total_paid_out - total_refunded
This keeps deposit, payout validation, payout recording, and refund logic on a single accounting model while get_escrow_info() still exposes the assembled public view.
The contract now keeps the most useful filter fields in event topics so indexers can query by actor, quest, and token without decoding event payloads first.
quest_reg: quest id, creator, reward asset are indexedsub_appr: quest id, submitter, verifier are indexedclaimed: quest id, submitter, reward asset are indexedesc_dep,esc_pay,esc_ref: quest id, user, token are indexeddisp_open,disp_res,disp_wd: quest id and dispute participants are indexed
Amounts and other display-oriented values remain in event data so topic space is reserved for query keys.
Dispute handling is intentionally hybrid:
- the contract records dispute state and emits auditable events
- evidence review and adjudication remain off-chain
- arbitrators resolve or close disputes by writing the result back on-chain
The full operator flow is documented in docs/DISPUTE_RESOLUTION.md.
To ensure fairness, the contract supports an escalation path for resolved disputes:
- Escalation: Initiators can appeal a resolved dispute if they disagree with the outcome.
- Senior Review: Appeals are escalated to a senior reviewer or admin for a final verdict.
- On-Chain Tracking: The appeal status and final resolution are recorded on the ledger.
Detailed documentation is available in docs/APPEAL_PROCESS.md.
WASM Binary: target/wasm32-unknown-unknown/release/earn_quest.wasm (21KB)
Optimized for deployment to Stellar network.
| Requirement | Status |
|---|---|
| Rewards transfer correctly | ✅ |
| Asset validation | ✅ |
| Balance checking | ✅ |
| Claim reward function | ✅ |
| Partial claims supported | ✅ |
| Duplicate prevention | ✅ |
| Event emission | ✅ |
| Comprehensive tests | ✅ |
// 1. Register quest with reward
client.register_quest(
&quest_id,
&creator,
&reward_asset, // Stellar token address
&1000, // Reward amount
&verifier,
&deadline,
);
// 2. User submits proof
client.submit_proof(&quest_id, &user, &proof_hash);
// 3. Verifier approves
client.approve_submission(&quest_id, &user, &verifier);
// 4. User claims reward
client.claim_reward(&quest_id, &user, &100);
// ✅ Tokens transferred to user's account- Authorization checks on all state-changing functions
- Duplicate claim prevention via status tracking
- Balance validation before transfers
- Proper error propagation for safe failure handling
- Event logging for transparency and monitoring
-
Deploy to Testnet
soroban contract deploy \ --wasm target/wasm32-unknown-unknown/release/earn_quest.wasm \ --source deployer \ --network testnet
-
Integration Testing - Test with real Stellar tokens
-
Frontend Integration - Connect to UI
-
Monitoring Setup - Index claim events
- IMPLEMENTATION_SUMMARY.md - Detailed implementation notes
- Contract README (parent directory) - Full contract documentation
- Inline code documentation with
///comments
This implementation follows the contribution guidelines:
- ✅ Assignment completed
- ✅ Timeframe: Completed within 48-72 hours
- ✅ Ready for PR with "Close #24"
MIT - See LICENSE for details
Status: ✅ Production Ready
Issue: #24 Build Automated Payout Distribution System
Labels: contract, payouts, stellar-assets, priority-high