> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rekt.money/llms.txt
> Use this file to discover all available pages before exploring further.

# Referrals

> How a referrer registers, how a wallet links itself to a referrer once and for life, the discount a linked wallet gets on the exit fee, the referrer's share and its tiers, how a share is credited and claimed, and what Minute players get.

*Values as of 2026-11-10; they can change.* Referrals are built into the Knockout program on
Solana. Every rule here is a rule of that program, and every value lives on chain.

## In short

| | |
| - | - |
| **Referrer** | A wallet that registers a referral record. The record collects the referrer's share. |
| **Link** | A wallet links itself to one referrer, by its own signature. The link is for life. |
| **Discount** | A linked wallet pays **10 %** less exit fee. A link made before 2026-11-10 keeps **25 %**. The rate is fixed into the link when the link is made. |
| **Referrer's share** | A part of the protocol's share of each settled trade of a linked wallet. The rate starts at **20 %** and rises in tiers, up to **50 %**. |
| **Which trades count** | For the share, only trades committed at or after the moment of the link. For the discount, every close settled after the link exists. |
| **Claim** | The record's owner withdraws the credited share in USDC, at any time. |

## Becoming a referrer

A wallet registers a referral record with `register_referral_account`. The wallet signs, and any
payer can pay the rent of the record. Each wallet can register one record. A record does not need a
trading account first.

The wallet that signs is the record's **authority**: it claims the share. The authority can hand the
record to another wallet with `set_referral_authority`. The record's address does not change, and its
links stay with it.

## Linking a wallet to a referrer

A wallet links itself to a referrer's record with `set_referral`.

* **Only the wallet can link itself.** The link is made for the wallet that signs. A payer can pay
  the rent, but nobody can link a wallet that is not theirs.
* **One link, for life.** A wallet has at most one link. Once made, the link cannot be changed, moved
  to another referrer or removed. A second link for the same wallet is refused.
* **A wallet cannot refer itself.** A link to a record whose authority is the same wallet is
  refused.
* The link can be made in its own transaction or in the same transaction as a trade.

The `referrer` argument of `open` is not a referral link. It moves no money and creates no link.

## The discount

A linked wallet pays a lower exit fee. The discount is a share of the exit fee: **10 % off** the fee
the close would pay without a link. A link made before 2026-11-10 keeps its **25 % off**.

* **Fixed in the link.** The discount rate is copied into the link when the link is made, and it
  never changes. A later change to the program's discount setting applies only to links made after
  it.
* **Every close after the link.** The discount applies to every close of the wallet's positions that
  settles after the link exists. The time the position opened does not change this.
* **Only where there is a fee.** A knockout pays no exit fee, so it has no discount. The same holds
  for any other close that pays no exit fee.
* **It comes out of the protocol's part of the fee.** The pool's part of the fee does not change.

Each discounted close emits a `ReferralDiscounted` event. It carries the fee without the discount
(`fee_full`) and the amount saved (`discount`). The fee actually charged is on the close's
`SettlementReceipt` event (`exit_fee_total`), so `fee_full` is `exit_fee_total` plus `discount`.

## The referrer's share

When a linked wallet's trade settles, the referrer's record is credited a share of the **protocol's
part** of that settlement: the protocol's part of the exit fee, after the discount, and the protocol's
part of the loss tax. The share is moved to the program's referral vault and credited to the record.

* **Which trades earn.** A trade earns the share only if it was committed at or after the moment
  the link was made. A trade committed before the link earns nothing, even if it settles later.
* **What the share is computed on.** The share is computed on the protocol's part that the settle
  actually pays. The protocol's own part of a close is paid last, and not at all while any win is
  still owed (see [how a win is paid](https://docs.rekt.money/liquidity.md#how-a-win-is-paid)). A settle that pays the
  protocol nothing credits no share.
* **The rate.** The rate is the record's tier. A record starts at the default tier and moves up as
  the revenue its linked wallets drive grows. That revenue is the record's running total of the
  protocol's part on which the share was computed.

| Revenue driven by the record's linked wallets (USDC) | Rate, share of the protocol's part |
| - | - |
| Below 10,000 | 20 % |
| 10,000 | 25 % |
| 40,000 | 30 % |
| 150,000 | 35 % |
| 500,000 | 40 % |
| 650,000 | 45 % |
| 1,000,000 | 50 % |

The tier rises when a record's total crosses a line and the record is refreshed with
`refresh_referral`. Anyone can send it, for up to twenty records at a time. The rate applied to each
settle is on its event, and it is never more than half of the protocol's part.

### How the share is recorded

Each settle of a linked wallet's trade emits a `ReferralAccrued` event, after the settle's other
events. It names the record and the position, and carries:

| Field | Meaning |
| - | - |
| `base` | The protocol's part the share was computed on. 0 when the trade does not earn. |
| `amount` | The share credited to the record. 0 when the trade does not earn. |
| `rate_bps` | The rate applied, in basis points. |
| `tier_bps` | The record's tier at the settle. |
| `base_total` | The record's running total after this settle. |

If the settle finds a fault at the referral accounts that the sender cannot cause, it emits
`ReferralSkipped` instead, and the share goes to the protocol. A trade is never refused for it.

### When the discount and the share apply

The discount and the share are applied by the settle of the position, when the settle carries the
wallet's link and, for the share, the referrer's record and the referral vault. A settle of a linked
book that leaves them out can be refused (`ReferralAccountsRequired`, 6306); one that is not refused
applies no discount and credits no share. If you settle your own positions, include the three
accounts.

`config.referralFlags` holds the program's referral switches. One can stop the share for every link,
one can stop the discount for every link, and one lets a settle of a linked book that leaves out the
referral accounts be refused. A record can be disabled. A disabled record earns nothing more, its links stay, the wallets
linked to it keep their discount, and it can still claim what it was credited.

## Claiming

The record's authority withdraws the credited share with `claim_referral`.

* It names an amount, or 0 to take everything credited and not yet claimed. An amount above that
  is refused.
* It pays into any USDC token account, except three: the referral vault itself, the pool's vault
  account and the protocol's treasury.
* A claim with nothing to pay does nothing.
* Credited money stays on the record until it is claimed. A claim has no waiting period.

Each claim emits a `ReferralClaimed` event with the amount and the record's lifetime total claimed.

## Minute players

A Minute round is a Knockout position held by a player account that Minute derives from your wallet.
The link of the wallet is copied to that player account with `bind_referral`: the copy names the
same referrer, the same moment of the link and the same discount. Anyone can send it, and the
sender pays the rent. Only a wallet's own link can be copied, so the copy always says what the
wallet's link says.

Once the copy exists, a Minute round gets the discount and earns the share under the same rules as
any other trade. A round settled before the copy exists has no discount and credits no share.

## Reading it

| What | Where |
| - | - |
| Program totals: USDC claimed by referrers, referrers who have claimed, linked wallets | [`GET /v0/referral`](https://docs.rekt.money/data-api) |
| A link was made | the `ReferralLinked` event |
| A record was registered | the `ReferralAccountRegistered` event |
| A discount was given | the `ReferralDiscounted` event |
| A share was credited | the `ReferralAccrued` event |
| A share was claimed | the `ReferralClaimed` event |
| A record's tier moved | the `ReferralRefreshed` event |
| The referral switches | `config.referralFlags` |

## Using the SDK

The SDK builds every instruction a wallet signs here, derives every address and decodes every
account and event. Addresses passed to a builder are hex (`toHex`); decoded addresses are hex; money
is `bigint` in USDC base units. Each builder returns a `BuiltInstruction`: turn it into a web3.js
instruction with `ourInstruction`, then simulate and send it as on the [quickstart](https://docs.rekt.money/sdk/quickstart.md).

### Become a referrer

The wallet signs. `payer` pays the rent of the record and can be the same wallet or an app.

```ts theme={"dark"}
import { type PublicKey } from '@solana/web3.js';
import { referralRecordPda, registerReferralAccount, toHex, type BuiltInstruction } from '@rekt-trading/1000x';

export function becomeReferrer(wallet: PublicKey, payer: PublicKey): { built: BuiltInstruction; record: PublicKey } {
  const built = registerReferralAccount({ referrer: toHex(wallet), payer: toHex(payer) });
  // The record's address comes from the wallet that registers it, and never moves.
  return { built, record: referralRecordPda(wallet).address };
}
```

### Link a wallet, once

The wallet being linked signs; `payer` can be an app. The record is the one derived from the
referrer's registering wallet. Read the wallet's link address first: if an account is there, the
wallet is already linked for life and a second link is refused.

```ts theme={"dark"}
import { type Connection, type PublicKey } from '@solana/web3.js';
import {
  decodeReferralLink,
  referralLinkPda,
  referralRecordPda,
  setReferral,
  toHex,
  type BuiltInstruction,
  type ReferralLinkView,
} from '@rekt-trading/1000x';

/** The wallet's link, or null when it has none. A link never changes, so one read is enough. */
export async function readLink(connection: Connection, wallet: PublicKey): Promise<ReferralLinkView | null> {
  const info = await connection.getAccountInfo(referralLinkPda(wallet).address, 'confirmed');
  return info === null ? null : decodeReferralLink(new Uint8Array(info.data));
}

export function linkToReferrer(wallet: PublicKey, payer: PublicKey, referrerWallet: PublicKey): BuiltInstruction {
  return setReferral({
    referee: toHex(wallet),
    payer: toHex(payer),
    record: toHex(referralRecordPda(referrerWallet).address),
  });
}
```

`readLink(...)?.discountBps` is the discount frozen into the link.

### Read a referrer's earnings

`referralEarnings` returns what an earnings screen shows, from the record and the settings on chain.
Show its fields; compute no amount of your own.

```ts theme={"dark"}
import { type Connection, type PublicKey } from '@solana/web3.js';
import {
  chainNowSeconds,
  decodeReferralConfig,
  decodeReferralRecord,
  referralConfigPda,
  referralEarnings,
  referralRecordPda,
  type ReferralEarnings,
} from '@rekt-trading/1000x';

/** Null when the wallet has no record. */
export async function readEarnings(connection: Connection, referrerWallet: PublicKey): Promise<ReferralEarnings | null> {
  const [record, config] = await connection.getMultipleAccountsInfo(
    [referralRecordPda(referrerWallet).address, referralConfigPda().address],
    'confirmed',
  );
  if (!record || !config) return null;
  return referralEarnings(
    decodeReferralRecord(new Uint8Array(record.data)),
    decodeReferralConfig(new Uint8Array(config.data)),
    await chainNowSeconds(connection), // the chain's time, never your machine's
  );
}
```

| Field | Meaning |
| - | - |
| `claimable` | What a claim pays now: credited less claimed. |
| `accrued`, `claimed` | Lifetime credited, lifetime claimed. |
| `tierBps` | The record's tier. |
| `rateNow.rateBps` | The rate on the next settle. |
| `revenueDriven` | The record's running total that sets the tier. |
| `nextThreshold` | The next tier line, its rate and the revenue still to drive; `null` at the top. |
| `referees` | The wallets linked to the record. |
| `refreshOwed` | The total has crossed a tier line that the record does not show yet. Send `refreshReferral([toHex(record)])`; anyone can. |

### Claim

The record's authority signs. `destination` is a USDC token account, for example the wallet's
associated token account. An amount of `0n` claims everything credited and not yet claimed.

```ts theme={"dark"}
import { type PublicKey } from '@solana/web3.js';
import { claimReferral, toHex, type BuiltInstruction } from '@rekt-trading/1000x';

export function claim(authority: PublicKey, record: PublicKey, destination: PublicKey, amount = 0n): BuiltInstruction {
  return claimReferral({ authority: toHex(authority), record: toHex(record), destination: toHex(destination) }, amount);
}
```

`setReferralAuthority({ authority, record }, newAuthority)` hands the record to another wallet.

### Settling your own positions

A settle applies the discount and the share only when it carries the referral set: the owner's
link, the record it names and the referral vault. Pass the set as the `referral` argument of
`ix.settle`, and compose the transaction through the table `chooseSettleTable` picks: a settle with
the set fits only through the shared referral table.

```ts theme={"dark"}
import { type Connection, type PublicKey } from '@solana/web3.js';
import {
  chooseSettleTable,
  decodeReferralLink,
  referralLinkPda,
  referralSettleAccounts,
  toHex,
  type ChosenMarketTable,
  type ReferralSet,
} from '@rekt-trading/1000x';

/** The set for a book's owner, or null when the owner has no link. */
export async function referralSetFor(connection: Connection, owner: PublicKey): Promise<ReferralSet | null> {
  const info = await connection.getAccountInfo(referralLinkPda(owner).address, 'confirmed');
  if (info === null) return null;
  return referralSettleAccounts(toHex(owner), decodeReferralLink(new Uint8Array(info.data)).record);
}

/** The lookup table for the settle: the shared referral table when the set rides. */
export function settleTable(connection: Connection, market: PublicKey, set: ReferralSet | null): Promise<ChosenMarketTable> {
  return chooseSettleTable({
    market,
    referral: set !== null,
    required: [],
    fetchTable: async (address) => (await connection.getAddressLookupTable(address)).value,
  });
}
```

Then build the settle as on [settling](https://docs.rekt.money/sdk/settling.md#3-build-the-close) and pass the set:
`ix.settle(accounts, seq, reason, feeds, set)`. A settle of a linked book without it can be refused
(`ReferralAccountsRequired`, 6306); one that is not refused applies no discount and credits no share. For a Minute round the owner is the player account, so read the player's link.

### Events

`decodeReferralEvent(bytes)` decodes any referral event and returns `null` for any other event.
`decodeReferralAccrued`, `decodeReferralDiscounted` and `decodeReferralClaimed` decode one each.

## Refusals

| Error | Code | When |
| - | - | - |
| `ReferralClosed` | 6305 | Registration or linking is closed. |
| `ReferralAccountsRequired` | 6306 | A settle of a linked book left out the referral accounts. Pass the link, the record and the referral vault. |
| `ReferralSelfLink` | 6307 | The wallet tried to link to its own record. |
| `ReferralRecordDisabled` | 6308 | The record is disabled. Link to another record. |
| `NotReferralAuthority` | 6311 | The signer is not the record's authority. |
| `ReferralClaimDestinationRefused` | 6322 | The claim named one of the three refused accounts. Name another USDC account. |
| `ReferralClaimTooLarge` | 6323 | The claim asked for more than is credited and not yet claimed. |

A second link for the same wallet fails before the program's own checks run, because the link
already exists.

Questions: [support@asymmetra.xyz](mailto:support@asymmetra.xyz).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.