Overview
Growth depends on word-of-mouth. A referral system lets existing users earn rewards when people they bring in successfully complete escrows. This requires: on-chain referral code binding (so the referral is tamper-proof), backend fee-split accounting, and a frontend dashboard showing earnings and referral activity.
What to build
Contracts (new referral_registry contract):
register_code(referrer: Address, code: Symbol) — registers a unique referral code linked to a Stellar address; codes are globally unique; max 1 code per address
bind_referral(escrow_id: u64, code: Symbol) — called at escrow creation; records (escrow_id → referrer_address) on-chain; fails if escrow already has a referral; emits ReferralBound { escrow_id, referrer, code }
get_referrer(escrow_id: u64) -> Option<Address> — view function
get_code(referrer: Address) -> Option<Symbol> — view function
Backend:
referral_codes table: { code, referrer_user_id, created_at, total_referrals, total_earned_xlm: Decimal }
referral_earnings table: { id, referral_code, escrow_id, triggered_by_event: enum(release|completion), earned_xlm: Decimal, paid_out: bool, created_at }
- On escrow release:
ReferralService.calculateEarning(escrowId) — looks up on-chain referrer, computes platform_fee * referral_pct (configurable, default 20%), records in referral_earnings
POST /api/v1/referrals/codes — create a referral code for the authenticated user
GET /api/v1/referrals/my-stats — returns: code, total referrals, pending earnings (unpaid), total earned, top 5 referred users (anonymised by address prefix)
POST /api/v1/admin/referrals/pay-out — batch pay-out pending earnings via Stellar payment operations; marks entries as paid; requires admin auth
Frontend (/referrals page):
- Referral code display with copy button and shareable link
https://app.example.com/signup?ref={code}
- Stats cards: Total Referrals, Pending Earnings (XLM + USD), Total Paid Out
- Referral activity table: escrow ID (abbreviated), date, earned XLM, payout status
- Share buttons: Twitter/X and Telegram pre-filled with a referral message
Acceptance Criteria
Technical notes
- Referral code must be a valid Soroban
Symbol (max 32 chars, alphanumeric + underscore)
- Earnings percentage is stored in the backend config, not on-chain, to allow adjustment without contract upgrade
Overview
Growth depends on word-of-mouth. A referral system lets existing users earn rewards when people they bring in successfully complete escrows. This requires: on-chain referral code binding (so the referral is tamper-proof), backend fee-split accounting, and a frontend dashboard showing earnings and referral activity.
What to build
Contracts (new
referral_registrycontract):register_code(referrer: Address, code: Symbol)— registers a unique referral code linked to a Stellar address; codes are globally unique; max 1 code per addressbind_referral(escrow_id: u64, code: Symbol)— called at escrow creation; records(escrow_id → referrer_address)on-chain; fails if escrow already has a referral; emitsReferralBound { escrow_id, referrer, code }get_referrer(escrow_id: u64) -> Option<Address>— view functionget_code(referrer: Address) -> Option<Symbol>— view functionBackend:
referral_codestable:{ code, referrer_user_id, created_at, total_referrals, total_earned_xlm: Decimal }referral_earningstable:{ id, referral_code, escrow_id, triggered_by_event: enum(release|completion), earned_xlm: Decimal, paid_out: bool, created_at }ReferralService.calculateEarning(escrowId)— looks up on-chain referrer, computesplatform_fee * referral_pct(configurable, default 20%), records inreferral_earningsPOST /api/v1/referrals/codes— create a referral code for the authenticated userGET /api/v1/referrals/my-stats— returns: code, total referrals, pending earnings (unpaid), total earned, top 5 referred users (anonymised by address prefix)POST /api/v1/admin/referrals/pay-out— batch pay-out pending earnings via Stellar payment operations; marks entries as paid; requires admin authFrontend (
/referralspage):https://app.example.com/signup?ref={code}Acceptance Criteria
Err(CodeTaken)bind_referralfails gracefully if the code is not registered (Err(UnknownCode))?ref=param pre-fills the code on the sign-up pageTechnical notes
Symbol(max 32 chars, alphanumeric + underscore)