Skip to content

[PR #35] oracle.rs: missing answeredInRound < roundId check — incomplete rounds pass as valid #108

@obchain

Description

@obchain

PR: #35 (feat/10-chainlink-pricecache)
Commit: 5c36873
File: crates/charon-scanner/src/oracle.rs, refresh()

Problem: Chainlink latestRoundData() returns five fields: roundId, answer, startedAt, updatedAt, answeredInRound. Invariant: answeredInRound < roundId means aggregator has not finished aggregating current round. Answer in that state is value from previous older round — stale data published under new round ID. PR captures round.answeredInRound from sol! binding but never uses it.

Current staleness check (updated_at + max_age >= now) does NOT catch this: partially-completed round still carries recent updatedAt from last completed round, so freshness predicate passes while returned answer is old. Under high volatility gap can be large enough to cause mispriced liquidation.

Risk: Liquidation executed on stale answer causes:

  • Under-priced collateral seized → protocol loss.
  • Over-priced collateral seized → borrower griefed / Venus revert.

Fix: Before cache insertion:

if round.answeredInRound < round.roundId {
    anyhow::bail!(
        "feed '{symbol}': round not complete (answeredInRound={}, roundId={})",
        round.answeredInRound, round.roundId
    );
}

Must execute before staleness check. Add unit test with stub/mock aggregator returning answeredInRound < roundId.

Metadata

Metadata

Assignees

No one assigned

    Labels

    layer:rustRust crates (core / scanner / protocols / executor / cli)pr-reviewFindings from PR review processpriority:p0-blockerBlocks the critical pathtype:featureNew capability or deliverable

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions