Referral System Overview
The Rocky Referral & Rebate system allows users to earn commissions by inviting traders to the platform. Referrers create unique invitation codes, share them with traders, and earn a percentage of the trading fees generated by their referees.
How It Works
Referrer (Promoter)
├── Creates referral code (POST /v1/referral/codes)
└── Shares code with traders
Trader (Referee)
├── Binds referral code (POST /v1/referral/bind)
└── Every trade generates fees → Referrer earns commission
Commission Flow
Trader executes a trade
→ Backend calculates commission (trade_fee × tier_rate)
→ Credited to the referrer's pending balance in referral.earnings (off-chain)
Referrer withdraws via POST /v1/referral/claim → Instant credit to USDT account balance
(On-chain sync and on-chain claim signatures are not yet implemented)
The off-chain half of this flow is implemented: a background pipeline (services/api-gateway/src/accrual.rs, shared with Trading Points) computes trade_fee × tier_rate for every fill by a bound referee and credits the referrer's pending balance in referral.earnings. GET /v1/referral/dashboard's total_referred_volume, active_referrals, pending_earnings, and tier fields all reflect this real data now (previously hardcoded to 0/null). POST /v1/referral/claim moves referral.earnings.pending_amount into a referral.claims row and credits the user's USDT balance, enforcing the documented NO_PENDING_EARNINGS/BELOW_MINIMUM (10 USDT) checks.
Not implemented: the hourly batchSyncRebates on-chain sync and any on-chain rebate ledger. POST /v1/referral/on-chain/claim-signature, GET /v1/referral/on-chain/claimed/:address and GET /v1/referral/on-chain/operator-status are not routed (404). GET /v1/referral/on-chain/user-rebate/:address and GET /v1/referral/on-chain/referral-info/:address return placeholder starter-shape responses without reading any contract. The EIP-712 signature/timestamp fields on create/bind/unbind are accepted but not verified — the caller is resolved from the session token.
Tier System
Commission rates are determined by the referrer's tier, which is based on the cumulative trading volume of their referees:
| Level | Tier Name | Commission Rate | BPS | Min Referrals (AND) | Min Referred Volume (AND) |
|---|---|---|---|---|---|
| 0 | Starter | 10% | 1000 | ≥ 1 | ≥ $1,000 |
| 1 | Bronze | 12% | 1200 | ≥ 5 | ≥ $10,000 |
| 2 | Silver | 17% | 1700 | ≥ 20 | ≥ $100,000 |
| 3 | Gold | 22% | 2200 | ≥ 50 | ≥ $500,000 |
| 4 | Diamond | 25% | 2500 | ≥ 100 | ≥ $2,000,000 |
Both conditions must be met simultaneously; the highest tier for which both clear applies. Users below the Starter threshold have no active tier and accrue no commission (0%). Tiers are automatically recalculated by the backend (from the referrer's total referral count and referees' cumulative
qty × pricetrade volume) on every referee fill.
Smart Contracts
The referral system uses on-chain contracts on the Avalanche C-Chain:
| Contract | Purpose | Status |
|---|---|---|
ReferralStorage | Stores referral code mappings, referral relationships, and tier info | Deployed |
ReferralRebate | Tracks cumulative on-chain rebates, issues claim signatures, handles on-chain withdrawals | Pending redeployment on Avalanche |
GET /v1/referral/on-chain/operator-status (which would expose contract addresses) is not yet implemented.
The backend currently has no integration with either contract: none of the /v1/referral/on-chain/* endpoints read from or write to the chain (see On-Chain Query Endpoints). Off-chain referral accrual and POST /v1/referral/claim are unaffected.
Authentication
All protected endpoints require a wallet session Bearer token:
Authorization: Bearer <session_token>
session_token is the value returned by POST /v1/wallet/verify after signing the challenge from POST /v1/wallet/challenge (24h TTL). A missing or invalid token returns 401 with a plain-text body (missing bearer token / invalid session).
API Endpoints
Public Endpoints (No Auth Required)
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/referral/leaderboard | Top-N referrer leaderboard |
| GET | /v1/referral/on-chain/user-rebate/:address | Query user's on-chain rebate info (placeholder response) |
| GET | /v1/referral/on-chain/referral-info/:address | Query trader's on-chain referral config (placeholder response) |
| GET | /v1/referral/on-chain/claimed/:address | Query on-chain claimed amount — not yet implemented |
| GET | /v1/referral/on-chain/operator-status | Check backend signer operator status — not yet implemented |
Protected Endpoints (Session Token Required)
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/referral/codes | Create referral code |
| POST | /v1/referral/bind | Bind to a referral code |
| POST | /v1/referral/unbind | Unbind from referral code |
| GET | /v1/referral/status | Query referral status (as referrer & referee) |
| GET | /v1/referral/dashboard | Get earnings dashboard |
| GET | /v1/referral/logs | Query operation history |
| POST | /v1/referral/claim | Claim off-chain earnings |
| POST | /v1/referral/on-chain/claim-signature | Generate on-chain claim signature — not yet implemented |
EIP-712 Signature Types
The backend does not currently verify these signatures or the timestamp field: POST /v1/referral/codes and POST /v1/referral/unbind ignore the request body entirely, and POST /v1/referral/bind reads only code. The types below are retained for forward compatibility.
| Operation | TypeHash |
|---|---|
| Create Code | CreateReferralCode(address wallet,uint256 timestamp) |
| Bind Code | BindReferralCode(address wallet,string code,uint256 timestamp) |
| Unbind Code | UnbindReferralCode(address wallet,uint256 timestamp) |
There is currently no backend endpoint that serves the EIP-712 domain (GET /auth/nonce/:address is not implemented).