Soroban smart contracts for Fundlr — a decentralized crowdfunding platform on Stellar.
Fund boldly. Build transparently.
Fundlr enables creators to raise funds for projects using milestone-based escrow and on-chain governance. Supporters contribute XLM or USDC, receive governance tokens, and vote to approve milestone completions before funds are released.
┌─────────────────────────────────────────────────────────┐
│ Frontend │
│ (React + Freighter Wallet) │
└────────────────────┬────────────────────────────────────┘
│ reads via API / writes via Freighter
┌────────────────────▼────────────────────────────────────┐
│ Backend │
│ (NestJS + Horizon Event Indexer) │
└────────────────────┬────────────────────────────────────┘
│ indexes events via Horizon
┌────────────────────▼────────────────────────────────────┐
│ Soroban Contracts (this repo) │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ │
│ │ campaign-factory│───▶│ campaign │ │
│ │ (deploys new │ │ (escrow, voting, │ │
│ │ campaigns) │ │ tokens, refunds) │ │
│ └─────────────────┘ └──┬─────┬─────┬───┘ │
│ │ │ │ │
│ ┌────────┘ │ └────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │campaign- │ │ voting │ │ escrow │ │
│ │ token │ │ │ │ │ │
│ │(SEP-41) │ │ │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────┘
The entry point for Fundlr. Creators call this contract to deploy new campaign instances. Maintains a registry of all campaigns on the platform.
- Deploys new campaign contract instances
- Tracks campaign IDs and creator associations
- Admin-controlled upgrade path
The core contract. One instance is deployed per campaign. Handles the full lifecycle: contributions, escrow, token distribution, milestone voting, fund release, and refunds.
- Accepts XLM/USDC contributions
- Issues campaign governance tokens to contributors
- Manages milestone submission and voting
- Releases funds to creator on milestone approval
- Processes refunds on failed campaigns
A SEP-41 compliant Stellar custom asset issued per campaign. Token balance equals voting power in milestone votes.
- Standard SEP-41 token interface (transfer, approve, burn)
- Minted by the campaign contract at contribution time
- Transferable between wallets
Standalone voting utility contract. The campaign contract delegates vote tallying and validation here.
- Token-weighted vote recording
- Vote finalization with approval/rejection logic
- Reusable across future Fundlr governance features
Standalone escrow contract that holds all campaign funds. The campaign contract instructs this contract to hold, release, or refund funds.
- Holds XLM/USDC deposits per campaign
- Releases funds to creator on milestone approval
- Processes contributor refunds
- Lockable for refund mode
Campaign Creation: Creator → Frontend wizard → Freighter signs → campaign-factory → deploys campaign instance → Horizon event → Backend indexes
Contribution: Backer → Campaign page → Freighter signs XLM/USDC transfer → campaign contract → escrow contract holds funds → campaign-token mints tokens to backer
Milestone Approval: Creator submits evidence → Token holders vote via voting contract → Votes tallied → escrow releases funds to creator
Refund: Deadline passes, goal not met → Anyone triggers refund → escrow returns funds to contributors
# Install Rust (if not already installed)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Add WebAssembly target
rustup target add wasm32v1-none
# Install Soroban CLI
cargo install --locked soroban-cli
# Verify installation
soroban --version# Build all contracts to WASM
cargo build --target wasm32v1-none --release
# Compiled WASM files will be in:
# target/wasm32v1-none/release/*.wasm# Run all contract tests
cargo test
# Run tests for a specific contract
cargo test --package campaign
cargo test --package campaign-factory
cargo test --package campaign-token
cargo test --package voting
cargo test --package escrowNote: Test files are currently stubs. To write real tests, add
features = ["testutils"]to the dev-dependency in the contract'sCargo.toml, then useAddress::generate(&env)andenv.mock_all_auths(). See CONTRIBUTING.md for the test pattern.
# Deploy a contract to Soroban testnet
soroban contract deploy \
--wasm target/wasm32-unknown-unknown/release/campaign.wasm \
--network testnet
# Or use the deploy script
./scripts/deploy.shSee CONTRIBUTING.md for detailed development workflow.
fundlr-contracts/
├── Cargo.toml # Workspace root
├── README.md
├── CONTRIBUTING.md
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug.md
│ │ └── feature.md
│ └── pull_request_template.md
├── contracts/
│ ├── campaign-factory/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── storage.rs
│ │ ├── events.rs
│ │ └── errors.rs
│ ├── campaign/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── storage.rs
│ │ ├── events.rs
│ │ ├── errors.rs
│ │ ├── types.rs
│ │ └── test.rs
│ ├── campaign-token/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── storage.rs
│ │ ├── events.rs
│ │ └── errors.rs
│ ├── voting/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── storage.rs
│ │ ├── events.rs
│ │ └── errors.rs
│ └── escrow/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ ├── storage.rs
│ ├── events.rs
│ └── errors.rs
└── scripts/
├── deploy.sh
└── test.sh
MIT License. See LICENSE for details.