Referrals#
How Seesaw's referral attribution and payout system works on-chain. Every trader can pick exactly one referrer — once, ever (first-touch). For 365 days after that, 40% of every taker fee the trader pays flows to that referrer automatically; after the 365 days lapse, the attribution simply stops earning.
If you're a user, the friendlier explanation is at
docs/for-traders/referrals-and-fees.
How referral attribution works#
The referral system uses three on-chain primitives. Understanding what each one does is the key to understanding the whole flow:
| Account | PDA seeds | What it stores |
|---|---|---|
ReferralAccount | ["seesaw", "referral", referee_pubkey] | One row per trader. Holds the referrer's pubkey, the timestamp it was set at, and expires_at = created_at + 365 days (in seconds). |
ReferrerEarningsAccount | ["seesaw", "referrer_earnings", referrer_pubkey] | One row per referrer. Accumulates the 40% share across every fee paid by every trader who picked them, plus the shard index it is bound to. |
referrer_treasury PDA | ["seesaw", "referrer_treasury", &[shard_index]] | 8 sharded token-account PDAs holding accrued-but-unclaimed referral earnings. Sharding spreads write contention so trades don't serialize through one account. |
The lifecycle#
1. The referrer registers: InitReferrerEarningsAccount (0x22)#
Before anyone can name them as a referrer, the referrer (or anyone on
their behalf) initializes their ReferrerEarningsAccount, choosing a
treasury_index ∈ [0, 8). That index permanently binds the referrer
to one of the 8 referrer_treasury shards (INV-REFERRAL-T1) — every fee
they ever earn routes through that shard. The instruction also lazily
creates the shard token account itself if it doesn't exist yet.
2. The trader opts in: SetReferrer (0x21)#
The trader (referee) signs SetReferrer, which:
- Requires the referee's signature — no third party can set someone else's referrer.
- Rejects self-referral (
referrer == referee). - Requires the referrer's
ReferrerEarningsAccountto already exist (step 1). - Creates the
ReferralAccountPDA withexpires_at = now + 365 days(REFERRAL_DURATION_SECONDS).
First-touch is final. If a ReferralAccount already exists for the
referee — even an expired one — SetReferrer fails with
ReferrerAlreadySet. There is no changing, clearing, or re-pointing a
referral. When the 365 days lapse, the attribution stops earning; it does
not become re-assignable.
3. Attribution at fill time#
When a taker order fills, the fill processor:
- Computes the total taker fee from the capped-decay curve (see
README.md§ Fee Structure). - Splits it three ways — the shares are configured in bps of the fee
and must sum to exactly 100% (default 50% protocol / 10% creator /
40% referral):
- 50% → protocol treasury (see
treasury-and-fee-split.md) - 10% → the market's
accumulated_creator_fees - 40% → the referral slice
- 50% → protocol treasury (see
- For the referral slice, the transaction may supply the optional
referral account triple (the taker's
ReferralAccount, the referrer'sReferrerEarningsAccount, and the bound treasury shard):- Valid, unexpired referral + correct triple → the 40% transfers
into the referrer's bound shard, and the referrer's
accumulatedcounter is credited in the same instruction (INV-REFERRAL-T2: every credit is paired with an equal shard transfer). AReferralAccruedevent is emitted. - No referrer, expired referral, or triple not supplied → the 40%
forfeits to the protocol treasury alongside the 50%
(
ReferralForfeitedevent).
- Valid, unexpired referral + correct triple → the 40% transfers
into the referrer's bound shard, and the referrer's
The taker pays the same total fee either way — referrer presence only changes who receives the 40% slice.
4. Claiming: ClaimReferrerEarnings (0x24)#
When the referrer wants to withdraw:
- The referrer must sign — PDA seeds tie the earnings account to their pubkey.
- The program transfers exactly
accumulatedfrom the referrer's bound shard to the referrer's stablecoin account and resets the counter. - The supplied shard account is re-validated against the bound
treasury_index(ReferrerTreasuryMismatchon a wrong shard).
There's no minimum claim, but each claim costs a transaction fee, so most referrers batch claims.
Why 8 shards?#
- Write contention: Solana parallelizes transactions only when their writable accounts don't overlap. One global referral treasury would serialize every fee-paying fill in the protocol; 8 shards let fills for referrers on different shards execute in parallel.
- Cheap to reconcile: 8 is small enough for the indexer to enumerate every shard and verify the system-wide solvency invariant (Σ shard balances ≥ Σ accumulated earnings) on a schedule.
- Deterministic addresses: each shard is a stable PDA, so the claim and accrual paths always know exactly which account to validate.
A referrer's shard is whatever treasury_index was chosen when their
earnings account was initialized — set once, immutable afterwards.
Account and parameter reference#
Invariants#
Referral invariants are catalogued in
security/invariants.md as INV-F2, INV-F3,
INV-F4 (fee-split and attribution guarantees) and INV-REFERRAL-T1
through INV-REFERRAL-T3 (per-shard credit/transfer pairing and
system-wide solvency). In summary: the three fee shares always sum to 100%,
first-touch attribution is immutable for 365 days, every credit is
paired with an equal shard transfer, and referrer treasuries always cover
the sum of all accumulated earnings. One referral per trader, self-referral
rejected, taker fee identical with or without a referrer.
What can the referrer see?#
The referrer sees:
- The list of traders who picked them (publicly derivable from on-chain
ReferralAccounts). - Their aggregate accrued earnings (via
ReferrerEarningsAccount).
They do not get any special view of individual referee trades — though all Solana activity is publicly visible by wallet address anyway, so the referral system introduces no privacy delta.
Edge cases#
- Referrer's wallet is lost: the
ReferrerEarningsAccountPDA still exists and keeps accruing; the referrer just can't signClaimReferrerEarnings. There's no clawback or escheat. - Referral expires mid-stream: fills after
expires_atroute the 40% to the protocol (ReferralForfeited); nothing else changes for the taker. The binding cannot be replaced afterwards. - Client omits the referral accounts: accrual requires the optional
referral triple to be passed with
PlaceOrder. Standard Seesaw clients pass it automatically when a referral exists; a transaction built without it forfeits that fill's 40% to the protocol. - Multisig / program-owned referrer: works — the referrer pubkey is whatever the trader pointed at; only claiming requires that key to sign.
Related docs#
for-traders/referrals-and-fees— user-facing prosesdk/referral-and-creator-fees— SDK-level integrationhow-it-works/treasury-and-fee-split— the 50% protocol-treasury side of the same feehow-it-works/flow-of-funds— where every fee slice physically goessecurity/invariants— full invariant catalog