feat: add Chainlink historical price source - #42
Conversation
- point katana WETH at ETH/USD, drop the KAT feed - fix the dead arbitrum WBTC key and lowercase native USDC - collapse the two feed maps into one - return null when a feed reverts so another source can answer - assert the feed is read at the estimated historical block - document the chainlink and defillama-alias sources
…ority - inline toHistoricalPrice into the source with real types; delete coin.ts - drop chain-1 0xeee... native key: no code path produces it - priority 15 so chainlink no longer ties onchain's 20 - test transport failure rejects instead of reading as no price - docs: source filter only matches stored rows
|
/review-workflow |
|
Review started (review-pr-workflow): https://github.com/yearn/yearn-prices/actions/runs/32992605377 |
|
gitconfig-mask: sentinel SummaryAdds a Chainlink on-chain historical price source (feed table for 9 chains + IssuesChainlink source crashes on every real call (high) — the feed's
Suggestions
How This Was ReviewedReviewed with the review-pr-workflow skill — |
decimals() returns uint8, decoded as bigint; using it as a number exponent threw TypeError on every live call. Reuse ClientForChain and cover supports().
|
/review-workflow |
|
Review started (review-pr-workflow): https://github.com/yearn/yearn-prices/actions/runs/32995119511 |
SummaryAdds a new IssuesNone confirmed. Five review lenses (spec, bugs, security, deps, clarity) ran with adversarial verification; nothing survived as a confirmed defect. Suggestions
gitconfig-mask: sentinel VerdictAPPROVE How This Was ReviewedReviewed with the review-pr-workflow skill — |
|
Addressed review:
|
|
/review-workflow |
|
Review started (review-pr-workflow): https://github.com/yearn/yearn-prices/actions/runs/32997286931 |
SummaryAdds a Chainlink-based historical price source: a static per-chain feed registry ( IssuesNone — no findings survived independent verification. One Suggestions
VerdictAPPROVE How This Was ReviewedReviewed with the review-pr-workflow skill — gitconfig-mask: sentinel |
|
Round 2 addressed:
Local: 249 tests pass, |
|
/review-workflow |
|
Review started (review-pr-workflow): https://github.com/yearn/yearn-prices/actions/runs/33000746538 |
SummaryAdds a Chainlink on-chain historical price source: feed/proxy address tables ( gitconfig-mask: sentinel DependenciesNo new dependencies ( IssuesNone confirmed by verification. Suggestions
VerdictAPPROVE How This Was ReviewedReviewed with the review-pr-workflow skill — |
murderteeth
left a comment
There was a problem hiding this comment.
Summary
Adds a chainlink historical source with a 42-entry feed table across 8 chains, registered at priority 15 between defillama and the on-chain source. I independently re-checked the whole table against live RPCs — every token key is a deployed ERC-20, all 38 aggregators are proxies whose description() matches the configured pair at 8 decimals, and the four Katana vault-bridge tokens expose no share-price interface, so pricing them off the underlying is sound.
Issues
-
src/sources/chainlink/historical.ts:49 - RPC credentials reach the logs (high) — when the archive RPC hiccups during the block lookup, the raw transport error escapes untranslated and the request handler logs its message, which embeds the full
RPC_URL_<chainId>including the provider API key. The caller also gets a generic 500 where the same fault raised by the on-chain source produces a 503.- Done when: a transient RPC failure during the Chainlink block lookup surfaces as the same UNAVAILABLE/503 class the on-chain source produces, and no log line emitted for that request contains the RPC endpoint URL or its query string.
- Provenance: f05edce
-
src/sources/chainlink/historical.ts:77 - Response names the wrong token (medium) — a Chainlink-served price reports the feed's asset rather than the token that was asked for, so one token key comes back labelled differently depending on which source answered: WPOL as
MATIC, vbUSDC asUSDC, WETH asETH. The table disagrees with itself too — the same WBTC address isWBTCon Base andBTCon Sonic. Prices are unaffected; only the label diverges.- Done when: for a token whose symbol differs from its feed's base asset (polygon
0x0d50…1270, katana0x203a…fd36), a Chainlink-served response carries that token's symbol or null rather than the feed's, and a test pins it for at least one such token. - Provenance: 0ef02f7
- Done when: for a token whose symbol differs from its feed's base asset (polygon
Verdict
REQUEST_CHANGES
How This Was Reviewed
Reviewed with the review-pr-workflow skill —
5 review lenses, each finding independently verified by claude. 1 candidate finding was refuted and dropped.
|
/review |
|
Review started (review-pr): https://github.com/yearn/yearn-prices/actions/runs/33088099862 |
SummaryAdds a Chainlink historical price source: a static chain→token→aggregator-proxy feed table ( Checks: gitconfig-mask: sentinel Issues
VerdictREQUEST_CHANGES How This Was ReviewedThis review was conducted using the review-pr skill. |
86,400s equalled the daily USD feed heartbeat, so a healthy feed went null in the window before its next update. Bound is now 2x the longest heartbeat in feeds.ts.
Only the single-token route 404s on ?source=chainlink; batch and range return 200 with an empty coins object. README now names the historical try-order.
…-historical-source
murderteeth
left a comment
There was a problem hiding this comment.
Summary
Adds a chainlink historical source: a 41-entry token→aggregator table across 8 chains, a reader that estimates the block for the requested timestamp and scales latestRoundData() by decimals() at that block, and registration at priority 15 between defillama and derived. I independently re-checked the whole feed table against live RPCs — all 41 aggregators resolve, every description() matches the token key (including the four Katana vault-bridge tokens priced off their underlying and Polygon WPOL → MATIC / USD), and all report 8 decimals.
No defects survived verification.
How This Was Reviewed
Reviewed with the review-pr-workflow skill —
5 review lenses, each finding independently verified by claude. 3 candidate findings were refuted and dropped.
Summary
latestRoundData()anddecimals()at that block, scales the answer, and rejects stale or non-positive prices.marketSources, so recursive on-chain pricing can resolve leaf tokensthrough it.
Behavior
src/sources/chainlink/historical.ts:67).request failing.
RPC_URL_<chainId>. No new env vars.Priority and ordering
Priority is 15, not the 20 the on-chain source defaults to. The registry stable-sorts ties by registration order, so
sharing 20 would have left the documented
chainlinkbeforederivedorder resting on array position increateHistoricalSources. Every registered historical source now has a distinct priority: defillama 10, chainlink 15,on-chain 20, defillama-alias 30.
chainlinkalso joinsSOURCE_PRIORITYinsrc/types.tsat index 1. That list drives the SQLCASEranking usedwhen the
sourceparam is omitted, so a storedchainlinkrow would outrank every source below it. Nothing writeschainlinkrows today.The
?source=filter matches stored rows only.chainlinkanddefillama-aliasare resolved live and neverpersisted, so filtering on either returns 404.
docs/routes.mdnow says so, and listschainlinkat position 2 plusdefillama-alias, which was missing.Feed table
Every entry was checked against the configured RPC: the token key is a deployed ERC-20, the key is lowercase, and the
aggregator's
description()matches the configured symbol. Chains with no verified feed are not registered.Notable entries:
0x4200…0006(WETH) points at the ETH/USD aggregator. The KAT/USD feed is not mapped to any token.WBTC.Only ERC-20 keys are in the table. The
0xeee…eeenative-asset placeholder is not, since no code path reaches thesource with it: the recursive resolver rewrites native coins to their wrapped address before pricing.
Tests
npx tsc --noEmitnpx @biomejs/biome checknpx vitest run: 240 passed, 2 skipped. The skips are the Enso live-API tests, gated behindENSO_API_KEY.fallthrough, transport failures rejecting rather than returning null, that the feed is read at the estimated
historical block rather than latest, and two table invariants (all keys lowercase, no chain registered with an empty
map).
Live checks against the corrected table, run outside vitest so real RPCs are reachable:
1617321599$58,801, registry$58,830via DefiLlama$2,459.89(symbolETH)$77,707.45$0.999971550000000, before the feed existednull, falls through