Skip to content

fix(scanner): per-feed Chainlink max_age overrides (closes #331)#339

Merged
obchain merged 1 commit into
mainfrom
fix/331-per-feed-max-age
Apr 26, 2026
Merged

fix(scanner): per-feed Chainlink max_age overrides (closes #331)#339
obchain merged 1 commit into
mainfrom
fix/331-per-feed-max-age

Conversation

@obchain
Copy link
Copy Markdown
Owner

@obchain obchain commented Apr 26, 2026

Summary

  • New config section [chainlink_max_age_secs.<chain>] (symbol → seconds) lets stable-coin feeds bypass the global 600s freshness gate.
  • CLI wires parsed overrides into the existing PriceCache::with_per_symbol_max_age constructor.
  • Ship USDT = USDC = 86400 (24h) defaults in default.toml and fork.toml to match Chainlink's documented heartbeat ceiling for BSC stables.

Why

USDT/USDC on BSC are deviation-triggered — heartbeat ~24h. The single global 600s default routinely flagged them stale even when the price had not moved, silently dropping every position priced against them. Volatile feeds (BNB/ETH/BTCB) keep the 600s gate. Closes #331.

Test plan

  • cargo test -p charon-core --lib (45/45 incl. 2 new round-trip tests)
  • cargo test --workspace --lib (56/56)
  • cargo build --workspace clean
  • Soak test against live fork — verify USDT/USDC stay inside the freshness window indefinitely while BNB still gates at 600s

…artbeats (closes #331)

The single global DEFAULT_MAX_AGE = 600s in oracle.rs cannot fit
feeds whose heartbeats span two orders of magnitude. BSC stable-coin
Chainlink feeds (USDT, USDC, FDUSD) update on deviation, not
heartbeat — heartbeat is ~24h — so the 600s gate routinely flags
them as stale even when the price has not moved, and the freshness
gate then silently drops every position priced against them.

Add a top-level config section `[chainlink_max_age_secs.<chain>]`
keyed by symbol → seconds. Missing entries fall through to the
global default, so volatile feeds (BNB, ETH, BTCB) stay gated
tightly at 600s. Wire the parsed map into `PriceCache::with_per_symbol_max_age`
in the CLI bootstrap.

Top-level section instead of `[chainlink.<chain>.max_age_secs]`:
the existing `chainlink.<chain>` table is `HashMap<symbol, Address>`
with `deny_unknown_fields`, so a typed sub-table would require a
breaking schema change. Doc comment on the new field justifies.

Ship sane stable defaults out of the box: USDT and USDC = 86400s
in both default.toml and fork.toml, matching Chainlink's documented
24h heartbeat ceiling. Operators can lower per-deployment without
rebuilding.

Tests: round-trip TOML deserialization + serde-default empty fallthrough.
@obchain obchain merged commit b93136f into main Apr 26, 2026
4 checks passed
@obchain obchain deleted the fix/331-per-feed-max-age branch April 26, 2026 10:38
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.

scanner: per-feed Chainlink max_age — stable feeds (USDC/USDT) regularly stale beyond 600s

1 participant