Skip to content

checkStablecoin and checkIlRisk both misclassify tokens by symbol substring (root cause of #2821, #2822) #2823

Description

@Himess

Summary

#2821 and #2822 each fix one token misclassification, but they share a single root cause: both checkStablecoin and checkIlRisk decide what a token is by substring-matching the pool symbol against a hand-curated list. Any volatile token whose symbol merely contains a listed substring gets mislabeled. This issue proposes fixing the class once instead of patching symbols one by one.

Two symptoms, both live today

checkIlRisk (src/handlers/triggerEnrichment.js) matches token names against l1Token = ['btc', 'eth', 'avax', 'matic', 'eur', 'link', 'sushi']:

  • Forward false no: 'ethfi'.includes('eth') buckets Ether.fi's ETHFI as ETH, so ETH/ETHFI pairs collapse to a single asset and report ilRisk: no. Narrowly fixed in fix(ilRisk): don't collapse ETHFI/sETHFI into the ETH family (substring false-match) #2821.
  • Reverse false yes: sol is not in the list, so SOL LST pairs (jitoSOL, mSOL, bSOL) report ilRisk: yes even though they are SOL-correlated with minimal IL. Live sample: 93 jitoSOL/mSOL/bSOL pairs, about $29.8m, all ilRisk: yes. Not fixed, because adding sol to the list would then bucket memecoins like SOLO or SOLCEX by the same substring.

checkStablecoin (src/adaptors/checkStablecoin.js) substring-matches against the ~410 symbol list from https://stablecoins.llama.fi (many short entries: feth, ern, uno, use, cash, hai, vai):

Why they are one bug

Both classifiers read a token's nature from characters in its symbol, not from the token itself. The failure mode is identical (substring collision), and the current patch pattern (exception arrays: notCorrelated in #2821, notStable in #2822) can only remove known-wrong flags one symbol at a time while the false-positive tail keeps growing.

Proposed direction

Classify from each pool's underlyingTokens (contract addresses), which are populated on essentially every pool today (15,373 of 15,376), against a small shared registry instead of by symbol substring:

  • a pegged-USD address set for checkStablecoin
  • correlated-asset groups (ETH LSTs, SOL LSTs, BTC wrappers) for checkIlRisk

This resolves the whole class at once: the 93 SOL-LST ilRisk pairs and the memecoin-USDC stablecoin pairs all carry real underlying addresses, so they classify correctly, and the set self-maintains as new tokens reuse the same addresses.

Honest caveats:

  • A few pools report a placeholder underlying (for example the top pufETH staking pool lists underlyingTokens: ["0x0000000000000000000000000000000000000000"]), so the symbol path has to stay as a fallback and a small symbol-level exception set is still needed for those.
  • A registry is more upfront work than another exception line, and it needs an owner to keep current.

The actual decision

  1. Keep extending the centralized exception arrays (notCorrelated / notStable). Tiny, safe, one-directional, but whack-a-mole as the tail grows.
  2. Move both classifiers to an address-based registry with a symbol fallback. Removes the class, more upfront cost, needs maintenance.

Offer

I have the reproduction and evidence for both functions and I am happy to build whichever direction you prefer. I can fold #2821 and #2822 into the registry approach, or leave them as the targeted patches and just extend the exception lists. A steer on which you would merge would let me put up the right PR.

Reproduces from https://yields.llama.fi/pools plus the two functions, 2026-07-16.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions