Skip to content

fix(pricing): validate asset prices before storing and guard the DEX reference amount#4201

Open
Danswar wants to merge 1 commit into
developfrom
fix/degenerate-asset-price-validation
Open

fix(pricing): validate asset prices before storing and guard the DEX reference amount#4201
Danswar wants to merge 1 commit into
developfrom
fix/degenerate-asset-price-validation

Conversation

@Danswar

@Danswar Danswar commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Why

The hourly asset-pricing job on dev has been failing on every run for 7+ days:

ERROR [DexService] Error while getting target amount (Arbitrum/TGT -> Arbitrum/USDT): Error: Invariant failed: AMOUNT

The stored approxPriceChf for TGT on dev is a degenerate near-zero value (observed: 4.24e-60). Asset.minimalPriceReferenceAmount inverts the stored price, so the reference amount used for the quote overflows uint256 and the Uniswap SDK rejects it.

Two things make this worse than a single bad quote:

  1. The asset is stuck in a self-poisoning loop. The DEX quote is itself the price source for this asset (pricing-dex.service), so the failing quote prevents the corrupt price from ever being overwritten. Prod's TGT row is healthy (0.0061 CHF, refreshing hourly), which confirms the pool quote is fine — only the stored dev value is corrupt.
  2. A degenerate stored price leaks everywhere. approxPriceChf also feeds the public asset DTO, custody balance values, trading profit estimates, and the persisted daily price history — so it must not be stored in the first place.

What

  • Asset.isSanePrice() with sanity rails (1e-121e15): a price outside these is corrupted data, not a real price.
  • Read side (minimalPriceReferenceAmount): falls back to a reference amount of 1 for insane prices instead of inverting them. This breaks the deadlock: the next pricing run can quote again and overwrite the corrupt value.
  • Write side (AssetPricesJobService.updatePrices): a degenerate fetched price is no longer stored — the job logs a warning and keeps the last known good price, protecting all downstream consumers at the source.

Replaces the asset-price half of #4200, which only guarded the single read site and had no write-path validation.

Test plan

  • New asset.entity.spec.ts (sanity rails + reference-amount fallback) and asset-prices-job.service.spec.ts (stores sane prices, skips degenerate ones, keeps last known good, continues with remaining assets) — 9/9 passing
  • Full local suite green (pre-push hook)
  • Lint / tsc --noEmit / prettier clean
  • After dev deploy: verify the hourly DexService error stops and TGT's price self-heals

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.

1 participant