Skip to main content
Package: monaco.api.managed_markets Source: protos/api/managed_markets.proto Use ManagedMarketsService to drive the maker side of an operator-managed market launch: read the launches this wallet is assigned to, hold a short lease that fences one process at a time, and ask the engine to restate a quote set whose prices, sizes, fees and risk bucket are all chosen server-side. It never places a quote the caller describes and never moves funding.

Notes

  • All three RPCs require an authenticated wallet session, and a delegated-agent session is refused on every one of them with PERMISSION_DENIED (REST 403) — permanently, not as a rollout state. The maker requirement is narrower: ClaimLease and RefreshQuotes are launch-scoped and need a session whose user and application both match that launch’s configured maker, while ListAssignments accepts any authenticated wallet and filters by it, so a non-maker gets an empty page rather than an error.
  • owner_id is a process fence, never authorization. It is a UUID the caller chooses to identify the process holding the lease; authorization is derived solely from the authenticated session’s user and application binding and never from an identity in the request body, so no owner_id widens what a caller can reach. A value that is not a UUID returns INVALID_ARGUMENT, as does a non-UUID launch_id on ClaimLease or RefreshQuotes, a non-positive quote_revision, and ListAssignments pagination outside page 1..=10000 or page_size 1..=100.
  • A different owner_id cannot seize a live lease. claim_maker admits a claim only when the lease is unset, already expired, or held by that same owner, so a standby claiming under a new owner_id gets ALREADY_EXISTS (REST 409) until the current lease lapses. Takeover is “wait out the remaining lease, then claim”. A claim after expiry increments maker_generation, which is what makes the previous holder’s next refresh fail as stale. A claim by the current owner splits on expected_generation: supplied, it renews through renew_maker and moves the expiry out; omitted while the lease is still unexpired, it neither claims nor renews — it replays the existing reservation and returns its original expiry, so a loop that always omits it will let its own lease lapse.
  • A launch this session is not the configured maker for answers NOT_FOUND, not PERMISSION_DENIED, so the service never confirms the existence of a launch the caller has no part in. ListAssignments is the exception in shape rather than in rule: it filters by the caller, so a wallet with no assignments gets an empty page rather than any error. PERMISSION_DENIED is reserved for delegated-agent sessions.
  • generation and quote_revision are uint64 fences. Over gRPC they are uint64 fields; over REST they are exact decimal strings. A JSON client should keep the wire value as text but must not order or increment it as text — "10" < "2" lexically — and must not round-trip it through a double, which loses values above 2^53. Convert to an exact 64-bit integer type (BigInt in JavaScript) for every comparison and increment.
  • ListAssignments returns expected inventory, not a ready-to-quote set: a row with quote_enabled = false is still assigned and still listed, but ClaimLease and RefreshQuotes on it return ALREADY_EXISTS (REST 409, Managed market is not ready for maker quotes) until the launch is ready to quote: its launch phase is warming, activating or live, its runtime phase is warming or live, and its allocated collateral has reached the configured allocation in full. activating counts — a maker must keep quoting through activation, which is the phase that depends on those quotes. Its lease field is declared on the message but never populated: the released handler serializes it as absent on every row, deliberately, because the database reservation cannot prove the engine’s current generation or quote revision and a fabricated zero would read as a usable fence. ClaimLease is the only source of a lease a caller may act on.
  • The two phase fields are different vocabularies. On ManagedMarketAssignment and ManagedMarketLease it is the launch phase: pending, validating, registering, warming, activating, live or failed. On ManagedMarketQuoteStatus it is the engine’s runtime phase and takes only warming, live or halted; anything else the engine reports is answered as UNAVAILABLE with Managed market status unavailable; reconcile the lease.
  • ClaimLease reserves the lease, fences it in the matching engine, then rechecks ownership before replying, so the reply is the engine’s view rather than the database reservation — a lease that expired in flight fails instead of being reported as held. The lease runs for 30 seconds. expected_generation is required when renewing an unexpired lease you hold and omitted for an initial claim or after expiry.
  • An initial claim whose reply was lost replays its existing durable reservation instead of incrementing generation, so a transport retry never costs you the lease you already hold.
  • RefreshQuotes places one bounded bid and one bounded ask. quote_revision must be positive (INVALID_ARGUMENT otherwise) and at least the revision the engine holds for that generation: a lower one is refused, and re-sending the one it already holds is an accepted idempotent no-op. Derive it from the ClaimLease reply rather than starting at 1 — a renewal keeps the generation’s existing revision, so 1 restates the quote set when the held revision is exactly 1 and is refused once it has advanced past it. Only a claim that bumped the generation resets the revision to 0.
  • The engine also enforces the launch’s own cadence, refusing a refresh that advances the revision less than refresh_interval_ms after the last one. That is a rejection rather than a dropped no-op, so pace the loop on the assignment’s refresh_interval_ms. An equal-revision retry is exempt — quote_intent returns on the equality check before it reaches the interval check — so an idempotent replay succeeds inside the window.
  • Two ALREADY_EXISTS paths, and the engine’s own refusals are indistinguishable. The handler re-checks the launch’s persisted readiness before calling the engine, so a launch already recorded as halted or otherwise not quotable answers Managed market is not ready for maker quotes. Everything the engine itself refuses is folded by handlers::managed_markets::engine_errorABORTED, ALREADY_EXISTS, FAILED_PRECONDITION and INVALID_ARGUMENT all become one ALREADY_EXISTS carrying Managed market lease changed or expired; reconcile the lease — so a stale revision, an early refresh, an expired lease and a market halted since the readiness check are one status and one message. Both paths are ALREADY_EXISTS: do not branch on the text, reconcile through ClaimLease and act on the generation and revision it returns.
  • Cost: ClaimLease admits one item and RefreshQuotes two against the caller’s ordinary order-creation budget, and ListAssignments draws the per-account read budget like every other authenticated read, charged before the handler runs. Those two budgets are the only sources of RESOURCE_EXHAUSTED here — the engine’s cadence refusal is not one of them; it arrives as the folded ALREADY_EXISTS below. Either way it carries a google.rpc.RetryInfo detail — the same interval REST returns as retryAfter — so honor that rather than guessing. See Rate limits.
  • ALREADY_EXISTS (REST 409) also covers Managed market lease changed or expired; reconcile the lease: the lease moved to another owner or generation, or lapsed. Reconcile through ClaimLease and read the engine-observed generation and quote_revision back before continuing.
  • Treat a RefreshQuotes timeout or UNAVAILABLE as an unknown outcome, not a rejection: the intent may have reached the engine. Reconcile through ClaimLease and take the engine’s quote_revision rather than advancing your own and retrying blind.
  • UNAVAILABLE (REST 503) means the matching engine could not be reached or answered with a status this service cannot act on. INTERNAL is a generic server-side failure and can happen before the engine is contacted at all, so unlike UNAVAILABLE it says nothing about engine state.
  • Quote intents are server-derived end to end: a fixed preallocated risk bucket, server-chosen prices, sizes and the application’s canonical taker fee. The request carries no price, quantity or identity that could override any of it, and no path here funds an account.