跳到主要内容

Points System Overview

The Rocky Points System rewards traders for activity on the platform. Points accumulate within each Epoch and determine token distribution at the end of each Season.

Base URL: https://api.rocky.exchange

Epoch & Season Structure​

Season (≈56 days)
├── Epoch 1 (≈14 days)
├── Epoch 2 (≈14 days)
├── Epoch 3 (≈14 days)
└── Epoch 4 (≈14 days)
  • Each Epoch is a row in points.epochs (id, start_date, end_date, status); its length is DB configuration, not pinned by code. The ≈14-day figure above is the intended cadence — the epoch seeded by the backend migration (Epoch 1) actually runs 2026-01-01 → 2026-12-31. Use GET /v1/epochs for the live boundaries. Points accumulate within the epoch.
  • A Season spans start_epoch..end_epoch (also DB configuration; the seeded Season 1 covers epochs 1–4). At season end, tokens are intended to be distributed proportionally to weighted points — the snapshot/distribution engine is not implemented yet (see below).

Point Types​

TypeCodeHow EarnedStatus
Trading PointsTPCalculated per fill based on volume × tier rateImplemented — a background accrual pipeline (services/api-gateway/src/accrual.rs) polls ledger.trades every 5s and writes points.balances/points.events using exactly this rate table
PnL PointsPPCalculated on position close based on realized PnLNot implemented — no accrual source (would need a durable log of realized-PnL deltas over time; ledger.positions only holds current state)
Holding PointsHPAccrued per minute based on open position valueNot implemented — needs a continuous position-valuation sampler, not built
Referral PointsRPAwarded when a referee reaches the volume trigger within 7 days of bindingNot implemented (distinct from referral commission, which is implemented — see that page)
Staking PointsSPAccrued while staking tokensNot implemented — no staking feature exists in rocky-backend at all

points.balances.trading_points/trading_volume/trade_count are the only fields with a real writer; pnl_points/holding_points/referral_points/staking_points stay at their schema default of 0 for every user. The season snapshot/distribution engine (points.distributions, POST /v1/points/claim/:distribution_id, weighted-share calculation) is also not implemented — it depends on all five point types being accrued to produce a meaningful total_points weighting.

Tier System​

Trading point rates are multiplied by the user's Tier, determined by 14-day rolling trade volume:

TierVolume (14-day rolling)Maker RateTaker Rate
T1$0 – $4,999,9991.2 TP / $1,0000.8 TP / $1,000
T2$5M – $99,999,9991.5 TP / $1,0001.0 TP / $1,000
T3≥ $100M2.0 TP / $1,0001.3 TP / $1,000

The accrual pipeline selects the rate from the trader's trailing-14-day volume at fill time. The tier reported by GET /v1/points, GET /v1/points/tier and the leaderboard is instead derived from the epoch-cumulative trading_volume stored in points.balances, using the same $5M / $100M boundaries.

Earn Level​

Total points within an epoch determine the user's Earn Level, which sets a weight multiplier used for season token distribution:

LevelPoints RangeWeight
L00 – 9994
L11,000 – 9,9998
L210,000 – 49,99912
L350,000 – 199,99925
L4200,000 – 499,99960
L5500,000+120

Token Distribution Formula​

Intended formula — the engine that computes it is not implemented, so points.distributions stays empty:

weighted_points = total_points × earn_level_weight
share_pct = user_weighted_points / Σ(all_users_weighted_points)
token_reward = season_user_pool × share_pct

Claim window: the per-record claim_deadline on the distribution row (nullable, set by the distribution engine — no fixed 30-day constant exists in code). POST /v1/points/claim/:distribution_id currently returns 501 because on-chain EIP-712 claim signing is not configured.

Season Token Pool​

SeasonEpochsUser PoolMM Pool
S11–435,000,00020,000,000
S25–825,000,00016,000,000
S39–1220,000,00013,000,000
S413–1620,000,00013,000,000
S517–2020,000,00013,000,000
S621–2430,000,00025,000,000

Only Season 1 (35,000,000 / 20,000,000, epochs 1–4) is seeded by the backend migration. Seasons 2–6 are the planned schedule; they are DB rows in points.seasons, not code constants, and do not exist until inserted. GET /v1/points/seasons returns what is actually configured.

Authentication​

Protected endpoints require the wallet session token returned by POST /v1/wallet/verify (an opaque 64-char hex token, not a JWT):

Authorization: Bearer <session_token>

A missing, invalid, expired or revoked token returns 401 with a plain-text body (missing bearer token / invalid session), not JSON. Other errors on /v1/points/* are likewise plain-text bodies: 400 for a malformed UUID path parameter, 500 for a database failure.

API Endpoints​

Public Endpoints (No Auth Required)​

MethodEndpointDescription
GET/v1/epochsList all epochs
GET/v1/points/earn-level-configGet Earn Level configuration
GET/v1/points/seasonsList all seasons
GET/v1/points/distribution/:season_idSeason token distribution records (all users)
GET/v1/points/leaderboardEpoch cumulative leaderboard (top 100, current epoch)
GET/v1/points/leaderboard/dailyDaily points increment leaderboard — stub, returns an empty board
POST/v1/points/simulateSimulate points for a trade

Protected Endpoints (Session Token Required)​

MethodEndpointDescription
GET/v1/pointsGet user points summary (aliases: /v1/points/balance, /v1/points/summary)
GET/v1/points/historyGet points history
GET/v1/points/tierGet user tier info
GET/v1/points/earn-quotaGet Earn Level quota info
POST/v1/points/claim/:distribution_idClaim season tokens (EIP-712) — not implemented, always 501