Skip to main content

Monaco Protocol SDK v1.0.73

This release adds frontend builder-fee controls and change history, bounds resident TP/SL orders, and hardens withdrawals, margin provisioning, market-price protection and oracle valuation. It also clarifies what an order replacement’s updatedFields reports, without changing its wire shape. Action items: if you maintain many conditional exits, handle the new cardinality refusals and cancel unused legs; if you integrate BuilderCodes, use the payout-wallet session and distinguish the stored fee from the fee currently charged.

Added

Set a frontend’s builder fee and read its change history

sdk.buildercodes.setBuilderFee({ bps }) (PUT /api/v1/buildercodes/builder-fee; gRPC BuildercodeRewardsService.SetBuilderFee) stores an additional taker fee for the authenticated frontend. bps is required, an integer from 0 through 100, and a real change must not exceed the live builderFeeCapBps. An explicit zero clears the fee. The caller must use a non-delegated frontend application session whose wallet is the application’s current payout address, with BuilderCodes enabled; server keys are not accepted. An enrolled frontend may make two real BUILDER changes in a rolling 24 hours; ADMIN changes do not count. Setting the stored value again succeeds without a change-log row, email or consumed allowance, even when no changes remain or the cap has since fallen below that stored value. A missing or nonmatching payout address fails the ownership gate first with 403 / PERMISSION_DENIED; incomplete enrollment reached after that gate passes and an exhausted allowance return 409 / FAILED_PRECONDITION. A change-limit response includes details.reason: "builder_fee_change_limit" and details.nextChangeAllowedAt. Invalid or above-cap changes return 400 / INVALID_ARGUMENT. sdk.buildercodes.listBuilderFeeChanges(params) (GET /api/v1/buildercodes/builder-fee/changes; gRPC BuildercodeRewardsService.ListBuilderFeeChanges) walks newest-first change history with pageToken / nextPageToken, default pageSize 20 and maximum 100. Rows include actorType ("BUILDER" | "ADMIN"), actor, oldBps, newBps and createdAt; an ADMIN actor is the operator’s email. getConfig() (GET /api/v1/buildercodes/config; gRPC BuildercodeRewardsService.GetBuildercodesConfig) adds builderFeeEnabled, stored builderFeeBps, effective builderFeeCapBps, changesRemaining and nullable/absent nextChangeAllowedAt. A fee can be stored while builder fees are disabled. Charging depends on that separate gate and enrollment, and is capped at the live cap; changes reach new orders within seconds, while resting orders retain their admitted fee. This release does not activate BUILDER_FEE payout credits. React hooks and MCP tools intentionally omit these frontend configuration operations. See BuilderCodes.

Changed

Conditional-order cardinality is bounded

Position-attached TP/SL and trailing-stop creates are checked against resident conditional-order caps: defaults are 10 active legs per position, 100 resident legs per user on an engine shard (including pending entry-order legs), and 100,000 resident legs on that shard. Deployments can override these limits. Entry-order TP/SL placement checks the user and shard caps before admission; position attaches also check the position cap. Exceeding a cap refuses the create without creating its new legs. Entry-order placement reports 400 / INVALID_ARGUMENT (or a per-item batch rejection), but the released position-attach adapter reports an engine cap refusal as generic 500 MATCHING_ENGINE_ERROR / gRPC INTERNAL. Not every internal error means a cap was reached; inspect and cancel unused legs before repeating a known over-cap attach. A full-close TP/SL attach counts the same-type full-close legs it supersedes as retiring, so moving an existing level at the cap does not consume an extra slot. Conditional trigger scanning now rotates through bounded batches rather than repeatedly inspecting the same subset. See Conditional Perps Orders.

Replacement fields report the resulting order

ReplaceOrderResponse.updatedFields and each batchReplace result report the replacement’s price and total quantity, whether those inputs changed or were omitted. Repricing a 60/100-filled order with no quantity reports quantity: "100" and rests 40; read remainingQuantity for the resting size. Margin reduce-only replacements keep their close-size meaning. This corrects descriptions and JSDoc, with no wire or runtime change. See Replace Orders.

Fixed

Withdrawals cannot pay back into the vault or regress after execution

sdk.withdrawals.initiateWithdrawal (POST /api/v1/withdrawals; gRPC WithdrawalsService.InitiateWithdrawal) rejects the configured vault address as destination with 400 / INVALID_ARGUMENT, before debiting funds. A transfer there would leave the tokens in the vault without crediting a recipient. Recording a delayed withdrawal proof also preserves an already-executed withdrawal’s terminal status instead of moving it back to confirmed. See Low-Level Withdrawals.

Margin provisioning reports overload as unavailable

When automatic parent margin account creation or cross risk bucket provisioning is refused because the sequencer is full, the API returns retryable 503 / UNAVAILABLE instead of 400 / INVALID_ARGUMENT. Back off before retrying. Other engine refusals retain their existing classification. See Error Handling.

Market protection and margin valuation stay consistent

The spot market-order reference counts one price per liquidity-taking order, at its last fill, including after restart; a multi-level sweep cannot contribute several votes to the five-order median. See Market-order price protection. Margin collateral transfers now resolve token decimals and symbol from the engine’s asset registry, require USDC-family collateral and an exact raw/normalized amount pair, and cannot restate a live account or risk bucket’s lifecycle state. Ordinary SDK gateways already send the required collateral shape. Oracle marks that would push a position’s notional, margin requirements or unrealized PnL outside the supported valuation range are refused without replacing the last accepted mark; position-margin changes are held to the same bounds, and read-time arithmetic no longer panics on unrepresentable values.

Oracle delivery supports streams and managed SEDA feeds

Pyth-primary markets can receive prices over WebSocket, with HTTP polling taking over when a stream stops advancing. Managed markets can use a SEDA-served Pyth Pro feed with an explicitly configured Hyperliquid on-close fallback coin, selected when the upstream market is shut rather than on any Pyth failure during an open session; the price source’s session informs market policy. Fallback metadata determines the coin’s index and rejects missing or delisted coins, but choosing a fallback for the same asset as the Pyth feed remains the operator’s responsibility. Availability depends on deployment configuration. Clients continue to use the existing mark/index prices and market-status surfaces. See Markets.

Upgrade

The WebSocket reference’s helper, wire-channel and reconnect lists also now include the existing authenticated copy subscription. This corrects an omission; it is not a new channel in this release.
The SDK additions are source-compatible. Handle conditional-order cap refusals, keep withdrawal destinations distinct from the vault, and retry provisioning overloads with backoff. BuilderCodes configuration remains rollout-gated, and stored builder fees do not imply active charging or builder-fee payout credits.