fix(checkStablecoin): don't flag pufETH/LanternSOL/SUNOLD as stablecoins (substring false-match)#2822
Open
Himess wants to merge 3 commits into
Open
fix(checkStablecoin): don't flag pufETH/LanternSOL/SUNOLD as stablecoins (substring false-match)#2822Himess wants to merge 3 commits into
Himess wants to merge 3 commits into
Conversation
checkStablecoin substring-matches a single token against the stablecoins
list, so 'pufeth'.includes('feth'), 'lanternsol'.includes('ern') and
'sunold'.includes('uno') flag these volatile assets (restaked ETH, a SOL
LST, TRON SUN) as stablecoin: true. Add a notStable exception so they don't
match. 7 live pools (~$49M) flip to the correct false; 0 regressions.
Contributor
📝 WalkthroughWalkthrough
ChangesStablecoin detection and adaptor filtering
Estimated code review effort: 1 (Trivial) | ~5 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Contributor
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/adaptors/checkStablecoin.js`:
- Around line 8-9: Normalize each token by removing the same parenthetical
annotations used by the later stablecoin match before applying the notStable
guard in checkStablecoin. Ensure annotated pufeth, lanternsol, and sunold values
are excluded, and add regression coverage for these annotated forms.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b5b1f2bf-369c-49e3-aabf-c5daf9b21926
📒 Files selected for processing (1)
src/adaptors/checkStablecoin.js
checkStablecoin.js is a shared helper (like utils.js), not a project adapter, so 'npm run test --adapter=checkStablecoin.js' fails with 'module.apy is not a function'. Exclude it from getFileList.js the same way utils.js/test.js are.
… guard Match the cleanup the later stablecoin check already applies, so annotated forms like 'pufeth (...)' also hit the exclusion.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
checkStablecoinsets the flag by substring-matching the pool symbol against the ~410 symbol list from https://stablecoins.llama.fi:The list contains short, word-like symbols (feth, ern, uno, usc, usn, and others), so a volatile token that merely contains one as a substring gets flagged stable.
'pufeth'.includes('feth')is true, so Puffer's liquid restaking token is classified as a stablecoin. It trades around $3k and is ETH-correlated.'pufeth'.includes('feth')'lanternsol'.includes('ern')'sunold'.includes('uno')(plus smaller pufETH pools on compound-v3, pendle and euler-v2.)
$47.5m of this is puffer-stake pufETH. Someone filtering the yields page for stablecoin yield sees a restaked-ETH position as stable.
Every other ETH LST/LRT in the data is correctly flagged
false. pufETH is the same asset class and gets the opposite answer, purely because of the substring collision:All 14 siblings are
false; pufETH is the only ETH LST in the dataset flaggedtrue.const checkStablecoin = (el, stablecoins) => { let tokens = el.symbol.split('-').map((el) => el.toLowerCase()); const symbolLC = el.symbol.toLowerCase(); + // tokens that merely CONTAIN a stablecoin symbol substring but are NOT stablecoins. + // e.g. 'pufeth'.includes('feth') or 'lanternsol'.includes('ern') otherwise flag these + // volatile assets (restaked ETH, a SOL LST, TRON SUN) as stablecoin: true. + const notStable = ['pufeth', 'lanternsol', 'sunold']; + if (tokens.some((t) => notStable.includes(t))) return false; + let stable;Tested against the live pool set: exactly 7 pools flip true to false (PUFETH x4, LANTERNSOL x2, SUNOLD x1). 0 flip false to true. The change is one-directional by construction: forcing a known-non-stable token to false can only remove wrong stable flags, it cannot introduce a new false true. The genuine cases (SUSDE, STEAKUSDC, SDAI, SGHO all really wrap the stablecoin they match) are unchanged.
I checked how wide this collision class is: about 25 distinct symbols are affected (cash, hai, vai, use, xy, gai, ern, volt, mod, feth, uno). The dollar impact is concentrated: pufETH alone is $47.5m of roughly $50m. This patch covers the material cases. The tail is about 22 small pools totalling $1.2m.
An ends-with rule would not help here, since 'pufeth' ends with 'feth'. The symbols themselves are the problem.
This has the same root cause as #2821 (checkIlRisk, where 'ethfi'.includes('eth') mislabels ETHFI-ETH pools). Both classify tokens by symbol substring against a hand-curated list, and both exception fixes are whack-a-mole. The proper fix is one shared registry keyed on the pool's underlyingTokens addresses, which the data already carries, instead of symbol matching. That would retire both defect classes at once. If you would rather have that than two exception lists, I am happy to close both and do the rewrite instead. Just say the word.
Summary by CodeRabbit