New contributor? Check out the 📖 FAQ for common setup, contract, and contribution questions before getting started. 🔤 Glossary — definitions for Soroban, XDR, TTL, Freighter, Horizon, and all Trivela-specific terms.
Trivela is a Stellar Soroban–based campaign and rewards platform. It lets campaign operators create on-chain campaigns, register participants, award points via smart contracts, and let users claim rewards—all on the Stellar network. The project is built for the Stellar Wave on Drips and is designed for open-source contributors.
- �🚀 Live Deployment
- 📖 Overview
- 🏗️ Project Structure
- 🔧 Architecture
- 🔒 Security & Rate Limiting
- 📋 Prerequisites
- ⚡ Quick Start
- 🧪 Testing
- 💻 Tech Stack
- 🛠️ Maintainer Automation
- 🤝 Contributing
- 📚 Resources
- 📄 License
Both contracts are deployed and live on Stellar Testnet, built from the latest main and
verified end-to-end (initialize → credit/balance, initialize → register/participant-count).
| Contract | Contract ID (Testnet) | Explorer |
|---|---|---|
| Rewards | CB6KYDQ7X2V5B46FU7FKZCRCK5ZEYCHRUHZPVF73RA5M5AAUUBQA6IXZ |
View ↗ |
| Campaign | CDDVJVHP6PUYWB42VQJ6YC7GEUQR622JEE5MY65ZIKUETGDT33QZPBQH |
View ↗ |
| Property | Value |
|---|---|
| Network | Stellar Testnet (Test SDF Network ; September 2015) |
| Deployer Key | GA4S5DDI3M3H37IDZIK422IH7WPBECOATLAOYPKOJFLBYTKHU2BB4M6P |
| Soroban RPC | https://soroban-testnet.stellar.org |
Wire these into the frontend via .env.testnet:
VITE_STELLAR_NETWORK=testnet
VITE_REWARDS_CONTRACT_ID=CB6KYDQ7X2V5B46FU7FKZCRCK5ZEYCHRUHZPVF73RA5M5AAUUBQA6IXZ
VITE_CAMPAIGN_CONTRACT_ID=CDDVJVHP6PUYWB42VQJ6YC7GEUQR622JEE5MY65ZIKUETGDT33QZPBQHNote: Testnet deployments are periodically reset by the network. If a contract ID stops resolving, redeploy with
STELLAR_SOURCE=<identity> npm run deploy:testnetand update the values above.
| Component | Description |
|---|---|
| Campaigns | Create and manage reward campaigns with on-chain configuration (Soroban) |
| Rewards Contract | Tracks user points, credits (by admin/campaign), and claims |
| Campaign Contract | Stores campaign active flag and participant registration |
| Backend API | REST API for campaign metadata, health checks, and integration |
| Frontend | React app to list campaigns and connect wallets to interact with contracts |
- Loyalty points – Reward user engagement with on-chain points
- Drip campaigns – Distribute rewards over time based on participation
- Bounties – Issue on-chain rewards for completing tasks
- Custom flows – Any scenario requiring on-chain rewards + off-chain campaign metadata
Trivela/
├── contracts/ # Soroban (Rust) smart contracts
│ ├── rewards/ # Points balance, credit, claim
│ └── campaign/ # Campaign active flag, participant list
├── backend/ # Node.js Express API
├── frontend/ # React + Vite + Stellar SDK
├── Cargo.toml # Rust workspace
├── package.json # npm workspaces (backend + frontend)
└── README.md
For detailed system architecture documentation:
- System Map – Diagram, trust boundaries, and end-to-end data flows:
docs/ARCHITECTURE_OVERVIEW.md - Network Configuration – Stellar testnet/mainnet presets and runtime config flow:
docs/STELLAR_NETWORKS.md
All public /api/* and /api/v1/* routes are protected by a rate limiter that keys per API key
when present and falls back to per-IP otherwise.
| Feature | Description |
|---|---|
| Rate Limit Headers | X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset on every response |
| Rate Limit Exhausted | Returns 429 Too Many Requests with Retry-After header |
| Key Strategy | API key when present, otherwise per-IP fallback |
| Variable | Default | Purpose |
|---|---|---|
RATE_LIMIT_WINDOW_MS |
60000 |
Window size in milliseconds |
RATE_LIMIT_MAX_REQUESTS |
60 |
Max requests per key per window |
REDIS_HOST / REDIS_URL |
(unset) | When set, swaps the in-memory bucket store for Redis so multiple API pods share state |
See
backend/.env.examplefor full configuration options.
- Documentation:
backend/README.md - Middleware Source:
backend/src/middleware/rateLimit.js - Unit Tests:
backend/src/middleware/rateLimit.test.js
| Tool | Purpose | Installation |
|---|---|---|
| Rust | Required for Soroban contract development | rustup |
| Stellar CLI | Optional but recommended for contract deployment | Install Stellar CLI |
| Node.js | Required for backend and frontend development (v18+) | nodejs.org |
git clone https://github.com/FinesseStudioLab/Trivela.git
cd Trivela
npm installThe repo uses Turborepo to coordinate builds and tests across the
backend and frontend workspaces with caching and parallelization.
| Command | Description |
|---|---|
npm run build |
Build contracts (cargo/stellar), then turbo run build (frontend) |
npm run test |
Run backend + frontend tests in parallel (cached) |
npm run lint |
Lint all workspaces in parallel |
npm run dev |
Run backend + frontend concurrently |
Note:
npm run buildbuilds the Soroban contracts first (producing TypeScript bindings the frontend depends on) then runsturbo run build. Turbo caches task outputs, so unchanged workspaces are skipped on subsequent runs.
To share the cache across CI and machines, export a Vercel Remote Cache (or self-hosted) token:
export TURBO_TOKEN=<your-token>
export TURBO_TEAM=<your-team>Without these variables, Turbo falls back to the local .turbo cache only.
Ensure you have the Stellar CLI installed.
# Build both contracts using Stellar CLI
stellar contract build
# Alternatively, build specific packages with cargo
cargo build --target wasm32-unknown-unknown --release -p trivela-rewards-contract
cargo build --target wasm32-unknown-unknown --release -p trivela-campaign-contractexport STELLAR_NETWORK=testnet
export STELLAR_SOURCE=alice| Variable | Description |
|---|---|
STELLAR_NETWORK |
Stellar CLI network alias (e.g., testnet or mainnet) |
STELLAR_SOURCE |
Stellar CLI identity to sign deploy transactions |
# Deploy Rewards Contract
stellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/trivela_rewards_contract.wasm \
--source "$STELLAR_SOURCE" \
--network "$STELLAR_NETWORK"
# Deploy Campaign Contract
stellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/trivela_campaign_contract.wasm \
--source "$STELLAR_SOURCE" \
--network "$STELLAR_NETWORK"After deployment, you will receive a Contract ID. Use it to call the initialize function:
stellar contract invoke --id <CONTRACT_ID> --source alice --network testnet -- \
initialize --admin alice --name "Trivela Rewards" --symbol "TVL"Build and deploy both contracts with the helper script:
STELLAR_SOURCE=alice npm run deploy:testnetOptional environment variables:
| Variable | Default | Description |
|---|---|---|
STELLAR_NETWORK |
testnet |
Stellar CLI network alias to deploy against |
STELLAR_SOURCE |
(required) | Stellar CLI identity used for the deploy |
TRIVELA_ENV_OUT |
.env.testnet |
Output env file for the deployed contract IDs |
The script generates:
VITE_REWARDS_CONTRACT_ID=...
VITE_CAMPAIGN_CONTRACT_ID=...cp backend/.env.example backend/.env
npm run dev:backend| Service | URL |
|---|---|
| API | http://localhost:3001 |
| Health | http://localhost:3001/health |
| API v1 | http://localhost:3001/api/v1 |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/v1/campaigns |
List all campaigns |
GET |
/api/v1/campaigns/:id |
Get campaign by ID |
POST |
/api/v1/campaigns |
Create new campaign |
PUT |
/api/v1/campaigns/:id |
Update campaign |
DELETE |
/api/v1/campaigns/:id |
Delete campaign |
Migration note: Legacy
/api/*campaign routes are still available for backward compatibility, but new integrations should target/api/v1/*.
npm run dev:frontend| Service | URL |
|---|---|
| App | http://localhost:5173 |
The frontend proxies
/apiand/api/v1requests to the backend.
docker compose up --build| Service | URL | Environment Variables |
|---|---|---|
| Backend | http://localhost:3001 | CORS_ALLOWED_ORIGINS=http://localhost:5173 |
| Frontend | http://localhost:5173 | VITE_API_URL=http://backend:3001 |
docker compose --profile redis up --build| Command | Description |
|---|---|
npm run test |
Backend + Frontend tests via Turborepo (parallel, cached) |
cargo test --workspace |
Rust contract tests |
npm run test:contracts |
Rust contract tests (npm wrapper) |
npm run test:backend |
Backend tests only |
npm run build:frontend |
Build frontend (required for E2E tests) |
npm run test:frontend |
Frontend E2E tests (Playwright) |
npm run test:visual |
Frontend visual regression tests |
npm run test:visual:update |
Update baseline snapshots |
npm run test:visual:report |
View detailed HTML report |
The frontend E2E tests use Playwright and run against a local preview server (npm run preview).
| Requirement | Description |
|---|---|
| Backend | Ensure backend is running for tests to hit real API endpoints |
| Isolated | Without backend, tests show "empty state" as expected |
Load tests validate backend performance under synthetic traffic using k6.
brew install k6 # macOS
# See https://k6.io/docs/get-started/installation/ for other platforms# Default scenario: read-campaigns
npm run load-test
# Custom scenarios
LOAD_SCENARIO=burst-registration npm run load-test # User registration spike
LOAD_SCENARIO=claim-storm API_KEY=sk_dev npm run load-test # Reward claim surge| Scenario | Configuration | Description |
|---|---|---|
| read-campaigns | 100 VUs, 30s | Read-heavy GET requests |
| write-campaigns | 10 VUs, 30s | POST campaign creation |
| mixed-read-write | 80R+20W VUs, 60s | Combined traffic |
| burst-registration | 0→200 VUs, 70s | Registration spike |
| claim-storm | 0→150 VUs, 75s | Concurrent reward claims |
See
load-tests/README.mdfor thresholds, CI integration, and custom scenarios.
Visual regression tests capture screenshots of Storybook components and detect unintended UI changes.
cd frontend
# Run tests (starts Storybook automatically)
npm run test:visual
# Update snapshots after intentional design changes
npm run test:visual:update
# View detailed test report with diffs
npm run test:visual:reportTests run automatically in CI on PRs that touch frontend code. See
frontend/tests/visual/README.mdfor adding new tests and troubleshooting.
| Layer | Technology |
|---|---|
| Smart contracts | Rust, Soroban SDK |
| Backend | Node.js, Express |
| Frontend | React, Vite, @stellar/stellar-sdk |
| Network | Stellar (testnet/mainnet), Soroban RPC |
If you cloned or created this repo without git:
./scripts/setup-git.sh
git add . && git commit -m "chore: initial Trivela scaffold"
git branch -M main && git push -u origin mainUse a Personal Access Token (PAT) with
reposcope when pushing over HTTPS, or switch to SSH:git remote set-url origin git@github.com:FinesseStudioLab/Trivela.git.
Sync the shared label taxonomy with GitHub CLI:
gh auth login
npm run labels:sync -- --repo FinesseStudioLab/Trivela| Detail | Location |
|---|---|
| Taxonomy | scripts/github-labels.json |
| Behavior | Idempotent – re-running updates colors/descriptions instead of failing |
After the repo is pushed, create labels and open all 50 issues in GitHub in one go:
node scripts/create-github-issues.js| Detail | Description |
|---|---|
| PAT Source | Reads from .env.local |
| Issues Data | Creates issues from docs/issues-data.json |
| Preference | For labels, prefer npm run labels:sync (uses gh auth instead of PAT) |
We welcome contributions, especially from the Stellar and Drip community.
| Resource | Link |
|---|---|
| Guidelines | CONTRIBUTING.md |
| Open Issues | GitHub Issues |
| Governance | GOVERNANCE.md |
Check the open issues for labeled tasks: backend, frontend, smart-contract, good first issue, etc.
| Resource | Link |
|---|---|
| Stellar Developers | developers.stellar.org/docs |
| Soroban smart contracts | Soroban Documentation |
| **Stellar Wave | Drips** |
| Soroban Examples | github.com/stellar/soroban-examples |
Apache-2.0. See LICENSE for details.