Enterprise-grade Smart Contracts for Incentive Management on Stellar/Soroban
A comprehensive smart contract ecosystem designed to power quest-based incentive programs, trading campaigns, and gamified user engagement on the Stellar blockchain.
Apollo Contracts is a battle-tested smart contract suite that enables businesses to create and manage incentive campaigns (quests) with automatic reward distribution. Perfect for:
- Trading Competitions - Reward users for volume milestones
- Liquidity Mining - Incentivize pool participation
- Token Holding Campaigns - Encourage long-term holding
- Community Engagement - Gamify user interactions
โ
Multiple Quest Types: TradeVolume, PoolPosition, TokenHold
โ
Flexible Distribution: Raffle or First-Come-First-Served
โ
Automatic Balance Deduction: Secure escrow system
โ
Real-time Analytics: Comprehensive stats and monitoring
โ
Enterprise Security: Tested with 100% function coverage
โ
TypeScript SDK: Full type safety for frontend integration
# Install Stellar CLI
cargo install --locked stellar-cli
# Add testnet configuration
stellar network add \
--global testnet \
--rpc-url https://soroban-testnet.stellar.org:443 \
--network-passphrase "Test SDF Network ; September 2015"# 1. Clone the repository
git clone https://github.com/ParaDevsAI/apollo-contracts.git
cd apollo-contracts
# 2. Create and fund your account
stellar keys generate admin --network testnet
stellar keys fund admin --network testnet
# 3. Build and deploy
cd contracts/quest-manager
stellar contract build
stellar contract deploy \
--wasm target/wasm32v1-none/release/quest_manager.wasm \
--source admin \
--network testnet
# ๐ Your contract is live!# Create a trading volume quest with 5 XLM rewards
stellar contract invoke \
--id YOUR_CONTRACT_ID \
--source admin \
--network testnet \
-- create_quest \
--admin YOUR_ADMIN_ADDRESS \
--reward_token CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC \
--reward_per_winner 10000000 \
--max_winners 5 \
--distribution Raffle \
--quest_type '{"TradeVolume":"50000000"}' \
--duration_seconds 86400 \
--reward_pool_amount 50000000 \
--title '"Daily Volume Challenge"' \
--description '"Trade 5 XLM worth to win rewards!"'# Rust toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32v1-none
# Stellar CLI
cargo install --locked stellar-cli# Add testnet
stellar network add \
--global testnet \
--rpc-url https://soroban-testnet.stellar.org:443 \
--network-passphrase "Test SDF Network ; September 2015"
# Add mainnet (when ready)
stellar network add \
--global mainnet \
--rpc-url https://soroban-rpc.stellar.org:443 \
--network-passphrase "Public Global Stellar Network ; September 2015"# Generate new account
stellar keys generate admin --network testnet
# Get account address
stellar keys address admin
# Fund testnet account
stellar keys fund admin --network testnet
# Check balance
curl -s "https://horizon-testnet.stellar.org/accounts/$(stellar keys address admin)" | jq '.balances'# Navigate to contract directory
cd contracts/quest-manager
# Compile the contract
stellar contract build
# Verify compilation
ls -la target/wasm32v1-none/release/quest_manager.wasm# Deploy to testnet
stellar contract deploy \
--wasm target/wasm32v1-none/release/quest_manager.wasm \
--source admin \
--network testnet
# Save the returned contract ID
export CONTRACT_ID="CXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"# Deploy to mainnet (when ready)
stellar contract deploy \
--wasm target/wasm32v1-none/release/quest_manager.wasm \
--source admin \
--network mainnetRun our comprehensive test suite to verify everything works:
# Return to project root
cd ../..
# Run complete verification
./test-comprehensive.shThis will verify:
- โ All 15 contract functions
- โ Balance deduction system
- โ Quest workflow end-to-end
- โ Error handling
- โ Data persistence
stellar contract invoke \
--id $CONTRACT_ID \
--source admin \
--network testnet \
-- create_quest \
--admin $(stellar keys address admin) \
--reward_token CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC \
--reward_per_winner 5000000 \
--max_winners 10 \
--distribution Fcfs \
--quest_type '{"TradeVolume":"20000000"}' \
--duration_seconds 604800 \
--reward_pool_amount 50000000 \
--title '"Weekly Trading Challenge"' \
--description '"Trade 2 XLM worth in one week to win!"'stellar contract invoke \
--id $CONTRACT_ID \
--source admin \
--network testnet \
-- create_quest \
--admin $(stellar keys address admin) \
--reward_token CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC \
--reward_per_winner 10000000 \
--max_winners 5 \
--distribution Raffle \
--quest_type '{"PoolPosition":"100000000"}' \
--duration_seconds 1209600 \
--reward_pool_amount 50000000 \
--title '"Liquidity Provider Rewards"' \
--description '"Maintain 10 XLM in liquidity pool!"'stellar contract invoke \
--id $CONTRACT_ID \
--source admin \
--network testnet \
-- create_quest \
--admin $(stellar keys address admin) \
--reward_token CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC \
--reward_per_winner 2000000 \
--max_winners 20 \
--distribution Raffle \
--quest_type '{"TokenHold":["CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC","50000000"]}' \
--duration_seconds 2592000 \
--reward_pool_amount 40000000 \
--title '"HODL Challenge"' \
--description '"Hold 5 XLM for 30 days to be eligible!"'# User registers for quest
stellar contract invoke \
--id $CONTRACT_ID \
--source user \
--network testnet \
-- register \
--quest_id 0 \
--user $(stellar keys address user)# Verify user is registered
stellar contract invoke \
--id $CONTRACT_ID \
--source user \
--network testnet \
-- is_user_registered \
--quest_id 0 \
--user $(stellar keys address user)# After backend verifies user completed the task
stellar contract invoke \
--id $CONTRACT_ID \
--source admin \
--network testnet \
-- mark_user_eligible \
--quest_id 0 \
--user $(stellar keys address user)# Resolve quest (select winners)
stellar contract invoke \
--id $CONTRACT_ID \
--source admin \
--network testnet \
-- resolve_quest \
--quest_id 0
# Distribute rewards to winners
stellar contract invoke \
--id $CONTRACT_ID \
--source admin \
--network testnet \
-- distribute_rewards \
--quest_id 0# Get specific quest details
stellar contract invoke \
--id $CONTRACT_ID \
--source admin \
--network testnet \
-- get_quest \
--quest_id 0
# Get all active quests
stellar contract invoke \
--id $CONTRACT_ID \
--source admin \
--network testnet \
-- get_active_quests
# Get quest statistics
stellar contract invoke \
--id $CONTRACT_ID \
--source admin \
--network testnet \
-- get_quest_stats \
--quest_id 0# Get quest participants
stellar contract invoke \
--id $CONTRACT_ID \
--source admin \
--network testnet \
-- get_participants \
--quest_id 0
# Get quest winners
stellar contract invoke \
--id $CONTRACT_ID \
--source admin \
--network testnet \
-- get_winners \
--quest_id 0# Get user's quests
stellar contract invoke \
--id $CONTRACT_ID \
--source admin \
--network testnet \
-- get_user_quests \
--user $(stellar keys address user)
# Get user statistics
stellar contract invoke \
--id $CONTRACT_ID \
--source admin \
--network testnet \
-- get_user_stats \
--user $(stellar keys address user)import { Client } from './contracts/quest-manager/bindings/src/index.js';
const client = new Client({
contractId: 'YOUR_CONTRACT_ID',
networkPassphrase: 'Test SDF Network ; September 2015',
rpcUrl: 'https://soroban-testnet.stellar.org:443'
});
// Create quest with full type safety
const questId = await client.create_quest({
admin: adminAddress,
reward_token: tokenAddress,
reward_per_winner: 1000000,
max_winners: 5,
distribution: { tag: 'Raffle', values: void 0 },
quest_type: { tag: 'TradeVolume', values: [5000000] },
duration_seconds: 86400,
reward_pool_amount: 5000000,
title: 'Daily Challenge',
description: 'Complete daily trading goals!'
});import QuestManagerSDK from './sdk-integration/simple-quest-sdk.js';
const sdk = new QuestManagerSDK();
// Get active quests
const activeQuests = await sdk.getActiveQuests();
// Register user
await sdk.register(questId, userSecretKey);
// Check if user is registered
const isRegistered = await sdk.isUserRegistered(questId, userAddress);import React, { useState, useEffect } from 'react';
import QuestManagerSDK from './sdk/quest-manager-sdk';
function QuestList() {
const [quests, setQuests] = useState([]);
const sdk = new QuestManagerSDK();
useEffect(() => {
async function loadQuests() {
const result = await sdk.getActiveQuests();
if (result.success) {
setQuests(result.data);
}
}
loadQuests();
}, []);
return (
<div>
<h2>Active Quests</h2>
{quests.map(quest => (
<div key={quest.id} className="quest-card">
<h3>{quest.title}</h3>
<p>{quest.description}</p>
<p>Reward: {quest.reward_per_winner / 10000000} XLM</p>
<p>Winners: {quest.max_winners}</p>
</div>
))}
</div>
);
}contracts/quest-manager/
โโโ src/
โ โโโ lib.rs # Main contract logic
โ โโโ types.rs # Data structures and enums
โ โโโ storage.rs # Storage helper functions
โ โโโ errors.rs # Error definitions
โ โโโ test.rs # Unit tests
โโโ bindings/ # TypeScript bindings (auto-generated)
โโโ Cargo.toml # Rust dependencies
| Function | Description | Access Level |
|---|---|---|
create_quest |
Create new incentive campaign | Admin only |
register |
User registers for quest | Public |
mark_user_eligible |
Mark user as task completed | Admin only |
resolve_quest |
Select winners (raffle/fcfs) | Admin only |
distribute_rewards |
Send rewards to winners | Admin only |
get_active_quests |
List all active quests | Public |
get_quest_stats |
Get quest analytics | Public |
| Type | Description | Use Case |
|---|---|---|
| TradeVolume | User must achieve trading volume | DEX trading campaigns |
| PoolPosition | User must maintain liquidity position | Liquidity mining programs |
| TokenHold | User must hold specific tokens | Token retention incentives |
| Method | Description | Best For |
|---|---|---|
| Raffle | Random selection from eligible users | Fair chance for all participants |
| FCFS | First eligible users win | Rewarding quick completion |
โ
Access Control: Admin-only functions protected
โ
Balance Verification: Automatic escrow system
โ
Reentrancy Protection: Soroban built-in safeguards
โ
Input Validation: Comprehensive parameter checking
โ
Error Handling: Graceful failure modes
โ
Event Logging: Complete audit trail
- 15/15 Functions Tested โ
- 100% Error Cases Covered โ
- End-to-End Workflows Verified โ
- Balance Deduction Confirmed โ
- Data Persistence Validated โ
- Audit smart contract code
- Test on mainnet with small amounts
- Verify admin key security
- Set up monitoring systems
- Prepare incident response plan
-
Security Review
# Run comprehensive tests ./test-comprehensive.sh # Verify all functions work # Check balance deduction # Test error scenarios
-
Admin Account Security
# Generate secure admin account stellar keys generate admin-prod --network mainnet # Fund with sufficient XLM # Secure the secret key properly
-
Contract Deployment
# Deploy to mainnet stellar contract deploy \ --wasm target/wasm32v1-none/release/quest_manager.wasm \ --source admin-prod \ --network mainnet -
Post-Deployment Verification
# Test basic functions stellar contract invoke \ --id $MAINNET_CONTRACT_ID \ --source admin-prod \ --network mainnet \ -- get_quest_counter
# Monitor contract events
stellar events -c $CONTRACT_ID --network mainnet
# Set up balance monitoring
# Implement alerting for admin actions
# Track quest creation and completion ratesYour backend should monitor DEX activity and mark users eligible:
// Example backend service
class QuestOracle {
async monitorTradingVolume(questId, userAddress) {
// Monitor DEX APIs (Aqua, Blend, etc.)
const volume = await this.getUserTradingVolume(userAddress);
const quest = await this.getQuest(questId);
if (volume >= quest.quest_type.TradeVolume) {
// Mark user as eligible
await this.markUserEligible(questId, userAddress);
}
}
async markUserEligible(questId, userAddress) {
return await this.sdk.markUserEligible(
questId,
userAddress,
adminSecretKey
);
}
}// Listen for Stellar events
app.post('/webhook/stellar-events', async (req, res) => {
const event = req.body;
if (event.type === 'user_registered') {
// Start monitoring this user for quest completion
await startMonitoring(event.quest_id, event.user);
}
res.status(200).send('OK');
});# Verify contract ID is correct
stellar contract invoke --id $CONTRACT_ID --network testnet -- get_quest_counter
# Check if contract exists on network
curl -s "https://horizon-testnet.stellar.org/contracts/$CONTRACT_ID"# Check admin balance
curl -s "https://horizon-testnet.stellar.org/accounts/$(stellar keys address admin)" | jq '.balances'
# Fund testnet account
stellar keys fund admin --network testnet# Enable verbose logging
stellar contract invoke --id $CONTRACT_ID --source admin --network testnet --verbose -- function_name
# Check event logs for error details# Clean and rebuild
cargo clean
stellar contract build
# Verify Rust target
rustup target add wasm32v1-none- Documentation: Check function comments in
src/lib.rs - Tests: Review
src/test.rsfor usage examples - Issues: Open GitHub issues for bugs
- Community: Join Stellar Discord for help
Create .env file for easier management:
# .env
STELLAR_NETWORK=testnet
ADMIN_SECRET_KEY=SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
CONTRACT_ID=CXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XLM_TOKEN=CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC# Deploy your own token contract
stellar contract deploy --wasm token.wasm --source admin --network testnet
# Use custom token in quests
stellar contract invoke --id $CONTRACT_ID --source admin --network testnet -- create_quest \
--reward_token $CUSTOM_TOKEN_ID \
# ... other parameters# Create multiple quests efficiently
for i in {1..5}; do
stellar contract invoke --id $CONTRACT_ID --source admin --network testnet -- create_quest \
--admin $(stellar keys address admin) \
--reward_token $XLM_TOKEN \
--reward_per_winner 1000000 \
--max_winners 10 \
--distribution Raffle \
--quest_type "{\"TradeVolume\":\"$((i * 1000000))\"}" \
--duration_seconds 86400 \
--reward_pool_amount 10000000 \
--title "\"Quest $i\"" \
--description "\"Automated quest $i\""
done-
Quest Performance
- Total quests created
- Active quest count
- Completion rates
- Average time to completion
-
User Engagement
- Registration rates
- Participation by quest type
- Repeat participation
- Reward distribution efficiency
-
Financial Metrics
- Total rewards distributed
- Average reward per quest
- Cost per acquisition
- ROI by quest type
# Get total quest count
stellar contract invoke --id $CONTRACT_ID --source admin --network testnet -- get_quest_counter
# Monitor active quests
stellar contract invoke --id $CONTRACT_ID --source admin --network testnet -- get_active_quests | jq length
# Track user participation
stellar contract invoke --id $CONTRACT_ID --source admin --network testnet -- get_user_stats --user $USER_ADDRESS- Oracle Integration - Automatic task verification
- Multi-Token Support - Diverse reward tokens
- Governance System - Community-driven quest approval
- NFT Achievements - Gamification with collectibles
- Referral System - Viral growth mechanics
- Analytics Dashboard - Real-time insights
We welcome contributions! See our Contributing Guide for:
- Code style guidelines
- Testing requirements
- Pull request process
- Issue reporting
This project is licensed under the MIT License - see the LICENSE file for details.
- GitHub Issues: Report bugs or request features
- Documentation: Complete guides in
/docs - Examples: Sample implementations in
/examples - Discord: Join our community for real-time support
Built with โค๏ธ by ParaDevsAI for the Stellar ecosystem
Apollo Contracts - Powering the future of incentivized engagement on Stellar ๐