Skip to content

fix(quoter-bot): ignore matured configured markets - #186

Merged
haydenshively merged 4 commits into
mainfrom
hermes/mkt-2187-do-not-throw-if-market_ids-contains-post-maturity-markets
Sep 1, 2026
Merged

fix(quoter-bot): ignore matured configured markets#186
haydenshively merged 4 commits into
mainfrom
hermes/mkt-2187-do-not-throw-if-market_ids-contains-post-maturity-markets

Conversation

@prd-carapulse

@prd-carapulse prd-carapulse Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Why

A market ID can stay configured after its Midnight market reaches maturity. Treating that expected lifecycle state as a setup invariant failure stopped the quoter, instead of letting the other configured markets keep quoting while the matured one is cleaned up.

What changed

Setup no longer treats maturity as an invariant (identity, allowlisting, loan asset, and tick spacing are still checked), and the latest-block timestamp — whose only use was the maturity comparison — is removed end to end, so an RPC timestamp outage can no longer fail readiness:

-getLatestTimestamp(): Promise<bigint>                 // SetupStateService + viem impl + provider op
-booksCheck(config, timestamp, books)
+booksCheck(config, books)                             // no timestampProviderError, books fails closed

Production setup reads market metadata instead of active books. /v0/midnight/books excludes past maturities even when IDs are requested, so a matured configured market could never be found; ViemSetupStateService.getBook now reads /v0/midnight/markets (chain_ids + market_ids) concurrently with the on-chain toMarket/tickSpacing reads.

Both writer workflows recognize maturity from a fresh read and stop before offer arithmetic. Maturity and the timestamp it is compared against come from the same read, so no workflow owns a clock:

export const marketObservationMatured = (o: { maturityTimestamp?: bigint; observedTimestamp?: bigint }) =>
  o.maturityTimestamp !== undefined && o.observedTimestamp !== undefined &&
  o.observedTimestamp >= o.maturityTimestamp
  • Ladder: runOnce settles a matured market before reference-rate and ladder generation — make.reconcile({ desired: undefined, reason: 'market-matured' }) invalidates the owned groups and the market reports the non-failing matured action. buildLadderTree therefore never throws LadderAdapterError('market-matured'), so a cycle is not failed and runContinuously does not halt every other market.
  • Bootstrap: a matured market reports the non-failing matured action right after the position read, before any reference read or time-to-maturity arithmetic (no negative durations). Its groups can no longer be filled and are cancelled by the monitor's shutdown cleanup.
  • Ladder maturity now comes from midnight.getMarketData(marketId) plus the fresh block, replacing the group-derived ladderMarketMaturity helper (deleted), which returned nothing for a market with no owned groups.

Verification

  • pnpm --filter @morpho-org/quoter-bot run typecheck, pnpm lint, pnpm format, pnpm knip, pnpm --filter @morpho-org/quoter-bot run jsdoc:build — all clean
  • pnpm --filter @morpho-org/quoter-bot exec vitest run --exclude 'test/e2e/**' — 1170 passed (e2e suites need RPC_URL_8453; the remaining repo-wide pnpm test failures are the unbuilt @repo/contracts entry, both preexisting locally)
  • New coverage: matured market invalidated while another market publishes in the same cycle, monitoring continuing into later cycles, reconciliation failure during maturity cleanup (ladder); matured market observed as matured while another market publishes, and later-cycle continuation (bootstrap); adapter-level matured-market metadata read with an empty /books response

Closes MKT-2187

Link to Devin session: https://app.devin.ai/sessions/56a26bd3b0bc412eb931ea282d3c8036
Open in Devin Desktop: https://app.devin.ai/desktop/session/56a26bd3b0bc412eb931ea282d3c8036?variant=devin
Requested by: @haydenshively

Allow configured post-maturity markets to remain in MARKET_IDS without failing the setup readiness gate.

Co-authored-by: Julien Thomas <61523188+julien-devatom@users.noreply.github.com>
@linear-code

linear-code Bot commented Aug 31, 2026

Copy link
Copy Markdown

MKT-2187

devin-ai-integration[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

@prd-carapulse

prd-carapulse Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

<@U09LHAWUK3M> review

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Addressed in 88b210d. Maturity is now detected before offer generation, so buildLadderTree never sees a matured market: LadderQuoterService.runOnce checks the fresh readMarket() observation (maturityTimestamp + observedTimestamp from the same read) and, when matured, invalidates the owned ladder groups through make.reconcile({ desired: undefined, reason: 'market-matured' }) and returns a non-failing matured outcome, so other configured markets keep quoting and runContinuously proceeds into later cycles. Bootstrap does the same before any rate read or time-to-maturity arithmetic (non-failing matured action). New coverage: multi-market cycles with one matured market plus a market that still publishes, later-cycle continuation, and reconciliation-failure handling during maturity cleanup (ladder + bootstrap suites).

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Addressed in 88b210d. The latest-timestamp readiness dependency is gone end to end: getLatestTimestamp() removed from SetupStateService and its viem implementation, removed from the concurrent setup reads, booksCheck(config, books) no longer takes a timestamp, timestampProviderError propagation and its transient-retry branch are deleted, and the books check now fails closed with the other compound checks. An RPC timestamp outage can no longer affect readiness.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Addressed in 88b210d — the timestamp read, parameter, failure propagation, and its transient-retry classification are all removed (including ViemSetupStateService.getLatestTimestamp and the 'latest-timestamp' provider operation), so a now-irrelevant RPC failure cannot fail readiness.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Good catch — addressed in 88b210d. ViemSetupStateService.getBook no longer uses /v0/midnight/books (active-only); it reads immutable metadata from /v0/midnight/markets (chain_ids + market_ids) concurrently with the on-chain toMarket/tickSpacing reads, so matured configured markets resolve. Adapter-level coverage in test/infrastructure/setup-state/viem-setup-state.service.test.ts exercises a matured market whose /books response is empty.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Addressed in 88b210d. Rather than filtering at setup, the writer workflows now handle the lifecycle state, which also covers markets that mature while the bot runs: the ladder invalidates owned groups for a matured market (reason: 'market-matured', desired: undefined) and reports a non-failing matured outcome; bootstrap reports matured before any offer arithmetic. Tests assert a matured market with nonzero capacity/desired rungs no longer reaches buildLadderTree, that a second configured market still publishes in the same cycle, and that runContinuously keeps monitoring later cycles.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)

Devin Review

@devin-ai-integration
devin-ai-integration Bot force-pushed the hermes/mkt-2187-do-not-throw-if-market_ids-contains-post-maturity-markets branch from a7fdaf7 to 3f71ab0 Compare September 1, 2026 06:20
@haydenshively
haydenshively merged commit 9a7b84e into main Sep 1, 2026
5 checks passed
@haydenshively
haydenshively deleted the hermes/mkt-2187-do-not-throw-if-market_ids-contains-post-maturity-markets branch September 1, 2026 16:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants