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

# TraderCodes

> Integrate TraderCodes referral codes into your Monaco application.

TraderCodes, the referral program of PitPass, give every Monaco user a unique referral code tied to their wallet. When a referred user trades, the referrer earns a share of the protocol fee from that trade — across a referral chain up to 10 levels deep. The number of levels and their rates can be configured per client application, and a trade pays the levels of the client it was placed through. Rewards land in an app-agnostic rewards bucket and can be moved into any trading balance with a single ledger-only call.

Every wallet has a default code derived from its address, and a user can optionally claim a **custom (vanity) handle** — a human-readable code that is easier to share.

Use TraderCodes to capture a referral at signup, display a user's code for sharing, let users claim a custom handle, and let them check and move their earned rewards.

## Surface Map

| Surface | Use |
| - | - |
| REST | `GET /api/v1/tradercodes/codes/me`, `GET /api/v1/tradercodes/codes/{code}`, `PUT /api/v1/tradercodes/codes/me/custom`, `DELETE /api/v1/tradercodes/codes/me/custom`, `GET /api/v1/tradercodes/codes/available`, `GET /api/v1/tradercodes/rewards/balance`, `POST /api/v1/tradercodes/rewards/transfer`, `GET /api/v1/tradercodes/referrals/me`, `GET /api/v1/tradercodes/referrals/me/downline`, `GET /api/v1/tradercodes/referrals/me/tree` |
| TypeScript SDK | `sdk.tradercodes.getMyTraderCode()`, `sdk.tradercodes.getTraderCodeInfo(code)`, `sdk.tradercodes.setTraderCode(code)`, `sdk.tradercodes.clearTraderCode()`, `sdk.tradercodes.checkTraderCodeAvailability(code)`, `sdk.tradercodes.getRewardsBalance()`, `sdk.tradercodes.transferRewards(params)`, `sdk.tradercodes.getMyReferralPosition()`, `sdk.tradercodes.getMyReferralDownline(params?)`, `sdk.tradercodes.getMyReferralTree(params?)` |
| React SDK | `useMyTraderCode()`, `useSetTraderCode()`, `useClearTraderCode()`, `useTraderCodeAvailability()`, `useRewardsBalance()`, `useTransferRewards()`, `useMyReferralPosition()`, `useMyReferralDownline()`, `useMyReferralTree()`, or any core SDK method through `useMonacoSDK()` |
| WebSocket | None |
| gRPC | `monaco.api.tradercodes.TraderCodeService` |

## Concepts

**TraderCode.** Every wallet has a default code derived from its address, displayed by clients as `Monaco - <address>`. Codes are globally unique, work across every app on Monaco, and the default is permanent. Calling `getMyTraderCode` is idempotent — the code row is created on demand, so the endpoint never 404s for a signed-in wallet. Render the response's `display` field rather than building the display string yourself; it already reflects a custom handle when one is set.

**Custom handle.** A wallet can claim a custom (vanity) handle to use in place of the wallet-derived default: letter-first, `[A-Za-z0-9_]`, 5–20 characters, stored exactly as entered (casing preserved) but unique case-insensitively. Claim or edit it with `setTraderCode`, drop it with `clearTraderCode` (reverting to the default), and preview a handle with the public `checkTraderCodeAvailability`. `getMyTraderCode` reports `isCustom` and the claimed `customCode` alongside the default `code`. The referral chain always keys on the wallet, so changing or clearing a handle never affects earned rewards or referral relationships.

**Referral chain.** A referral relationship is recorded once at first signup. The chain is a directed graph: L1 is the direct referrer, L2 is their referrer, L3 is L2's referrer, and so on. TraderCodes rewards a configured number of levels, at most 10, and each rewarded level earns its own configured rate of the fee generated by the referred user's trades (see [Reward rates](#reward-rates)). Levels and rates can be configured per client application; a trade pays the levels of the client it was placed through.

**Reading referral state.** Three self-scoped, authenticated reads back the referral UI. `getMyReferralPosition()` returns your `upline` — the chain above you, nearest first: L1 (who referred you), L2, and so on, with as many entries as the levels your trades on this client application pay (never more than 10) — plus your direct referrer as `referrer` (`""` when you were not referred; `upline` is empty then, and also when this client application pays no referral levels), your all-time `totalEarned`, and recent reward rows (up to the 100 newest; `totalEarned` stays the complete aggregate) — use it for an "already referred" state and an earnings header. `getMyReferralDownline(params?)` returns one page of `earningsBySource` (who pays you, across every reward level, ordered by each source account's total, largest first), your L1 `directReferees` roster (including referees who have not traded yet, showing up to the 500 most recent effective referees (after per-wallet dedupe), drawn from a bounded scan so a very large history may yield an incomplete roster — the earnings views stay complete), and a per-level `summary`. `getMyReferralTree()` renders your full downline as a tree, one level per call: with no `parent` it lists your L1 referees; pass one of their wallets as `parent` to list its referees (L2), and so on down to the deepest level any client application rewards, since a wallet in your downline pays you at the levels of the client it trades through. Your L1 referees are always listed. Each node carries its `level`, `parent`, `referredAt`, `refereeCount` (expand it when above 0; always 0 at the deepest rewarded level), and `totalEarned` (what you earned from that wallet's own trades). Pages hold up to 200 nodes (default 50) with a `nextPageToken`. Each wallet appears once, under the referrer that earns from it, and any `parent` that is not you or one of your referees above the deepest rewarded level returns 404. All three identify every other user by wallet address plus a `display` (their custom handle when claimed), never an internal id, and only ever return your own chain.

**Paging the downline.** `earningsBySource` is paged: pass `pageSize` (1–500, default 100) and `pageToken` (the opaque `nextPageToken` from the previous response) — as SDK params, or as query parameters on `GET /api/v1/tradercodes/referrals/me/downline`. A call without paging parameters returns only the **first page** (up to 100 rows), not every row — keep requesting with the returned `nextPageToken` until it is `""`. Rows are ordered by each source account's total; rows of a wallet that traded through several applications are merged within a page at its first part's position, so a merged row can sit below a smaller one, and that wallet can appear again on a later page. `summary` always covers every level and source, whatever the page: `summary.byLevel` lists `{ level, earned }` (raw atomic units) in ascending level order, one entry per level you have earnings at, including levels deeper than TraderCodes rewards today. `summary.level1Earned`, `level2Earned`, and `level3Earned` are deprecated — they cover levels 1–3 only; use `byLevel`.

```typescript theme={null}
const first = await sdk.tradercodes.getMyReferralDownline({ pageSize: 500 });
const rows = [...first.earningsBySource];
let pageToken = first.nextPageToken;
while (pageToken) {
  const page = await sdk.tradercodes.getMyReferralDownline({ pageSize: 500, pageToken });
  rows.push(...page.earningsBySource);
  pageToken = page.nextPageToken;
}

for (const { level, earned } of first.summary.byLevel) {
  console.log(`L${level}: ${earned}`);
}
```

**Rewards bucket.** Earned rewards land in a dedicated per-user rewards bucket that is separate from any application's trading balance. The bucket is app-agnostic — a referrer earns rewards regardless of which app their referee trades on. To spend rewards, move them into a trading balance via `transferRewards`.

**Transfer.** The transfer between rewards bucket and trading balance is ledger-only (Monaco operates a single shared vault). No on-chain transaction, no gas. Funds are tradeable immediately after the transfer.

## Flow

### 1. Capture a referral at signup

Pass the referral code in the auth verify body. The code is silently ignored if it is invalid or if the user already exists, so it never blocks sign-in. The trading front-end captures `?ref=CODE` from the URL and forwards it here.

<Steps>
  <Step title="Validate the code (optional)">
    Before showing the user a confirmation, call the public lookup to verify the code resolves.

    ```typescript theme={null}
    const info = await sdk.tradercodes.getTraderCodeInfo(code);
    // info.code is the normalized wallet address
    ```

    ```bash theme={null}
    curl "https://staging.apimonaco.xyz/api/v1/tradercodes/codes/0xabc..."
    ```

    Returns `404` when the code does not resolve to a known wallet.
  </Step>

  <Step title="Pass the code on verify">
    Include `referralCode` in the auth verify request.

    ```typescript theme={null}
    await sdk.auth.authenticate("688a21440e304c06b935f743dae88278", code);
    ```

    ```bash theme={null}
    curl -X POST "https://staging.apimonaco.xyz/api/v1/auth/verify" \
      -H "Content-Type: application/json" \
      -d '{
        "sessionPublicKey": "...",
        "signature": "...",
        "referralCode": "0xabc..."
      }'
    ```

    For returning users the field is ignored. Self-referrals are silently dropped.
  </Step>
</Steps>

### 2. Get and display a user's TraderCode

Fetch the authenticated user's code to display it in the UI as a shareable link or copyable string.

```typescript theme={null}
await sdk.login("688a21440e304c06b935f743dae88278");

const { code, display, isCustom } = await sdk.tradercodes.getMyTraderCode();
// `display` is ready to show ("Monaco - <address>", or the custom handle when set).
// `code` is the wallet-derived value to embed in a referral URL: ?ref=<code>
console.log(display, isCustom);
```

```bash theme={null}
curl "https://staging.apimonaco.xyz/api/v1/tradercodes/codes/me" \
  -H "X-Monaco-PublicKey: $MONACO_SESSION_PUBLIC_KEY" \
  -H "X-Monaco-Timestamp: $MONACO_TIMESTAMP" \
  -H "X-Monaco-Signature: $MONACO_SIGNATURE"
```

Response:

```json theme={null}
{
  "code": "0xabc123...",
  "createdAt": "2026-07-01T00:00:00Z",
  "customCode": "",
  "isCustom": false,
  "display": "Monaco - 0xabc123..."
}
```

When the wallet has claimed a custom handle, `customCode` holds it (as entered), `isCustom` is `true`, and `display` is the handle.

### 3. Claim a custom handle (optional)

Let a user replace the wallet-derived default with a vanity handle. Preview availability as they type with the public check, then claim it. The handle is stored exactly as entered, must be letter-first `[A-Za-z0-9_]` and 5–20 characters, and is unique case-insensitively.

```typescript theme={null}
// Live availability feedback while the user types (public, no auth).
const { available, reason } = await sdk.tradercodes.checkTraderCodeAvailability("acePilot");
if (!available) {
  // reason ∈ "taken" | "too_short" | "too_long" | "reserved" | "invalid_char" | "looks_like_address"
  console.log(`Unavailable: ${reason}`);
}

// Claim or edit (authenticated). The server is the authoritative gate.
const { display, isCustom } = await sdk.tradercodes.setTraderCode("acePilot");
console.log(display, isCustom); // "acePilot", true

// Revert to the wallet-derived default.
await sdk.tradercodes.clearTraderCode();
```

```bash theme={null}
curl "https://staging.apimonaco.xyz/api/v1/tradercodes/codes/available?code=acePilot"

curl -X PUT "https://staging.apimonaco.xyz/api/v1/tradercodes/codes/me/custom" \
  -H "Content-Type: application/json" \
  -H "X-Monaco-PublicKey: $MONACO_SESSION_PUBLIC_KEY" \
  -H "X-Monaco-Timestamp: $MONACO_TIMESTAMP" \
  -H "X-Monaco-Signature: $MONACO_SIGNATURE" \
  -d '{ "code": "acePilot" }'
```

`setTraderCode` maps rejections to `409` (handle already taken, or a recently-freed handle still in cooldown), `422` (validation, with a reason code), `429` (edited too frequently), and `403` (custom handles are disabled). `clearTraderCode` counts against the same rolling-hour edit limit, so it can also return `429` or `403`. The public availability check is rate-limited and returns `429` once a caller exceeds the allowance — treat it as advisory feedback, not the authoritative gate.

### 4. Check rewards balance

Read accumulated rewards across all reward tokens. Returns an empty list if no rewards have been earned yet.

```typescript theme={null}
const { balances } = await sdk.tradercodes.getRewardsBalance();

for (const entry of balances) {
  console.log(`${entry.token}: ${entry.available} raw units`);
}
```

```bash theme={null}
curl "https://staging.apimonaco.xyz/api/v1/tradercodes/rewards/balance" \
  -H "X-Monaco-PublicKey: $MONACO_SESSION_PUBLIC_KEY" \
  -H "X-Monaco-Timestamp: $MONACO_TIMESTAMP" \
  -H "X-Monaco-Signature: $MONACO_SIGNATURE"
```

Response:

```json theme={null}
{
  "balances": [
    {
      "token": "0xusdc...",
      "available": "1200000"
    }
  ]
}
```

`available` is in raw atomic units. Divide by the token's decimals (e.g., `10^6` for USDC) to display a human-readable amount.

### 5. Transfer rewards to a trading balance

Move earned rewards from the rewards bucket into the authenticated application's trading balance. The transfer is instant and ledger-only — no on-chain transaction. Each call spends the per-user [movement budget](/developers/rate-limits) that withdrawals and collateral transfers share, whatever the rewards balance; past it the answer is `429` with `Retry-After` once that budget is enforced.

```typescript theme={null}
const result = await sdk.tradercodes.transferRewards({
  token: "0xusdc...",
  amount: "1200000", // raw atomic units, must be a positive integer string
});

console.log(result.rewardsBalance);  // remaining in rewards bucket after transfer
console.log(result.tradingBalance);  // new trading balance for this token
```

```bash theme={null}
curl -X POST "https://staging.apimonaco.xyz/api/v1/tradercodes/rewards/transfer" \
  -H "Content-Type: application/json" \
  -H "X-Monaco-PublicKey: $MONACO_SESSION_PUBLIC_KEY" \
  -H "X-Monaco-Timestamp: $MONACO_TIMESTAMP" \
  -H "X-Monaco-Signature: $MONACO_SIGNATURE" \
  -d '{
    "token": "0xusdc...",
    "amount": "1200000"
  }'
```

Returns `409` when the requested amount exceeds the available rewards balance.

## Reward Rates

Monaco configures how many referral levels TraderCodes rewards — from 1 up to a hard maximum of 10 — and a separate reward rate for each level. Levels and rates can be configured per client application, and a trade pays the levels and rates of the client it was placed through. Reward amounts are a share of the protocol fee generated by the referred user's trades:

| Level | Relationship |
| - | - |
| L1 | Your direct referral's trades |
| L2 | Trades of the users your L1 referrals referred |
| L3 – L10 | Each further step down the chain, up to the configured depth |

* Every level takes its rate of the same reward base for the fill; levels do not compound on each other. The rates of all levels together never exceed 100% of that base.
* Rewards are computed at fill time and credited instantly to the referrer's rewards bucket. Each reward row records the `rateApplied` at that fill.
* Levels the chain does not reach — where it is shorter than the configured depth — are simply not paid.
* Configuration changes apply to future fills only. Rewards already earned at a level deeper than the current depth stay readable in your reward history, `earningsBySource`, and `summary.byLevel`.

## Builder Checklist

* Capture `?ref=CODE` from the URL before the user completes sign-in, and pass it as `sdk.auth.authenticate(clientId, code)` or via the raw auth verify endpoint — `sdk.login` does not forward a referral code.
* Validate the code with `getTraderCodeInfo` before showing a confirmation to the user; return a clean message on `404` instead of an error.
* Never block sign-in on referral-code validation failures — a bad code is silently ignored server-side, but front-end validation errors should not prevent login.
* Render the `display` field from `getMyTraderCode` rather than composing the display string yourself, and read `isCustom` to reflect whether a vanity handle is set.
* In a custom-handle claim form, debounce `checkTraderCodeAvailability` for live feedback, but always handle `409`/`422`/`429` on `setTraderCode` — the server is the authoritative gate, not the advisory check.
* Display `available` reward balances in human-readable form by dividing by the token's decimal precision.
* Handle `409` on `transferRewards` by refreshing the balance and showing the user their current available amount.

## Deprecated PitPass Names

The TraderCodes surface was first published under PitPass names. They still work and return exactly what the TraderCodes names return, because the same handlers serve both, but they are deprecated and will be removed in a later release. Move to the TraderCodes names.

| Deprecated | Use instead |
| - | - |
| REST `/api/v1/pitpass/...` (`codes/me`, `codes/me/custom`, `codes/available`, `codes/{code}`, `rewards/balance`, `rewards/transfer`, `referrals/me`, `referrals/me/downline`, `referrals/me/tree`) | The same sub-path under `/api/v1/tradercodes/...` |
| gRPC `monaco.api.pitpass.TraderCodeService` | `monaco.api.tradercodes.TraderCodeService` (same RPCs and messages) |
| `sdk.pitpass` | `sdk.tradercodes` (the same object) |
| `PitpassAPI` type (`@0xmonaco/types`) | `TraderCodesAPI` |

## Related References

* [Earn](/earn) for the product overview of TraderCodes and BuilderCodes
* [Auth](/developers/accounts/wallets) for the full sign-in flow
* [TypeScript TraderCodes SDK](/sdk/typescript/tradercodes) for complete method signatures
* [REST — Get your TraderCode](/api-reference/tradercodes/get-my-trader-code) for the generated schema
* [REST — Look up a TraderCode](/api-reference/tradercodes/get-trader-code-info) for the generated schema
* [REST — Get rewards balance](/api-reference/tradercodes/get-rewards-balance) for the generated schema
* [REST — Transfer rewards](/api-reference/tradercodes/transfer-rewards) for the generated schema


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