feat(blend): periodic SEP-40 oracle price snapshot with Comet BLND/LP leg#660
feat(blend): periodic SEP-40 oracle price snapshot with Comet BLND/LP leg#660aditya1702 wants to merge 8 commits into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d86bb208a6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
7fa1c21 to
8036e49
Compare
d86bb20 to
a1197eb
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a1197eb096
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
a1197eb to
5c7d419
Compare
9330867 to
edaf72c
Compare
5c7d419 to
731499d
Compare
edaf72c to
e00ce3f
Compare
10ccc0a to
9d078d4
Compare
e00ce3f to
23307c6
Compare
9d078d4 to
23486c9
Compare
23307c6 to
3995153
Compare
23486c9 to
6cdb564
Compare
39d5745 to
8b68326
Compare
6cdb564 to
97ab9f5
Compare
…e live advisory lock The blend price snapshot task started in setupDeps, so an instance that never wins the advisory lock (rolling deploy, standby pod) still ran snapshot passes, duplicating oracle RPC load and writes. Long-lived background tasks now register as PostLockTasks and launch inside startLiveIngestion after the lock is held.
The pool contract itself (pool.rs::load_price) rejects prices older than 24h or <= 0, so persisting them only lets the API price positions the pool would refuse to. Skip both at snapshot time, counted as new "stale"/"invalid" fetch outcomes, and add a wallet_blend_price_oldest_age_seconds gauge covering every decoded price including skipped-as-stale ones, so a dead oracle surfaces as unbounded gauge growth.
8b68326 to
d6d740f
Compare
97ab9f5 to
adec3b6
Compare
Blend oracle price snapshot task
Fourth of 5 stacked PRs adding Blend Capital v2 lending support (stacked on #659).
Adds a periodic price snapshot task under
ingest(live mode only) that keepsblend_oracle_pricesfresh for PR5's USD valuation:OraclePriceModel—GetPriceTargetsderives the deduped(oracle, asset)set fromblend_pools × blend_reserves(NULL-oracle pools excluded);BatchUpserton(oracle, asset). Runs directly on the pool (no ingest tx — the task is on its own schedule).Asset::Stellar(Address)arg encoding +Option<PriceData>decoding. Verified against the SEP-40 spec, the deployed Reflector oracle interface, and a live mainnetlastpricesimulation round-trip (encoding accepted, realPriceDatareturned).get_tokens/get_balance/get_normalized_weight/get_total_supply, 7-dec weight scalar, pinned against the deployed WASM8abc2891…). Convention (PR5 reads this): rows live underoracle_contract_id = <Comet address>; the LP row is self-priced (asset == oracle), the BLND row keys the real BLND token address. USDC ≡ $1 documented.decimals()once per oracle;Option::Noneprices skipped, as are prices the pool contract itself would reject (pool.rs::load_price): oracle-reported timestamps older than 24h (stale) and prices ≤ 0 (invalid); per-oracle failures isolated (errors.Join) and counted, never abort a pass; oneBatchUpsertper pass. Metrics: snapshot duration histogram, fetch counter (success|error|none|stale|invalid), prices-tracked gauge, oldest-price-age gauge (covers prices skipped as stale — a dead oracle surfaces as unbounded growth).BLEND_PRICE_INTERVAL(default60s,0disables) andBLEND_BACKSTOP_LP_CONTRACT_ID(empty disables the Comet leg); wired insetupDepsbehind live-mode + interval gates; documented in docker-compose.Deviations register (reviewer sign-off):
Asset::StellarScVec encoding verified against a live Reflector oracle (spec + interface dump + mainnet simulation)get_tokens🤖 Generated with Claude Code
Review fixes:
Runwaits the full interval AFTER each snapshot pass (timer reset) instead of a ticker, so an overrunning pass never triggers an immediate back-to-back pass (Codex).23486c90— skip stale and non-positive oracle prices at snapshot time: the pool contract itself rejects prices older than 24h or ≤ 0 (pool.rs::load_price), so persisting them only lets the API price positions the pool would refuse to. Newstale/invalidfetch outcomes plus awallet_blend_price_oldest_age_secondsgauge covering every decoded price (including skipped-as-stale ones — a dead oracle surfaces as unbounded gauge growth). This is the write-side half of the staleness guard; feat(graphql): Blend positions, pools, and earn discovery #661 adds the read-side half so already-stored rows also stop pricing once aged.