VIP Tier Fee Schedule
Overview
Perp trading fees follow a six-tier VIP ladder driven by a 14-day rolling notional volume across all USDⓈ-M perpetual pairs. Both maker and taker sides count. All fees are charged from the user's collateral margin — no external fee settlement.
The effective fee rate a user actually pays is:
effective_rate = base_rate × (1 − referral_discount) × (1 − token_staking_discount)
effective_fee = notional × effective_rate (rounded up to 6 decimals)
| Piece | Current value |
|---|---|
referral_discount | 0.10 (10%) |
token_staking_discount | 0.00 (interface reserved, always zero today) |
discount_multiplier | (1 − 0.10) × (1 − 0.00) = 0.90 |
notional | amount × mark_price |
| Precision | 6 decimals (USDC) |
Tier Table
| Level | 14d Volume (USD) | Taker | Maker |
|---|---|---|---|
| VIP 0 | < 5 M | 0.040% | 0.010% |
| VIP 1 | 5 M – 25 M | 0.036% | 0.008% |
| VIP 2 | 25 M – 100 M | 0.032% | 0.004% |
| VIP 3 | 100 M – 500 M | 0.028% | 0.000% |
| VIP 4 | 500 M – 2 B | 0.026% | 0.000% |
| VIP 5 | ≥ 2 B | 0.024% | 0.000% |
At VIP 3 and above maker is free.
Upgrade / Downgrade Semantics
- Upgrade is applied immediately the first time a fee-info read, a preview, or a fill observes the user crossing the next threshold.
- Downgrade is scheduled, not immediate: the new (lower) level is
recorded as
pending_tierand takes effect at the next UTC 00:00. This gives users a 24-hour buffer against momentary volume dips. - An audit row is written to
vip_tier_eventsfor everyupgrade_immediate,downgrade_scheduled, anddowngrade_appliedtransition.
GET /account/fee-info
Returns the caller's current VIP tier, the full tier schedule, the 14/30-day rolling volumes, progress to the next tier, and the active discount stack.
Request
Auth: JWT (wallet) or API Key. No query parameters.
GET /api/v1/account/fee-info
Authorization: Bearer <jwt>
Response
{
"current_tier": 3,
"current_label": "VIP 3",
"current_maker": "0.00000",
"current_taker": "0.00028",
"effective_maker": "0.000000",
"effective_taker": "0.000252",
"volume_14d": "138206820.47",
"volume_30d": "215440192.11",
"fee_tiers": [
{ "level": 0, "label": "VIP 0", "maker": "0.00010", "taker": "0.00040", "volume_min": "0", "volume_max": "5000000" },
{ "level": 1, "label": "VIP 1", "maker": "0.00008", "taker": "0.00036", "volume_min": "5000000", "volume_max": "25000000" },
{ "level": 2, "label": "VIP 2", "maker": "0.00004", "taker": "0.00032", "volume_min": "25000000", "volume_max": "100000000" },
{ "level": 3, "label": "VIP 3", "maker": "0.00000", "taker": "0.00028", "volume_min": "100000000", "volume_max": "500000000" },
{ "level": 4, "label": "VIP 4", "maker": "0.00000", "taker": "0.00026", "volume_min": "500000000", "volume_max": "2000000000"},
{ "level": 5, "label": "VIP 5", "maker": "0.00000", "taker": "0.00024", "volume_min": "2000000000" }
],
"progress_to_next": {
"next_level": 4,
"next_label": "VIP 4",
"required_volume": "500000000",
"remaining_volume": "361793179.53",
"percent": "0.276413640"
},
"pending_tier": null,
"pending_effective_at": null,
"discounts": {
"referral": "0.10",
"token_staking": "0",
"multiplier": "0.90"
}
}
Notes:
current_maker/current_takerare the base rates before discount.effective_maker/effective_takeralready includediscount_multiplier; use these for display and for reconciling withPOST /orders/preview.progress_to_nextis omitted when the user is already at VIP 5.pending_tierandpending_effective_atare non-null only when a downgrade is scheduled.
POST /orders/preview — fee fields
The preview endpoint returns fees that already include the discount multiplier, so frontends do not need to apply it again:
{
"order_value": "500.000",
"taker_fee_rate": "0.000252",
"maker_fee_rate": "0.000000",
"est_fee": "0.126000",
...
}
est_fee = order_value × (taker or maker rate) — which rate is used depends on
order_type:
order_type | rate used |
|---|---|
market | taker_fee_rate |
limit | maker_fee_rate |
WebSocket Push — vip_tier
Authenticated clients may subscribe to the private vip_tier channel to
receive push notifications whenever their tier changes.
Subscribe
{ "op": "subscribe", "args": ["vip_tier"] }
Event payload
{
"channel": "vip_tier",
"type": "vip_tier_changed",
"data": {
"user_address": "0xbc7c75bf109cda7104c2467532c578c0e8b0efe0",
"old_tier": 2,
"new_tier": 3,
"volume_14d": "138206820.47",
"reason": "upgrade_immediate",
"timestamp": 1776311435000
}
}
reason is one of:
| Value | Meaning |
|---|---|
upgrade_immediate | Volume crossed a threshold upward; new tier is already in effect. |
downgrade_scheduled | Volume fell; lower tier will apply at next UTC 00:00. data.new_tier is the scheduled target. |
downgrade_applied | The previously scheduled downgrade just took effect. |
Worker Cadence
- Lazy path: Any call into
GET /account/fee-infoorPOST /orders/previewtriggersvip_tier::resolve(), which recomputes and persists the user's tier. Upgrades are applied on the spot, downgrades are scheduled. - Daily sweep: A background worker ticks every 10 minutes and, during the
00:00–00:10 UTCwindow, does two things: applies all duepending_effective_at <= NOW()downgrades, then re-evaluates every user who traded in the past 14 days (so tiers are always up-to-date even for users who never hit the API).
Data Model
The VIP tier service persists, per user:
current_tier— the tier currently in effecteffective_since— when that tier took effectpending_tier+pending_effective_at— scheduled downgrade target and effective time (null when no change is pending)last_volume_14d— rolling 14-day volume used to evaluate the tierupdated_at— last recompute timestamp
An append-only event log also records every tier transition (old tier, new tier, the 14-day volume at the time, reason) so downgrades and upgrades are fully auditable.
Code Examples
curl
curl -s -H "Authorization: Bearer ${JWT}" \
https://api-sepolia.ztdx.io/api/v1/account/fee-info | jq
Python
import requests
r = requests.get(
"https://api.ztdx.io/api/v1/account/fee-info",
headers={"Authorization": f"Bearer {jwt}"},
timeout=10,
)
info = r.json()
print(f"VIP {info['current_tier']} "
f"taker={info['effective_taker']} maker={info['effective_maker']} "
f"volume_14d={info['volume_14d']}")
FAQ
Q: Does the daily sweep ever lower a user's tier mid-day?
No. Once current_tier is set, only a pending_effective_at <= NOW() check in
the 00:00 window can lower it. Lazy reads inside the day can only upgrade.
Q: What if a user is inactive for 14+ days?
Their volume_14d eventually becomes 0 and resolve() schedules a downgrade
to VIP 0. The downgrade applies at the next UTC 00:00.
Q: Are maker rebates paid at VIP 3+? No — maker is zero at VIP 3+ but not negative. Negative-fee market-maker contracts are governed separately via the MM admin API.
Q: Can discount_multiplier exceed 1?
No. Both referral_discount and token_staking_discount are capped in
[0, 1), so the multiplier is always (0, 1].