apollo_l1_gas_price_config,apollo_deployments,apollo_node: add ExchangeRateOracleSource - #14995
Open
asaf-sw wants to merge 1 commit into
Open
apollo_l1_gas_price_config,apollo_deployments,apollo_node: add ExchangeRateOracleSource#14995asaf-sw wants to merge 1 commit into
asaf-sw wants to merge 1 commit into
Conversation
…geRateOracleSource Adds `ExchangeRateOracleSource` (`Http` default, `Chainlink`) and the two `*_oracle_source` fields that carry it, one per feed, so a feed moves off the HTTP oracle by a config edit rather than a code deploy. Both feeds default to `Http` and nothing reads either field yet: B4 builds each feed's client from its source. The enum, both fields and both params are deleted with the HTTP oracle in C1, as the `TODO(asaf-sw)` on the enum records. Both param descriptions state that `Chainlink` requires a batcher client, and that it is mainnet-only until the feeds are deployed off mainnet. Also makes the shared `QuotedRateBoundsConfig` descriptions pair-agnostic. They are served for both `eth_usd` and `strk_usd`, so the ETH/USD worked conversion was shipped against `strk_usd`'s default of 100, which is $0.0001 rather than $20. Every description keeps a worked micro-unit conversion. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Artifacts upload workflows: |
This was referenced Aug 19, 2026
Contributor
Author
|
Migrated review comment from #14944, closed in favor of this split. @matanl-starkware wrote on
This PR owns the operator-facing descriptions that spell those values out ("a value of |
asaf-sw
marked this pull request as ready for review
August 19, 2026 07:22
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.

Adds
ExchangeRateOracleSource(Httpdefault,Chainlink) and one*_oracle_sourcefield per feed, so a feed moves off the HTTP oracle by editing a config key rather than by deploying code. The two feeds are selected independently so they can be migrated one at a time. Also rewrites the micro-unit bound descriptions, which were written for ETH/USD but served understrk_usdtoo, where the worked example disagreed with the shipped default by five orders of magnitude.B3 of the split of #14944. 2 new parameters.
Httpand nothing reads either field. B4 is the first PR whose behavior depends on them, so merging this changes nothing at runtime.ser_params and the three tests all go in C1 with the HTTP oracle. The enum carries aTODO(asaf-sw)naming that removal, which is why the switch is deliberately thin. The description rewrite is the part that survives.unrecognized_oracle_source_fails_to_loadpins that the enum takes noserde(rename_all), so a lowercase"chainlink"cannot silently fall back toHttpand produce a node that looks migrated.Detailed Summary for AI Bots
Stacked on #14994. Part of the L1 price oracle replacement split of #14942.
What
ExchangeRateOracleSource(Httpdefault,Chainlink) and the two*_oracle_sourcefields that carry it, one per feed, so a feed moves off the HTTP oracle by editing a config key rather than by deploying code. 2 new parameters, no existing parameter's value or privacy touched.Both feeds default to
Http, and nothing reads either field yet: B4 builds each feed's client from its source and is the first PR whose behavior depends on these values. Merging this changes nothing at runtime.The two feeds are selected independently so they can be migrated one at a time, and so the second flip can wait on a week of evidence from the first.
Every line of the switch has a deletion date
The enum, both fields, both
ser_params and the three tests below all go away in C1, together with the HTTP oracle they exist to switch away from. The enum carries aTODO(asaf-sw)naming that removal. Nothing here is built to last, so it is deliberately thin: an enum, twopubfields, aDefault, and no accessor, no helper, no validation beyond what serde already rejects.What survives C1 is the description rewrite in the second half of this PR, which is about micro units rather than about sources.
Chainlinkis mainnet-onlyThe feed addresses B2 shipped are the Chainlink proxies on Starknet mainnet, in every environment, because no other network has the feeds. That restriction is documented where an operator flipping the switch will read it: both
*_oracle_sourcedescriptions, and therefore both entries inconfig_schema.json, state thatChainlinkis mainnet-only until the feeds are deployed elsewhere.The same descriptions state the other precondition:
Chainlinkrequires a batcher client, and selecting it on a service that has none is a startup failure rather than a fallback toHttp. B4 is what enforces that, naming every offending key in one startup instead of one per restart. Every shipped topology gives the L1 service a batcher client, so it is reachable only by misconfiguration.Flipping a feed also waits on #14973's rate-of-change bound and on the cap chain, for the reasons set out in #14944; those are gates on using the switch, not on merging it.
The
rename_alltest is the one that has to be hereunrecognized_oracle_source_fails_to_loadpins that the enum takes noserde(rename_all), so the variants are matched exactly as written in Rust. Without it, a lowercase"chainlink"deserializes to nothing, falls back to theHttpdefault, and produces a node that looks migrated while reading the same HTTP API it read yesterday. Config loading does reject the unknown value today; the test is what keeps a later conveniencerename_allfrom turning that rejection into a silent default.It earns its place precisely because the window is short. While both sources exist a wrong flip is detectable by comparing the two, but once the HTTP source is gone in C1 there is no comparison left to make, so the only chance to notice is now.
The four cases cover the casings an operator plausibly types (
chainlink,CHAINLINK), a feed name mistaken for a source (eth_to_strk), and an unknown source (Coinbase), each against both param paths.The bound descriptions were pair-specific and served to every pair
QuotedRateBoundsConfig::dump()produces the descriptions for botheth_usdandstrk_usd, so its worked conversion, written for ETH/USD, was served under both keys. The schema shippedrate_bounds_config.strk_usd.minimum_micro_unitsdescribed as "a value of 20000000 on ETH/USD means $20" against an actual default of100, which is $0.0001. An operator reading the schema had a worked example that disagreed with the value next to it by five orders of magnitude.Both descriptions are now pair-agnostic and keep a worked conversion, since the schema is all an operator has:
minimum_micro_units: "Lowest accepted price for this pair, in micro units (1e-6) of the pair's quote currency, so a value of 20000000 means 20 units of the quote currency, and a value of 100 means 0.0001 units."maximum_micro_units: "Highest accepted price for this pair, in micro units (1e-6) of the pair's quote currency, so a value of 50000000000 means 50,000 units of the quote currency."The minimum carries both worked examples because the two defaults it is served for are
20000000and100; one example alone leaves whichever key it was not written for looking wrong. Micro units reached the schema for the first time in B2 and were explained only in a Rust doc comment before that, where being wrong by 10^6 either disables the guard or rejects every reading.DerivedRateBoundsConfigis untouched: it serveseth_to_frionly, so its ETH/STRK examples are correct for the one key they reach.Testing
9 new cases, 32 in the crate.
default_oracle_sources_are_httpon theDefault,oracle_sources_round_trip_through_serialize_configover all four combinations of the two feeds, andunrecognized_oracle_source_fails_to_loadover four rejected values against both params. The round-trip test shares its dump-to-flat-map helper with B2'sprovider_config_round_trips_through_serialize_config, which is what makes the rejection test meaningful:Chainlinkloads through the same path the bad values are rejected on, so the failures are about the value and not about the harness.default_config_file_is_up_to_dateanddeployment_files_are_up_to_dateare the staleness gates on the regenerated files, and both pass. The 2 new keys were added to both base app configs by hand, as in B2, before regenerating.🤖 Generated with Claude Code