feat(deepbook-v3): support the margin surface against Pyth's upgraded Core - #1194
Merged
Conversation
… Core Pyth is replacing Core with a separately published Sui package rather than upgrading it in place, so its `PriceInfoObject` is a distinct Move type that the existing margin entrypoints can never accept — their signatures are frozen by the `compatible` upgrade policy. `deepbook_margin` therefore exposes the upgraded surface as parallel modules (`margin_manager_upgraded`, `pool_proxy_upgraded`) and `margin_liquidation` as parallel entrypoints (`liquidate_base_upgraded`, `liquidate_quote_upgraded`), under the same function names: callers switch module, not function name. The SDK follows without changing its own surface. A `marginPyth` setting (`'legacy' | 'upgraded'`) decides which module each builder targets and whether it passes `priceInfoObjectId` or the new `priceInfoObjectIdUpgraded`; price feed pushes follow the same setting via a new `pythUpgraded` state config. Entrypoints that take no oracle stay on the base modules in both modes, because the upgraded modules do not carry them. Both networks ship as `'legacy'`, which is byte-identical to today's behaviour — the existing PTB snapshots pass unchanged. Neither network can serve the upgraded surface yet: the margin packages carrying it are not deployed, and neither upgraded Pyth deployment has an object for every feed its `MarginRegistry` is configured with. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tonylee08
deployed
to
sui-typescript-aws-kms-test-env
August 11, 2026 18:18 — with
GitHub Actions
Active
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…rmes v2 Testnet margin was pinned to v14, two upgrades behind; margin_liquidation to v3. Move both to the packages that carry the upgraded-Pyth surface — deepbook_margin v16 (0xce6ab629) and margin_liquidation v4 (0x29bccec5). Switch price update fetches from the deprecated Hermes v1 /api/latest_vaas to v2 /v2/updates/price/latest. Both return the same accumulator payload — one message covering all requested feeds, not one per feed — so this is not a behaviour change today, but v1 is being retired. Add PythConfig.hermesHeaders, forwarded to every Hermes request. The endpoint serving Pyth's upgraded Core requires an Authorization header and answers 401 without one, so the update path was unusable in 'upgraded' mode. No credential ships with the SDK; consumers supply it at runtime. Verified end to end on testnet: fetched signed update data through this path and pushed it to the upgraded Core BTC/USD PriceInfoObject (0x0fcd3e0c…), which went from 33 days stale to fresh (tx Bpkvp8NNH36KjJ8rZjEj1N5ZJaxLsbSwc5sTUmGWvBa). Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
tonylee08
deployed
to
sui-typescript-aws-kms-test-env
August 11, 2026 20:01 — with
GitHub Actions
Active
…nt fallback Follow-up to the review of the previous commit, which shipped three defects. The proxy fallback constant was a literal placeholder host. Because testnet now defaults to 'upgraded' and supplies no hermesHeaders, that placeholder became the default endpoint for every testnet price update, failing as an opaque `TypeError: Invalid URL` from inside axios. It is now `undefined` until the proxy exists, and the upgraded path throws a ConfigurationError naming the field to set. Added a test that every shipped endpoint constant is a parseable URL or absent. Testnet coins carried an upgraded `feed` next to a legacy `priceInfoObjectId` — two different deployments in one pair, so a forced legacy call would fetch one deployment's update data for the other's object. Testnet now carries the upgraded identity only; the beta feed ids and legacy price objects are removed, so legacy mode fails immediately naming the coin. Feed lookup moved behind config.getFeedId(), replacing the `coin.feed!` assertions. getPriceInfoObjects dropped a coin whenever two coins shared a feed id — the reverse index kept only the last key, and the caller got undefined for the other. Testnet DBTC prices off the generic BTC/USD feed, so this is reachable. Feeds are now deduplicated before the update is built (a repeated feed would also emit two update_single_price_feed calls against one object and pay two fees), and the index maps a feed to every coin using it. Also: export the Hermes constants, drop an orphaned doc comment, and correct the changeset, which still claimed both networks ship 'legacy'. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
tonylee08
deployed
to
sui-typescript-aws-kms-test-env
August 11, 2026 20:41 — with
GitHub Actions
Active
…rror Second review pass. No behaviour defects this time; the fixes are accuracy and diagnosability. Verified all six mainnet priceInfoObjectIdUpgraded values against the upgraded price table on chain — every one matches, including USDSUI, which had no corroborating record anywhere. The changeset said "five"; it ships six, now enumerated. The three Pyth examples built a client with no upgraded config, so against testnet — which now defaults to 'upgraded' — they threw on the first stale feed. They now attach credentials from PYTH_TOKEN when present. newCoinTypeData was the last direct `coin.feed!` read; it threw 'Coin feed not found' without naming the coin. Routed through config.getFeedId(). Docs: #hermesEndpoint claimed a proxy fallback that currently throws, and noted that supplying hermesHeaders without an endpoint sends the token to Pyth's own host; a doubled JSDoc opener; and a block that had drifted onto getFeedId, leaving getPriceInfoObjectId undocumented. The dedup comment implied an in-repo feed collision that does not exist. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
tonylee08
deployed
to
sui-typescript-aws-kms-test-env
August 11, 2026 20:56 — with
GitHub Actions
Active
…e only Legacy Core is being retired, so carrying both deployments was short-lived complexity. Removed: the marginPyth switch, the pythUpgraded config, the priceInfoObjectIdUpgraded field, and every legacy price object id. `pyth` is now the upgraded deployment's state objects and `priceInfoObjectId` its price object; the four routing getters return the upgraded modules unconditionally. Entrypoints that take no oracle argument still resolve to the base modules, which is the only place they exist. That split is what the rewritten routing test pins, since it is no longer expressible as a mode. BREAKING: this requires the upgraded margin package on the target network. The upgraded modules do not exist in earlier deepbook_margin publications, so the release must follow the mainnet package upgrade rather than precede it. Marked major. Verified on chain that every shipped (feed, priceInfoObjectId) pair belongs to the upgraded Pyth package on its network — 4 testnet, 6 mainnet. Mainnet XBTC had kept its legacy object while carrying a comment claiming it had none; it now correctly has no object, so pricing it throws until one is created. Examples take the upgraded config and a PYTH_TOKEN when present. They are not covered by tsconfig (include: src), so they were checked separately. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
tonylee08
deployed
to
sui-typescript-aws-kms-test-env
August 11, 2026 21:08 — with
GitHub Actions
Active
… to be created DBTC is testnet's wrapped BTC, so it should take the same feed mainnet XBTC does. It was pointed at plain BTC/USD only because no XBTC price object existed on upgraded Core; one was created on testnet on 2026-08-11, so testnet now mirrors the correct mainnet configuration rather than the substitution. The testnet MarginRegistry was migrated to match in the same operation (5uWP4ewCReip6htjEZCqapNWKPujhngZTZdAt5cNZXU9), bounds and max_age_secs unchanged, and the path was verified end to end by pool_proxy_upgraded:: update_current_price on DBTC_DBUSDC (FM1CNGwQf8obhU9hdmceZNdatk4npbBoc1ChgvarsyBx), which runs read_price_upgraded on both legs. Mainnet XBTC's missing priceInfoObjectId now says what to do about it: the feed is live, only the object is absent, and creating it takes one ordinary transaction from any funded address rather than an admin action. Verified on chain that every shipped price object carries exactly the feed id its coin config claims — a tighter check than "is it an upgraded object", and the one that would have caught a mismatched pair. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
tonylee08
deployed
to
sui-typescript-aws-kms-test-env
August 11, 2026 21:34 — with
GitHub Actions
Active
Both SDKs in this repo consume the same Pyth credential and had converged on the same design independently, under different names: suins takes `pythAccessToken` -> `accessToken`, deepbook took a generic `hermesHeaders` map. These are public API after release, so align now rather than ship two shapes. `accessToken` is also the narrower, more correct type here. The endpoint choice keys off whether the caller brought credentials, and with a header map that test was wrong: any header at all — a trace id, say — routed the caller to Pyth's authenticated host, where they would 401. A token means what the branch assumes. Added `pythAccessToken` at the client level too. Supplying `pyth` replaces the whole config, so setting only a credential silently dropped the built-in state object ids; the client option composes instead. Verified against the live endpoint: state ids preserved, 200 OK. Adopted two details from the suins implementation while aligning: `ids[]` is serialized explicitly rather than relying on axios's array encoding — which the mocked test could not have caught changing — and `parsed: false` drops the response half we never read. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
tonylee08
deployed
to
sui-typescript-aws-kms-test-env
August 12, 2026 14:55 — with
GitHub Actions
Active
…echeck the tests Independent review found the runtime logic sound and the defects concentrated in prose — including two claims that would have shipped as public release notes. The `@mysten/suins` attribution was false. suins takes no such credential in any published version: `accessToken` exists only on the draft migration branch (ts-sdks#1158). Both the changeset and the JSDoc asserted an existing cross-SDK convention; they now name it as a convergence target on unpublished work, which is what it is. DBTC was documented as pricing off BTC/USD in three places while the code uses Crypto.XBTC/USD — the constants file contradicted itself six lines apart. The header, the changeset and the dedup comment were written before 2ac9b54 moved the feed and never updated. An integrator building a coin map from the release note would have wired the wrong feed id. The dedup comment also cited testnet DBTC as an example of two coins sharing a feed. No shipped map has a collision; the guard is for caller-supplied maps, and it now says so. The test file did not typecheck: `Parameters<typeof PriceServiceConnection>` does not accept a class, which silently degraded the config parameter to `undefined`, so `connection({ accessToken })` was passing an argument the type system rejected. Tests are outside tsconfig (include: src) so nothing caught it. Fixed to ConstructorParameters, plus a narrowing failure. Added test/unit/queries/priceFeedQueries.test.ts: endpoint selection had zero coverage, despite being the code that decides where a credential is sent. It pins the no-credential ConfigurationError, that the failure names the field rather than surfacing from inside axios, and that an explicit hermesEndpoint wins with the token following it there — now documented rather than implicit. Also: export PriceServiceConnectionConfig, document the pyth/pythAccessToken precedence, and record why an empty token is treated as absent. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
tonylee08
deployed
to
sui-typescript-aws-kms-test-env
August 12, 2026 15:21 — with
GitHub Actions
Active
USDSUI's coin data is correct against Pyth — object exists, is an upgraded-Core object, and its internal feed id matches. But the mainnet MarginRegistry's PythConfig configures six currencies and USDSUI is not one of them, so an oracle-taking margin call aborts in the registry's currency lookup before the feed id is compared. Recorded at the constant so the next reader does not conclude from valid-looking ids that the path works. Lending is unaffected (margin_pool supply/withdraw take no oracle) and no USDSUI DeepBook pool is margin-registered, so nothing is broken today. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
tonylee08
deployed
to
sui-typescript-aws-kms-test-env
August 12, 2026 15:34 — with
GitHub Actions
Active
tonylee08
marked this pull request as ready for review
August 14, 2026 17:00
tonylee08
deployed
to
sui-typescript-aws-kms-test-env
August 14, 2026 17:00 — with
GitHub Actions
Active
tonylee08
marked this pull request as draft
August 14, 2026 17:00
Created on Pyth's upgraded Core on 2026-08-17 (tx 3SbNisBM…ePnSx), so the last mainnet feed gap is closed — all six mainnet-configured coins now have an upgraded price object. Worth recording why this was ours to create: XBTC is absent from Pyth's Sui push-feed list entirely, so Pyth never maintained an object for it on this chain. The legacy one exists because our own price pusher pushes it, and the upgraded one will be kept fresh the same way. Creation is permissionless, so no admin action or multisig was involved. Verified on chain: the object is of the upgraded Core type, carries exactly the feed id the SDK claims for XBTC, is registered in the upgraded price table (98 -> 99 feeds), and accepts updates (tx 2HEUjwau…Ra5Kf). Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
tonylee08
deployed
to
sui-typescript-aws-kms-test-env
August 17, 2026 16:31 — with
GitHub Actions
Active
tonylee08
marked this pull request as ready for review
August 17, 2026 16:31
tonylee08
deployed
to
sui-typescript-aws-kms-test-env
August 17, 2026 16:31 — with
GitHub Actions
Active
…lication
Mainnet `deepbook_margin` was upgraded to v7 on 2026-08-17, so
`MARGIN_PACKAGE_ID` moves to `0x55ee8099…`. It is the same lineage as the
v6 id it replaces (original `0x97d94737…`, matching `MARGIN_V1`) and its
`margin_manager_upgraded` / `pool_proxy_upgraded` function sets are
identical to testnet v16's, so every builder resolves the same way on both
networks.
Two mainnet preconditions this does not satisfy, both recorded in-code and
in the changeset rather than left implicit:
- v7 carries `MARGIN_VERSION = 7` and the live `MarginRegistry` allows
`{5, 6}`, so its flows abort `EPackageVersionDisabled` until
`enable_version(7)` runs. Do not release before that call lands.
- Mainnet `margin_liquidation` has no upgraded publication — its
`liquidation_vault` exposes only `liquidate_base`/`liquidate_quote`, not
the `_upgraded` pair `MarginLiquidationsContract` targets. Those two
builders have no mainnet target until it is upgraded. The rest of the
vault surface takes no oracle and is unaffected.
Package version, lineage and module surface read on chain (2026-08-17).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tonylee08
deployed
to
sui-typescript-aws-kms-test-env
August 17, 2026 17:07 — with
GitHub Actions
Active
XBTC is a distinct asset from BTC — its own peg and redemption risk — so substituting Crypto.BTC/USD misstates collateral in exactly the stress where the two diverge. That is not a configuration choice, so pin it with a test instead of a comment: mainnet XBTC and testnet DBTC (same underlying) must carry the XBTC/USD feed, and the BTC/USD id must appear for no coin on either network. Verified the test fails when mainnet XBTC is pointed at BTC/USD. The wider confidence band on XBTC is a separate question, answered by the registry's max_conf_bps bound, not by pricing a different asset.
tonylee08
deployed
to
sui-typescript-aws-kms-test-env
August 17, 2026 18:21 — with
GitHub Actions
Active
…e docs An axios error carries config.headers, and both JSON.stringify(err) and err.toJSON() serialize it — so a caller logging a failed Hermes request would print the bearer token. getLatestVaas now catches and re-throws a plain error carrying status and response body only. Verified the new test fails when the raw error is allowed to escape. Also: dropped an orphaned JSDoc block above #oracleCalls in three files (only the second attached, and the first still described the legacy/ upgraded 'both modes' toggle this PR removed); corrected an example comment claiming 4 mainnet assets carry feeds when 7 do; and softened a test comment that claimed to check a URL is resolvable when it checks parseability.
tonylee08
deployed
to
sui-typescript-aws-kms-test-env
August 17, 2026 18:26 — with
GitHub Actions
Active
sdelo
approved these changes
Aug 17, 2026
Merged
8 tasks
tonylee08
added a commit
that referenced
this pull request
Aug 18, 2026
…ded-Pyth publication (#1214) * chore(deepbook-v3): point mainnet LIQUIDATION_PACKAGE_ID at the upgraded-Pyth publication Mainnet `margin_liquidation` v4 (`0xf17bff1b…`) predates the upgraded-Pyth entrypoints: its `liquidation_vault` exposes `liquidate_base`/`liquidate_quote` only, while `MarginLiquidationsContract` targets the `_upgraded` pair. That left `liquidateBase` and `liquidateQuote` with no mainnet target in 2.0.0 — the second of the two release gates recorded in #1194. `LIQUIDATION_PACKAGE_ID` now points at the publication that closes it, `0xba2b39c0…`. The id is not on chain yet — `object(address:)` against mainnet GraphQL returns null — so nothing here is verified against a live package: not the version, not the lineage, not the module surface. Draft until the upgrade lands and those three are read on chain. The rest of the vault surface (deposit, withdraw, balance, swaps, trader authorization) takes no oracle and works against either id. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(deepbook-v3): record the mainnet liquidation upgrade as live The id is now published as margin_liquidation v5. Its liquidation_vault exposes liquidate_base_upgraded and liquidate_quote_upgraded, and its LIQUIDATION_VAULT type is still defined by the lineage's original 0x73c59388…, so the deployed vault stays valid across the upgrade. * chore(deepbook-v3): drop the liquidation package id comment The id change speaks for itself; the reasoning lives in the changeset and the PR. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Pyth is replacing Core with a separately published Sui package rather than upgrading it in place, so
pyth::price_info::PriceInfoObjectunder the upgraded deployment is a distinct Move type. Acompatibleupgrade cannot change a public function's parameter types, so the existing margin entrypoints can never accept it.deepbook_marginexposes the upgraded surface as parallel modules —margin_manager_upgraded(12 entrypoints),pool_proxy_upgraded(8) — andmargin_liquidationas parallel entrypoints in the same module (liquidate_base_upgraded,liquidate_quote_upgraded), all under the same function names. Contract side: MystenLabs/deepbookv3#1170 and MystenLabs/deepbookv3#1211 (DBU-668).This SDK now targets the upgraded deployment exclusively. Legacy Core is being retired, so carrying both was short-lived complexity for a surface that is about to stop existing. There is no
marginPythswitch, nopythUpgradedconfig and nopriceInfoObjectIdUpgradedfield:pythis the upgraded deployment's state objects,priceInfoObjectIdis its price object. Every margin method keeps its name and signature — what changed is the module each targets and the price object it passes.The split is not uniform, and that is the thing to review:
margin_manager_upgradedandpool_proxy_upgradedcarry only the entrypoints that take a price object. Creation, repayment, referrals, cancels, modifies, settlement withdrawals, staking, governance and every getter exist solely on the base modules and still resolve there. Routing one the wrong way targets a function that does not exist on chain, which is whatmargin-upgraded-pyth.test.tspins.getPriceInfoObjectId(coinKey)andgetFeedId(coinKey)replace scattered!assertions and throw naming the coin. Without them a misconfiguration surfaces as an on-chainEPriceFeedIdMismatchdeep inside the oracle read, which does not say which coin was at fault.Generated bindings were regenerated from the margin package sources; that also picks up
managerStates,setReferralandunsetReferral, which had landed on the contract side since the last codegen run. No generated function was removed.The upgraded modules do not exist in earlier
deepbook_marginpublications, so this release must not be used against a network whose margin package predates them. Markedmajor.Mainnet margin is now v7 (
0x55ee8099…, published 2026-08-17), andMARGIN_PACKAGE_IDpoints at it — verified on chain as the lineage head, carryingmargin_manager_upgradedandpool_proxy_upgraded. Two release gates remain, neither a code change:enable_version(7)has not run on the mainnetMarginRegistry. v7's gate constant isMARGIN_VERSION = 7and the live registry does not yet allow it, so every v7 flow abortsEPackageVersionDisableduntil that call lands. Do not release before it does.margin_liquidationhas no upgraded publication. Itsliquidation_vaultexposes onlyliquidate_base/liquidate_quote, not the_upgradedpair the liquidation builders target, so those two builders have no mainnet target until it is upgraded. The rest of the vault surface takes no oracle and is unaffected.Hermes: v2, and authentication
getLatestVaascalls/v2/updates/price/latestand readsbinary.data, replacing the deprecated/api/latest_vaas. Both return the same accumulator payload — verified byte-identical for a 2-feed request — but v1 is being retired. Worth knowing: Hermes returns one message covering every requested feed, not one per feed.pythAccessToken, a client-level option sent asAuthorization: Bearer <token>on every request. The endpoint serving the upgraded Core answers 401 without it, so before this the update path could not work at all —PriceServiceConnectionConfighad no way to set a header. The name matches the in-flight@mysten/suinsmigration (feat(suins): migrate to Pyth Pro [SUIP-1111] #1158) so both SDKs take the credential under one name.No credential ships with the SDK. Consumers supply a token at runtime and talk to Pyth directly. Those who do not are intended to fall back to a DeepBook-operated proxy that supplies credentials server-side — that proxy is not deployed yet, so
DEEPBOOK_HERMES_PROXYisundefinedand that path throws aConfigurationErrornaming the field to set. Wiring its hostname is the one thing outstanding before merge.Package ids and feeds
Testnet margin was pinned to v14 — two upgrades behind. Both move to the packages carrying the upgraded surface, published from
deepbookv3main and recorded in MystenLabs/deepbookv3#1232:MARGIN_PACKAGE_ID(testnet)0xe52c1dec…0xce6ab629…LIQUIDATION_PACKAGE_ID(testnet)0x8d69c3ef…0x29bccec5…Testnet's
MarginRegistrywas migrated onto the feed ids upgraded Core carries (SUI→Crypto.SUI/USD, DBUSDC→Crypto.USDC/USD, DEEP→Crypto.DEEP/USD, DBTC→Crypto.BTC/USD; per-coin bounds andmax_age_secsunchanged), so the testnet coin map follows. DBTC prices off BTC/USD because the upgraded deployment carries no distinct DBTC feed.Mainnet XBTC now has an upgraded price object —
0x4f7ff451…, created 2026-08-17. XBTC is absent from Pyth's Sui push-feed list, so Pyth never maintained one for it on the upgraded deployment; creating it is permissionless, and as on legacy Core it is kept fresh by our own pusher rather than Pyth's. The feed itself was never the problem —Crypto.XBTC/USDcarries the same id as legacy and serves live signed data.Test plan
tsc --noEmit,prettier --check,oxlint, andvitest run— 305 passed. Examples are outsidetsconfig(include: ['src']) so they were typechecked separately, also clean.(feed, priceInfoObjectId)pair verified on chain to belong to the upgraded Pyth package on its network — 4 testnet, 7 mainnet. This caught mainnet XBTC still holding its legacy object behind a comment claiming it had none.priceInfoObjectIdthis SDK ships, for all 7 — so no shipped pair can send an update to the wrong object.3xBRg6c5…. XBTC went from ~32 minutes stale to fresh, which is the one that proves the path end to end: it is the only configured feed Pyth's own pusher does not maintain.margin-upgraded-pyth.test.tspins the module split: oracle-taking builders target the upgraded modules, oracle-free builders stay on the base modules, liquidations pick the parallel entrypoint, and a coin with no price object throws naming the coin.priceServiceConnection.test.tspins the wire contract: v2 path and params, auth headers reaching the client, noheaderskey when unconfigured, the single-combined-message shape, a diagnosable error on a v1-shaped response, and that every shipped endpoint constant is a parseable URL or absent.Bpkvp8NN….5cUt69BH….pool_proxy_upgraded::update_current_priceonSUI_DBUSDCusing shipped defaults —C2osLSS7…. This runsread_price_upgradedon both legs, so staleness, feed id, EWMA and confidence all passed against the live registry.Note for callers: the price refresh must ride in the same PTB as the margin call.
max_age_secsis 60 on chain, so refreshing in a prior transaction is a race you lose — the first attempt at the above abortedcheck_price_is_freshfor exactly this reason.Review
Two independent review passes. The first found three real defects, all fixed and covered by tests:
DEEPBOOK_HERMES_PROXYshipped as a literal placeholder host and became the default endpoint, failing as an opaqueTypeError: Invalid URLfrom inside axios.feedwith a legacypriceInfoObjectId— two deployments in one pair. Resolved by dropping the legacy identity entirely.getPriceInfoObjectssilently dropped a coin when two coins shared a feed id (reachable: DBTC prices off the generic BTC/USD feed), returningundefinedinto a move call. Feeds are now deduplicated and the index maps a feed to every coin using it.The second pass found no behaviour defects; its findings were accuracy fixes (an undercount of mainnet price objects, examples that would throw, a doc block promising a fallback that throws).
Follow-ups, deliberately not here
PriceInfoObject.Related: MystenLabs/sui-operations#8693 (testnet price-pusher stack, blocked on this releasing) and MystenLabs/deepbook-services#147 (mainnet migration script, ordered before this).
State object ids, package versions, feed ids and per-feed price objects were resolved on chain (2026-08-11) rather than transcribed.
AI Assistance Notice