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

# Monaco Protocol SDK v1.0.53

This release covers the consumer-facing changes since v1.0.51. Three migrations come first: switch sweeper derivation to the factory's `forwarder` without exposing a new address for an existing registration until its stored row is migrated, remove `selectedTradingPairIds` from cross risk-bucket funding, and treat simulated `estimatedFee` as a signed whole-order estimate that may be absent. Order creation gains durable idempotency keys, stateful WebSocket subscriptions gain typed baseline callbacks, and portfolio margin rows identify their own scope. Order admission, triggered-close event delivery, first-use isolated simulation, and isolated-position backing are also corrected.

## Breaking

### Sweeper addresses now derive through the factory forwarder

`predictSweeperAddress` and `sweeperCloneInitCode` now take the factory's `forwarder` address instead of its Sweeper `implementation`. `SweeperAddressInput.implementation` is renamed to `forwarder`, and every derived address changes.

Read the input from `factory.forwarder()`. Fresh registrations persist and monitor the new address. An existing registration created before v1.0.53 needs an operator-side stored-row migration before you display or fund its new address: calling register returns and monitors the forwarder-derived address in the running resolver, but does not update that row's stored `sweeperAddress`, so a resolver restart reloads only the old address. Re-registration alone is not restart-durable. The forwarder is fixed while it resolves the current implementation per call, so later logic upgrades do not move deposit addresses. `sweeperImmutableArgs(user, clientId, hubToken?)` also gains the optional hub-token input; user-facing deposit addresses use the zero address.

The regenerated `SWEEPER_ABI` and `SWEEPER_FACTORY_ABI` include the new multi-chain entrypoints and configuration surface. See [Registered Deposit Addresses](/sdk/typescript/vault#registered-deposit-addresses).

### Cross risk-bucket funding no longer declares market scope

For `marginMode: "CROSS"`, `sdk.marginAccounts.transferCollateralToRiskBucket` now accepts only `{ asset, amount, marginMode: "CROSS" }`. Remove `selectedTradingPairIds`: the TypeScript SDK rejects it before sending, and `transfer_collateral_to_risk_bucket` removes it from the MCP tool.

Cross scope is derived from trading. A pair joins when a cross order on it succeeds. Removal after its positions close is lazy: the pair remains listed until a later successful cross order on a different pair reconciles the bucket, so a funding call has no scope to replace. Read current membership from the cross row returned by `listMarginAccounts()` or from the transfer response. The raw REST field remains deprecated for compatibility and is ignored in cross mode; the generated gRPC field is deprecated. `simulateRiskBucketOrderRisk` is unchanged and still requires the pair list in cross mode.

This also lets parent-margin simulation and pair-filtered margin summaries resolve a never-traded pair to the active cross risk bucket. See [Margin Accounts](/sdk/typescript/margin-accounts#transfer-collateral-to-a-risk-bucket).

### `estimatedFee` is a signed whole-order estimate

`estimatedFee` on all three authenticated order-risk simulation responses now estimates fees for the order's complete size: taker pricing, including the application taker fee, for the quantity that would cross immediately; maker pricing for the quantity that would rest. A fully crossing order is unchanged.

The field stays an optional string, so this change does not produce a compile error:

* A negative value is a net maker rebate. Do not parse or render it as an unsigned cost.
* Absence means the engine could not price the order. Treat it as unknown, never zero.
* The estimate remains available for reduce-only orders and can be present when `accepted` is false. It assumes a resting remainder eventually fills.

The affected REST endpoints are `POST /api/v1/margin/accounts/{margin_account_id}/simulate-order-risk`, `POST /api/v1/margin/parent-margin-account/simulate-order-risk`, and `POST /api/v1/margin/risk-buckets/simulate-order-risk`; the matching gRPC methods are on `MarginAccountsService`. See [Order Risk Simulation](/sdk/typescript/margin-accounts#simulate-order-risk).

## Added

### Durable order-submission idempotency

Single order create and each batch-create item accept an optional `idempotencyKey`. For 24 hours after acceptance, retrying the same normalized payload with the same key returns the original committed submission result without executing again, including after the order fills, is cancelled, or the matching engine restarts. Reusing the key with different details returns REST `409 IDEMPOTENCY_KEY_CONFLICT` / gRPC `ALREADY_EXISTS`.

The TypeScript SDK generates a UUID when the key is omitted, including one per batch item. That protects one SDK invocation; a later call receives a new key. Generate, persist, and reuse an explicit key when recovery must survive another call or process restart. `clientOrderId` remains a separate reusable correlation label and cannot prevent duplicate execution.

Raw REST, generated Rust/Python, and gRPC callers must generate their own keys. The field remains optional during this rollout; requests rejected before acceptance do not reserve it. See [Safe Retries with Idempotency Keys](/developers/trade#safe-retries-with-idempotency-keys).

### Typed WebSocket baseline callbacks

Stateful `sdk.ws` subscriptions accept an optional trailing `onSnapshot` callback. It receives the subscribe-time baseline as one typed array, separate from the live event handler. Use the array to replace baseline state; reducing it as live deltas can double-apply data. Empty state arrives as `onSnapshot([])`, malformed arrays are withheld rather than delivered partially, and reconnect resubscription re-arms the callback.

The server currently sends baselines for orders, user orders, balances, conditional orders, positions, and instruments. The other stateful method signatures are ready for future server arms; their callbacks remain inert until those arms ship. Event streams (`orderbook`, `ohlcv`, `trades`, and `movements`) have no `onSnapshot`.

`@0xmonaco/types` adds `SnapshotHandler<T>` and `OrderSnapshotItem`. `useInstruments`, `useUserBalances`, and `useUserOrders` now seed and repair state from these baselines, including reconnect omissions and version-ranked order reconciliation. See [Subscribe-Time Snapshots](/sdk/typescript/websockets#subscribe-time-snapshots).

### Portfolio risk-bucket rows identify their scope

Each `PortfolioRiskBucket` returned by `sdk.portfolio.getMargin()` now includes required `marginMode` (`"ISOLATED" | "CROSS"`) and optional `tradingPairId`, present only for isolated rows. Callers can identify each portfolio row's mode and isolated-pair scope without another lookup. Cross multi-market membership is not carried by this response; read `selectedTradingPairIds` from the cross risk-bucket row returned by `sdk.marginAccounts.listMarginAccounts()`.

The response's `positions.cross` and `positions.isolated` counts span all of the user's margin accounts, while `riskBuckets` belongs to the oldest parent account. Do not treat grouping the rows by `marginMode` as a checksum over those counts. See [Portfolio Margin](/sdk/typescript/portfolio#get-margin).

## Changed

### Matching-engine calls have bounded server deadlines

The matching-engine's internal gRPC listener now defaults to 1,024 concurrent handlers and HTTP/2 streams per connection, a 30-second deadline for ordinary RPCs, and a 600-second deadline for batch-order RPCs. State-snapshot capture stays single-flight and has no deadline by default so large proposer bootstraps can finish.

For public API callers, an internal deadline expiry is classified as REST `503` / gRPC `Unavailable`. A mutating request has an **unknown outcome** at that point: submitted sequencer or blocking work may still commit after the response times out. Reconcile state before retrying. For order creation, reuse the same `idempotencyKey`; never substitute a new key for an unresolved submission. Keyed `batchCreate` is the exception to the transport status: it returns overall REST `200` / gRPC `OK` and reports an expired item as `results[].error.code === "SERVICE_UNAVAILABLE"`. The TypeScript SDK keys every item, so inspect every result and retain the original key for any item you retry.

## Fixed

### Small collateral gaps no longer discard otherwise admissible orders

A bucket-scoped resting maker fill can now cover a collateral shortfall from the parent's unallocated capacity, capped at the position's `initialMarginRequired - maintenanceMarginRequired`. The resting remainder of a partially crossing taker can fund only deterioration caused by that order since admission; it stops at the same liquidation bound and is also capped by the remaining admission budget — parent capacity for an existing bucket, or the unused fee-reserve allowance for a bucket the order created. Previously a small adverse mark move could silently remove the maker or refuse the taker remainder even when repair was admissible. The tightest cap and liquidation freezes still win; there is no client switch to enable.

### Triggered TP/SL closes publish the full execution event tail

A take-profit or stop-loss close now publishes the same orderbook, order, trade, OHLCV, position, and balance updates as an ordinary execution. Previously the close could execute while subscribers received the conditional-order trigger but missed some downstream state updates. Continue subscribing to both conditional-order events and the ordinary state channels: the former confirms the trigger, while the latter carries execution and resulting state. See [Conditional Order Lifecycle](/developers/trade#conditional-order-lifecycle).

### First-use isolated order-risk simulation works before a bucket exists

`simulateRiskBucketOrderRisk({ marginMode: "ISOLATED", ... })` no longer fails when the trading pair has no isolated risk bucket yet. The engine models the bucket virtually with the collateral a real first order would attach, so `equityAfter` and `freeCollateralAfter` describe the bucket that placement would create. An account unknown to the risk engine because it has never been funded still reports `margin account not found`.

### Open isolated positions report their current backing

`isolatedMargin` on open isolated positions now reports the position's current backing: allocated risk-bucket collateral adjusted by settled realized PnL and funding, net of collateral reserved for open orders, and clamped at zero. The previous value was stale posted margin after partial closes or partial liquidations. REST reads, gRPC reads, the positions snapshot, and live `position_update` frames agree while live risk state is available; a persisted fallback is gross of open-order reserves. Cross positions and terminal rows retain their existing meanings. See [Positions](/sdk/typescript/positions#read).

## Upgrade

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

Before upgrading, pause deposits for every pre-v1.0.53 sweeper registration until Monaco confirms its stored row was migrated; then derive its address from `factory.forwarder()`. Remove `selectedTradingPairIds` from cross funding, and update fee displays for negative and absent `estimatedFee` values. Persist explicit idempotency keys when retries cross calls or restarts, and consume `onSnapshot` as a replaceable baseline rather than as live events.


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