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

# V1.0.70

# Monaco Protocol SDK v1.0.70

This release adds **copy trading** — `sdk.copyTrading`, the owner-private `copy` WebSocket channel, React hooks and MCP tools — makes the order and trade listings **cursor-only**, and has order frames name the **margin position an order is bound to** before it fills. Margin order placement that names a `riskBucketId` answers differently for buckets you do not own and for copy trading risk buckets, the movements and funding-payment listings bound their totals by their own pagination reach, and idempotent order submission gets a per-owner receipt share. **Action items:** if you pass `page` to `getPaginatedOrders`, `listConditionalOrders`, `listTwapOrders` or `getUserTrades`, switch to `pageToken` — the SDK no longer accepts it and REST answers `400`; if you read `total` / `totalPages` from user trades, they are gone; and if you place margin orders with an explicit `riskBucketId`, handle the new `404` and `400` answers.

## Breaking

### Order and trade listings are cursor-only

`getPaginatedOrders`, `listConditionalOrders`, `listTwapOrders` and `getUserTrades` (`GET /api/v1/orders`, `GET /api/v1/orders/conditional`, `GET /api/v1/orders/twap`, `GET /api/v1/accounts/trades`; gRPC `OrdersService.ListOrders`, `ListConditionalOrders`, `ListTwapOrders`, `TradesService.ListUserTrades` and `AccountsService.GetUserTrades`) no longer accept `page`. Every call is a cursor walk: send `pageToken` (empty on the first request) and follow `nextPageToken`. `pageSize` has a single ceiling of 1000 on all of them, instead of 100 for page-number requests. REST answers `400` naming `pageToken` when a request still sends `page`, and gRPC drops the field as unknown.

The page-number response fields are removed: `page` from the four listing responses, and `total` / `totalPages` from the user-trades responses, whose table is too large to count. `ListOrdersResponse` keeps `total`, `totalPages` and `totalCapped`; the conditional-order and TWAP listings keep `total`, now populated on the cursor path and bounded at 10,000. A request that sends neither `page` nor `pageToken` used to read only recent rows; it now starts a cursor walk over recent and archived history, so it can return rows it previously could not reach. The `iterateOrders` and `iterateUserTrades` async generators already walk cursors and need no change. `@0xmonaco/types` drops `page` from `GetPaginatedOrdersParams`, `ListConditionalOrdersParams`, `ListTwapOrdersParams` and `GetUserTradesParams` and from their Zod schemas. See [Trades](/sdk/typescript/trades).

### `riskBucketId` answers on margin order placement

Margin orders that name a risk bucket by `riskBucketId` on `POST /api/v1/orders`, `POST /api/v1/orders/batch-create` and `POST /api/v1/orders/twap` (gRPC `OrdersService.CreateOrder`, `BatchCreateOrders`, `CreateTwapOrder`) answer differently:

* A `riskBucketId` that belongs to another user or application returns `404` `Risk bucket not found` (`NOT_FOUND`), exactly as an unknown id does, whatever `tradingPairId` or `marginAccountId` the request carries. Previously it returned `400` `riskBucketId does not belong to tradingPairId`, `400` `riskBucketId does not belong to marginAccountId`, or `404` `Margin account not found`.
* For your own risk bucket the pair and account checks are unchanged: `400` `riskBucketId does not belong to tradingPairId` / `riskBucketId does not belong to marginAccountId` (`INVALID_ARGUMENT`).
* A `riskBucketId` naming one of your risk buckets under a non-default `strategyKey` — your copy trading risk buckets included — returns `400` `riskBucketId names a copy trading risk bucket; copy risk buckets are managed by the engine` (`INVALID_ARGUMENT`). Only the copy engine trades copy trading risk buckets.

Omit `riskBucketId`, name one of your default-strategy risk buckets, or route strategy-keyed orders by `strategyKey` to place a user order. See [Trade](/developers/trade).

## Added

### Copy trading

`sdk.copyTrading` adds thirteen methods over `CopyTradingService` (REST under `/api/v1/copy-trading`). Public, with no session: `listLeadTraders` (sort `roi`, `pnl`, `followers`, `allocated`, `win_rate` or `aum`; window `7d`, `30d`, `90d` or `all`), `getLeadTrader` and `listLeadTraderClosedTrades`. Authenticated: `listLeadTraderOpenPositions` (current followers only), `upsertLeadTrader`, `getMyLeadTrader`, `listMyFollowers`, `upsertFollow`, `previewFollow`, `stopFollow`, `listMyFollows`, `getFollow` and `listFollowLeaderPositions`. `upsertLeadTrader` and an edit through `upsertFollow` replace the settings as sent, so an omitted `bio` or `slPct` is cleared. `previewFollow` (`POST /api/v1/copy-trading/follows/preview`) returns totals only — `estimatedMargin`, `estimatedFee`, `copiedPositions`, `skippedPositions` and `truncated` — and spends the order-risk preview budget; `upsertFollow` accepts `copyExisting` on a create. `getFollow` returns `copyAccountEquity`, the copy account's marked equity.

`sdk.ws.copy(handler, onSnapshot?)` subscribes to the authenticated follower's `copy` channel, carrying `copy_relationship_update` and `copy_skip` frames; `onSnapshot` receives the live follows. React adds `useCopyTrading()` and `useCopyEvents()`, and the MCP server adds the matching copy trading tools. Entry and growth — activating a lead trader, creating a follow, increasing an allocation — are gated by a server-side switch and are refused with `Copy trading is not available` while it is off; stops, pauses and closes are always admitted. See [Copy Trading](/developers/copy-trading).

`MarginAccountSummary` and `PortfolioRiskBucket` gain optional `copyFollowId` and `copyLeaderHandle`, present only on copy risk bucket rows, and order events gain optional `origin` (`USER`, `LIQUIDATION`, `SYSTEM`, `ADL` or `COPY`; absent means not reported) and `copyRelationshipId`. `realizedPnl` on a risk bucket row is the risk bucket's cash balance, not trading profit. See [Margin Accounts](/sdk/typescript/margin-accounts).

### Order frames name their margin position

`orders` events carry optional `positionId` (wire `position_id`), the margin position the order is bound to — the same id `getOrder` returns. A margin order placed while its margin account (cross) or risk bucket (isolated) already holds an open position in the pair is bound when it is admitted, so a resting close or reduce-only order names its position before it fills; otherwise its first fill binds it. It is omitted, never `null`, while the order is unbound, for spot, and by older servers. `OrderPlaced` adds `marginAccountId`, `leverage` and `reduceOnly` for margin orders, and `orders` snapshot rows add `postOnly`, `expirationDate`, `triggerPrice` and `positionSide`, as REST returns them. See [WebSockets](/sdk/typescript/websockets).

## Changed

### Movement and funding-payment totals are bounded by pagination reach

`sdk.profile.getPaginatedUserMovements()` and `sdk.profile.listFundingPayments()` (`GET /api/v1/accounts/movements`, `GET /api/v1/accounts/funding-payments`) return `total` exact up to `pageSize` × 10,000 matching rows and saturated there, and `totalPages` saturated at 10,000, the last page the listing can reach. Wallets under that reach see the same numbers as before; loops that stop at `page >= totalPages` keep terminating. See [Profile](/sdk/typescript/profile).

### Per-owner share for idempotency receipts

Create-order requests that carry an idempotency key share receipt capacity per owner: each user and application may hold at most 10,000 receipts (32 MiB of payload) that are not yet safe to evict — responses still awaiting durable acknowledgement or persistence. An owner at its share gets the existing retryable `ORDER_SUBMISSION_CAPACITY_EXCEEDED` (`503` / `UNAVAILABLE`) for new keys while every other owner keeps placing orders. Retries of an existing key and orders without a key are unaffected, and the share frees up as responses are persisted, expire after 24 hours, or are replaced.

## Fixed

### Delegated replacements check notional in base-asset units

Delegated sessions using `ReplaceOrder` (`PUT /api/v1/orders/{order_id}`) or `BatchReplaceOrders` (`POST /api/v1/orders/batch-replace`) without a `quantity` evaluate `maxOrderNotional` in base-asset units, inheriting the remaining order quantity (or the original quantity when the remaining one is unavailable). A delegated create or replace whose notional is too large to compute is refused with `403` `Delegated agent order notional limit exceeded` (`PERMISSION_DENIED`) instead of failing as a server error. The `POST /api/v1/delegated-agents` types now mark `agentAddress`, `allowedActions` and `allowedTradingPairIds` as required, matching the server. See [Delegated Agents](/developers/delegated-agents).

### Funding history lists only charged windows

`GET /api/v1/market/pairs/{trading_pair_id}/funding/history` and `GET /api/v1/market/funding/history` (gRPC `MarketService.ListFundingHistory`, `ListAllFundingHistory`) list a settlement only once the risk engine has applied it, so every listed window was charged. A window the engine refuses is never charged later, so history can skip that window's time span while epochs stay consecutive. Rows written before this release are unchanged.

## Upgrade

```bash theme={null}
npm install @0xmonaco/core@1.0.70 @0xmonaco/types@1.0.70 @0xmonaco/react@1.0.70
# or bun
bun add @0xmonaco/core@1.0.70 @0xmonaco/types@1.0.70 @0xmonaco/react@1.0.70
```

Code that passes `page` to `getPaginatedOrders`, `listConditionalOrders`, `listTwapOrders` or `getUserTrades`, or reads `page` from their responses or `total` / `totalPages` from user trades, no longer type-checks — switch to `pageToken` / `nextPageToken`. Every other new field is optional.
