> ## 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.

# Profile

> User profile and account information

```typescript theme={null}
import { MonacoSDK } from "@0xmonaco/core";

// Initialize SDK with wallet client (see Initialization guide)

// Profile data
await sdk.profile.getProfile();

// Get the first page of balances (cursor paged: follow nextPageToken)
await sdk.profile.getUserBalances({ pageSize: 20 });

// Get balance for a specific asset (by UUID)
await sdk.profile.getUserBalanceByAssetId('123e4567-e89b-12d3-a456-426614174000');

// Transaction history with optional filters
await sdk.profile.getPaginatedUserMovements({ pageSize: 20 });
await sdk.profile.getPaginatedUserMovements({ entryType: "CREDIT", transactionType: "DEPOSIT" });
await sdk.profile.getPaginatedUserMovements({ assetId: "123e4567-e89b-12d3-a456-426614174000" });

// Trade history (cursor-paginated by default; follow nextPageToken)
await sdk.profile.getUserTrades({ pageSize: 20 });
await sdk.profile.getUserTrades({ tradingPairId: '123e4567-e89b-12d3-a456-426614174000' });

// Funding payment history (margin positions)
await sdk.profile.listFundingPayments({ pageSize: 20 });
await sdk.profile.listFundingPayments({ tradingPairId: '123e4567-e89b-12d3-a456-426614174000' });

// Sub-accounts (master account)
await sdk.subAccounts.list();

// Testnet faucet (a valid Cloudflare Turnstile token is required on every mint)
await sdk.faucet.mint({ turnstileToken: "<turnstile-token>" });
```

## Profile Data

<ResponseField name="getProfile" type="() => Promise<UserProfile>">
  Retrieve the authenticated user's core profile metadata.

  **Returns:**

  * `id`: Unique user identifier
  * `address`: Wallet address
  * `username`: Display name (nullable)
  * `accountType`: Account type (e.g. `"master"`)
  * `canWithdraw`: Whether the account can withdraw funds
  * `takerFeeBps`: **Deprecated.** Flat taker fee rate in basis points; echoes the pre-tiered pair-level rate, not the per-user tiered fee. Use `fees.getMyFeeTier` for the caller's real rate
  * `makerFeeBps`: **Deprecated.** Flat maker fee rate in basis points; echoes the pre-tiered pair-level rate, not the per-user tiered fee. Use `fees.getMyFeeTier` for the caller's real rate
  * `applicationTakerFeeBps`: Additional taker fee in basis points contributed by the application, if any (optional)
  * `applicationMakerFeeBps`: Additional maker fee in basis points contributed by the application, if any (optional)
  * `createdAt`: Account creation timestamp (ISO 8601)
</ResponseField>

## Token Balances

<ResponseField name="getUserBalances" type="(params?) => Promise<GetUserBalancesResponse>">
  Fetch paginated token balances for the authenticated user

  Cursor-paginated; there is no page-number (offset) mode.

  **Parameters:**

  * `pageSize` (optional): Number of items per page, 1 to 1000 (default: 20)
  * `pageToken` (optional): Pagination cursor:

    * **omitted** or **`""` (empty string)** — start a cursor walk from the lowest token address
    * **a previous `nextPageToken`** — resume the walk from that cursor

    There is no `page`: the SDK rejects one with a `ValidationError` before sending, and a raw REST request carrying it gets `400`. The walk seeks on `(token, assetId)` over the set the server builds after overlaying the matching engine's authoritative amounts, so a balance that appears or is zeroed mid-walk no longer shifts the rows behind it across the resume point.

  **Returns:**

  * `balances`: Array of AccountBalance objects
  * `pageSize`: Items per page
  * `total`: Total number of balances — exact, the set is bounded by the asset universe
  * `totalPages`: Total number of pages
  * `nextPageToken`: Cursor for the next page, `""` once the walk is exhausted. Each page over-fetches one row, so an empty token is definitive — follow a non-empty token, stop on an empty one
</ResponseField>

<ResponseField name="getUserBalanceByAssetId" type="(assetId: string) => Promise<AccountBalance>">
  Get the user's balance for a specific asset by its UUID

  **Parameters:**

  * `assetId`: Asset UUID (e.g., '123e4567-e89b-12d3-a456-426614174000')

  **Returns:**

  * `token`: Token contract address
  * `symbol`: Token symbol
  * `decimals`: Token decimal places
  * `availableBalance`: Available balance for trading
  * `lockedBalance`: Balance locked in orders
  * `totalBalance`: Total balance (available + locked)
  * `version` (optional): Opaque, non-negative spot-row revision. Compare only for the same authenticated user, application, and token; higher is newer. Absent and 0 both mean unranked and never participate in ordering: the row predates this field or came from a producer that does not stamp it, so normalize both to the same thing and fall back to a full field comparison rather than ranking an unranked row as oldest. A server mid-rolling-deploy sends no key at all, and `undefined <= n` is false, so an un-normalized guard silently passes.
</ResponseField>

## Transaction History

<ResponseField name="getPaginatedUserMovements" type="(params?) => Promise<GetPaginatedUserMovementsResponse>">
  Fetch paginated ledger history — deposits, withdrawals, trades, fees, funding payments, liquidations, interest, rewards, and collateral transfers — with optional server-side filtering

  Cursor-paginated; there is no page-number (offset) mode.

  **Parameters:**

  * `pageSize` (optional): Number of items per page, 1 to 1000 (default: 20)
  * `pageToken` (optional): Pagination cursor:

    * **omitted** or **`""` (empty string)** — start a cursor walk from the newest row (the oldest under `orderBy: "ASC"`)
    * **a previous `nextPageToken`** — resume the walk from that cursor

    There is no `page`: the SDK rejects one with a `ValidationError` before sending, and a raw REST request carrying it gets `400`. The filters and `orderBy` must match the request that minted the token.
  * `orderBy` (optional): Sort direction for `createdAt` — `"ASC"` or `"DESC"` (default `"DESC"`)
  * `entryType` (optional): Filter by entry type — `LedgerEntryType`: `"CREDIT"`, `"DEBIT"`, `"LOCK"`, `"UNLOCK"`, `"FEE"`
  * `transactionType` (optional): Filter by transaction type — `TransactionType`: `"DEPOSIT"`, `"WITHDRAWAL"`, `"TRADE"`, `"FEE"`, `"FUNDING"`, `"LIQUIDATION"`, `"INTEREST"`, `"REWARD"`. Filter values and returned values are both upper-case (a funding row reports `transactionType: "FUNDING"`, `entryType: "DEBIT" | "CREDIT"`), and the server matches the filter case-insensitively. The lower-case forms are the live `movements` WebSocket shape, not this one
  * `assetId` (optional): Filter by asset UUID

  **Returns:**

  * `movements`: Array of `LedgerMovement` objects. A `funding` row is a collateral USD delta rather than a token transfer: `amount` is a magnitude with the direction in `entryType`, `decimals` is `0`, and `amountRaw` equals `amount`. It shares its `id` with the live `movements` WebSocket frame, so dedupe by `id` across the two feeds — but the two payloads are not identical: the REST row is upper-case and resolves `assetId`, `symbol` and `decimals` — falling back to the pair's quote asset when the margin account's collateral binding is unset, so these carry a real asset rather than a placeholder, while the WebSocket frame is lower-case and carries no resolved asset id. See [Account Balances](/developers/account-balances#movements)
  * `pageSize`: Items per page
  * `total`: Matching movements, bounded by the server count ceiling — a lower bound when `totalCapped` is `true`
  * `totalPages`: Pages at this `pageSize`, bounded by the same ceiling
  * `nextPageToken`: Cursor for the next page, `""` once the walk is exhausted. Each page over-fetches one row, so an empty token is definitive — follow a non-empty token, stop on an empty one. Do not drive the loop off `total`: a user past the ceiling would stop early
  * `totalCapped`: `true` when more than the ceiling matched, so `total` / `totalPages` are a lower bound; `false` when exact, including exactly the ceiling
</ResponseField>

## Portfolio Analytics

Portfolio analytics are documented on the [Portfolio Analytics](/sdk/typescript/portfolio) page. That page covers the authoritative `sdk.portfolio` methods as well as the older `sdk.profile.getPortfolioStats` and `sdk.profile.getPortfolioChart`.

## Trade History

<ResponseField name="getUserTrades" type="(params?) => Promise<GetUserTradesResponse>">
  Fetch trade history for the authenticated user, cursor-paginated; there is no page-number (offset) mode.

  **Parameters:** all optional

  * `pageSize`: Number of items per page (default: 20); up to 1000
  * `tradingPairId`: Filter by trading pair UUID
  * `pageToken`: Pagination cursor:
    * **omitted** or **`""` (empty string)** — start a cursor walk over full trade history (hot and archived rows merged), newest first
    * **a previous `nextPageToken`** — resume the walk from that cursor

  **Returns:**

  * `trades`: Array of `UserTrade` objects
  * `pageSize`: Items per page
  * `nextPageToken`: Cursor for the next page — non-empty whenever a page returns rows, empty when the walk is exhausted
  * `total`: Trades a full walk returns — every trade you were maker or taker on, recent and archived, with a self-trade counted once. Bounded by the server count ceiling (10,000): a lower bound when `totalCapped` is `true`. Absent when `tradingPairId` is set or the count ran out of its server time budget; treat absent as unknown, not zero
  * `totalPages`: Pages at this `pageSize`, from the same bounded count. Absent exactly when `total` is
  * `totalCapped`: `true` when more than the ceiling matched, so `total` / `totalPages` are a lower bound; `false` when exact, including exactly the ceiling, and when `total` is absent

  The count runs only on an unfiltered walk: no index leads with both the user and the pair, so counting one pair would read your whole trade history. With `tradingPairId`, build "load more" on `nextPageToken`. Either way, drive the paging loop off `nextPageToken`, not `total`: past the ceiling, `total` stops at 10,000, and the count reads a replica, so for a moment it can disagree with the walk: a trade executed moments ago can be on the page before it is counted, and one being archived can be counted while the walk skips it.

  **`UserTrade` fields:**

  * `tradeId`: Trade unique identifier
  * `tradingPairId`: Trading pair UUID
  * `price`: Execution price (normalized string)
  * `quantity`: Executed quantity (normalized string)
  * `quoteVolume`: Price × quantity (normalized string)
  * `side`: `"BUY"` or `"SELL"` from the user's perspective
  * `fee`: Total fee paid for this trade (normalized string): `monacoFee + builderFee`. For a taker, Monaco's taker fee plus the frontend's builder fee; for a maker, the maker fee (negative for a rebate)
  * `monacoFee` (optional): Monaco's component of `fee` (normalized string)
  * `builderFee` (optional): The frontend's builder fee component of `fee` (normalized string); always `"0"` for a maker, since builder fees are charged to takers only
  * `executedAt`: Trade execution timestamp (ISO 8601)
</ResponseField>

<ResponseField name="iterateUserTrades" type="(params?) => AsyncGenerator<UserTrade>">
  Walk full trade history with cursor pagination, yielding one trade at a time. Accepts `tradingPairId` and `pageSize`; the cursor is managed internally.

  ```typescript theme={null}
  for await (const trade of sdk.profile.iterateUserTrades()) {
    // process trade
  }
  ```
</ResponseField>

## Funding Payments

<ResponseField name="listFundingPayments" type="(params?) => Promise<ListFundingPaymentsResponse>">
  Fetch the authenticated user's funding payment history for margin positions, with pagination and optional filters.

  Cursor-paginated; there is no page-number (offset) mode.

  **Parameters:**

  * `pageSize` (optional): Number of items per page, 1 to 1000 (default: 20)
  * `pageToken` (optional): Pagination cursor:

    * **omitted** or **`""` (empty string)** — start a cursor walk from the newest payment
    * **a previous `nextPageToken`** — resume the walk from that cursor

    There is no `page`: the SDK rejects one with a `ValidationError` before sending, and a raw REST request carrying it gets `400`. The walk seeks on `(createdAt, id)` descending — not `periodEnd`, which the removed mode led its sort with and which the risk engine leaves unset when a payment carries no window. The filters must match the request that minted the token.
  * `tradingPairId` (optional): Filter by trading pair UUID
  * `positionId` (optional): Filter by margin position UUID
  * `marginAccountId` (optional): Filter by margin account UUID

  **Returns:**

  * `records`: Array of `FundingPayment` objects
  * `pageSize`: Items per page
  * `total`: Matching payments, bounded by the server count ceiling — a lower bound when `totalCapped` is `true`
  * `totalPages`: Pages at this `pageSize`, bounded by the same ceiling
  * `nextPageToken`: Cursor for the next page, `""` once the walk is exhausted. Each page over-fetches one row, so an empty token is definitive — follow a non-empty token, stop on an empty one. Do not drive the loop off `total`: a user past the ceiling would stop early
  * `totalCapped`: `true` when more than the ceiling matched, so `total` / `totalPages` are a lower bound; `false` when exact, including exactly the ceiling

  **`FundingPayment` fields:**

  * `id`: Funding payment UUID
  * `positionId`: Margin position UUID
  * `marginAccountId`: Margin account UUID
  * `tradingPairId`: Trading pair UUID
  * `fundingRate`: Funding rate applied for the epoch (normalized string)
  * `positionSize`: Absolute position size at settlement (normalized string)
  * `paymentAmount`: Signed funding amount; positive means paid, negative means received (normalized string)
  * `direction`: `"PAID"` or `"RECEIVED"`
  * `periodStart`: Funding window start timestamp (ISO 8601, optional)
  * `periodEnd`: Funding window end timestamp (ISO 8601, optional)
  * `createdAt`: Funding payment creation timestamp (ISO 8601, optional)
</ResponseField>

## Self-Trade Prevention Default

<ResponseField name="getSelfTradePreventionDefault" type="() => Promise<SelfTradePreventionDefault>">
  Read the wallet's self-trade-prevention default — the mode the matching engine applies to an order that does not carry its own `selfTradePreventionMode`.

  **Returns:**

  * `mode`: `"CANCEL_MAKER" | "CANCEL_TAKER" | "CANCEL_BOTH" | "SKIP"` — the effective mode
  * `isCustom`: boolean — `true` when this wallet explicitly set a default, `false` when the platform default `CANCEL_MAKER` is being inherited

  The setting is **wallet-scoped**: one default per wallet address, shared by every application and account using it. The read serves an asynchronous projection, so a read issued immediately after `setSelfTradePreventionDefault` can briefly lag that call's own response.

  **Throws:**

  * REST `400` / gRPC `InvalidArgument` when the authenticated session's wallet is not a canonical EVM address (`0x` + 40 hex) — the setting is keyed by wallet, so a legacy non-EVM session has none to read
  * REST `401` / gRPC `Unauthenticated` when authentication is missing or invalid
  * REST `403` / gRPC `PermissionDenied` when a delegated-agent session calls it — an agent can neither read nor set this default
  * REST `429` / gRPC `ResourceExhausted` once read-budget enforcement is enabled: this is an authenticated read and draws your account's shared read budget, with the retry interval on the body's `details.retryAfter` or a gRPC `RetryInfo` detail. See [Rate limits](/developers/rate-limits)
  * REST `500` / gRPC `Internal` for an internal failure

  ```typescript theme={null}
  const stp = await sdk.profile.getSelfTradePreventionDefault();
  console.log(`Mode: ${stp.mode} (explicitly set: ${stp.isCustom})`);
  ```
</ResponseField>

<ResponseField name="setSelfTradePreventionDefault" type="(mode: SelfTradePreventionMode) => Promise<SelfTradePreventionDefault>">
  Set the wallet's self-trade-prevention default. See [Self-Trade Prevention](/developers/trade#self-trade-prevention) for what each mode does.

  **Parameters:**

  * `mode`: `"CANCEL_MAKER" | "CANCEL_TAKER" | "CANCEL_BOTH" | "SKIP"` — the SDK validates against these exact uppercase tokens and throws before sending, so pass them verbatim. The lowercase forms the server would accept never reach it

  **Returns:** the setting now in force, same shape as `getSelfTradePreventionDefault`.

  The write is sequenced through the matching engine and returns only after the durable append, so the wallet's **next order already resolves the new mode**. A per-order `selfTradePreventionMode` always overrides this default.

  The call sets the mode outright rather than applying a delta, so sending the same `mode` again is safe and a retry after a lost response cannot land a different value than the one you asked for. Concurrent sets for one wallet are last-write-wins in sequencer order, so serialize them per wallet if the final mode matters, and re-read with `getSelfTradePreventionDefault` after a transient failure rather than assuming the write was lost.

  **Throws:**

  * `ValidationError` (exported by `@0xmonaco/types`) when `mode` is not one of the four tokens — thrown before any network request
  * REST `400` / gRPC `InvalidArgument` when a raw caller sends an unknown mode, or when the authenticated session's wallet is not a canonical EVM address (`0x` + 40 hex)
  * REST `401` / gRPC `Unauthenticated` when authentication is missing or invalid
  * REST `403` / gRPC `PermissionDenied` when a delegated-agent session attempts the change
  * REST `429` / gRPC `ResourceExhausted` when the per-account settings-write budget is spent (about one write per second, burst 10, shared with the copy trading lead trader and follow writes). Nothing was applied; retry after `retryAfter`. See [Rate Limits](/developers/rate-limits#settings-and-copy-trading-writes)
  * REST `500` / gRPC `Internal` for an internal failure
  * REST `503` / gRPC `Unavailable` when the matching engine is unreachable or the sequenced write fails transiently. Do not read it as proof the setting is unchanged — the outcome is **unknown**: `is_transient_me_failure` also classifies `DeadlineExceeded` and `Cancelled` as transient, so the sequenced write may have committed before the response was lost. Re-read with the getter, or simply retry the same `mode` — the call sets an absolute value, so a replay cannot land anything other than what you asked for

  ```typescript theme={null}
  const stp = await sdk.profile.setSelfTradePreventionDefault("SKIP");
  console.log(`Mode is now ${stp.mode}`);
  ```
</ResponseField>

## Sub-Accounts

Manage a master account's sub-accounts and their legacy per-asset spending limits. All
methods are session-authenticated. The limit mutations (`createLimit`,
`updateLimit`, `deleteLimit`) additionally require the master account to hold the
`ManageSubAccounts` permission — enforced server-side, so a non-master or
unpermissioned caller receives a 403.

<ResponseField name="subAccounts.list" type="() => Promise<ListSubAccountsResponse>">
  List the authenticated master account's sub-accounts with their balances.

  **Returns:**

  * `subAccounts`: Array of `SubAccount` objects, each with `id`, `address`, `username` (nullable), `canWithdraw`, `createdAt`, and `balances` (array of `AccountBalance`)
  * `total`: Total number of sub-accounts
</ResponseField>

<Warning>
  Sub-account spending limits are not enforced:

  * `createLimit` and `updateLimit` fail with `400`: `Sub-account spending limits are not enforced and can no longer be configured`.
  * `getLimits` and `deleteLimit` act only on limits created before that change.
  * All four methods are scheduled for removal.
</Warning>

<ResponseField name="subAccounts.createLimit" type="(body: CreateSubAccountLimitRequest) => Promise<CreateSubAccountLimitResponse>">
  Create a per-asset spending limit on a sub-account. Requires the `ManageSubAccounts` permission.

  **Body:**

  * `subAccountId`: Sub-account UUID to create the limit for
  * `assetId`: Asset UUID to limit
  * `maxAmount`: Maximum amount allowed, in token units (string)
  * `dailyLimit` (optional): Maximum daily spending limit, in token units (string)

  **Returns:** `{ limit }` — the created `SubAccountLimit`.
</ResponseField>

<ResponseField name="subAccounts.getLimits" type="(subAccountId: string) => Promise<GetSubAccountLimitsResponse>">
  Get the limits configured for a sub-account.

  **Parameters:**

  * `subAccountId`: Sub-account UUID

  **Returns:** `{ limits }` — array of `SubAccountLimit` objects.
</ResponseField>

<ResponseField name="subAccounts.updateLimit" type="(subAccountId: string, assetId: string, body: UpdateSubAccountLimitBody) => Promise<UpdateSubAccountLimitResponse>">
  Partially update a sub-account's per-asset limit. Requires the `ManageSubAccounts` permission. The sub-account and asset are taken from the path; all body fields are optional.

  **Parameters:**

  * `subAccountId`: Sub-account UUID
  * `assetId`: Asset UUID

  **Body (all optional):**

  * `maxAmount`: New maximum amount, in token units (string)
  * `dailyLimit`: New maximum daily spending limit, in token units (string)
  * `isActive`: Whether the limit is active

  **Returns:** `{ limit }` — the updated `SubAccountLimit`.
</ResponseField>

<ResponseField name="subAccounts.deleteLimit" type="(subAccountId: string, assetId: string) => Promise<void>">
  Delete a sub-account's per-asset limit. Requires the `ManageSubAccounts` permission. Resolves with no value.

  **Parameters:**

  * `subAccountId`: Sub-account UUID
  * `assetId`: Asset UUID
</ResponseField>

## Faucet

<ResponseField name="faucet.mint" type="(params: MintTokensParams) => Promise<MintTokensResponse>">
  Mint the full set of testnet tokens to the authenticated user. Testnet-only, session-authenticated, and rate-limited per wallet and application (default 1 request per rolling 24h; the same wallet has its own quota on each application). Performs real on-chain mints and resolves only when every token confirms on-chain; it throws on `401` (not authenticated), `403` (delegated-agent session), `429` (quota exhausted), `502` (mints failed or only partially confirmed — the error carries the minted/failed breakdown), `500` (internal error), or `503` (faucet unavailable, safe to retry). Wrap the call in a `try/catch`.

  **Parameters:**

  * `turnstileToken`: string (required) - Cloudflare Turnstile captcha token. Requests without a valid token are rejected with `400`.

  **Returns (on resolve):**

  * `minted`: Array of confirmed minted tokens, each with `assetId`, `symbol`, `amount`, `txHash`. A mint credits spot and margin separately, so a token can appear twice — once as `USDC` and once as `USDC (margin)`
  * `failed`: Array of tokens that failed to mint (`assetId`, `symbol`, `error`); empty on a successful resolve — populated on the thrown `502` error instead
  * `remainingRequests24h`: Remaining faucet requests in the next 24h
</ResponseField>

<ResponseField name="faucet.getInfo" type="() => Promise<GetFaucetInfoResponse>">
  Read the authenticated wallet's faucet quota on the calling application. Throws on `401` (not authenticated), `403` (delegated-agent session), `429` (read budget exhausted), `500` (internal error reading quota history), or `503` (quota configuration unavailable, safe to retry).

  **Returns:**

  * `remainingRequests24h`: Remaining faucet requests in the rolling 24h window
  * `dailyLimit`: The application's daily request limit
  * `nextAvailableAt` (optional): ISO 8601 time the next request frees; omitted when the window is empty or the limit is 0
  * `recentRequests`: Up to five newest-first request groups from the calling application. Each group holds the mints that share a minute — `requestedAt` is truncated to the minute — with a `minted` array (`assetId`, `symbol`, `amount`, optional `txHash`); it is a display grouping, and the quota counts each request individually
</ResponseField>


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