fix: return 0 when contract doesn't implement decimals()#28
Merged
Conversation
wa0x6e
force-pushed
the
fix/fallback-token-decimals
branch
from
March 16, 2026 13:28
25f6ad5 to
bd96686
Compare
When getTokenDecimals() fails with CALL_EXCEPTION (contract doesn't implement decimals()), return 0 USD value instead of letting the error bubble up as an unhandled 500. Temporary errors (timeout, network) are re-thrown so the sequencer can retry. Fixes SNAPSHOT-SEQUENCER-48 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
wa0x6e
force-pushed
the
fix/fallback-token-decimals
branch
from
March 16, 2026 13:38
bd96686 to
ef20eee
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Updates the erc20-balance-of strategy to handle failures when reading ERC20 decimals() more explicitly, avoiding hard failures for a specific class of contract-call errors.
Changes:
- Wraps
getTokenDecimals(...)in a try/catch and returns0when the error code isCALL_EXCEPTION. - Re-throws non-
CALL_EXCEPTIONerrors to allow upstream retry/handling.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| try { | ||
| tokenDecimals = await getTokenDecimals(Number(tokenNetwork), tokenAddress); | ||
| } catch (e: any) { | ||
| // CALL_EXCEPTION means the contract doesn't implement decimals() |
Comment on lines
+96
to
+106
| let tokenDecimals: number; | ||
| try { | ||
| tokenDecimals = await getTokenDecimals(Number(tokenNetwork), tokenAddress); | ||
| } catch (e: any) { | ||
| // CALL_EXCEPTION means the contract doesn't implement decimals() | ||
| // Other errors (timeout, network) are temporary and should be retried | ||
| if (e.code === 'CALL_EXCEPTION') { | ||
| return 0; | ||
| } | ||
| throw e; | ||
| } |
ChaituVR
requested changes
Mar 17, 2026
| } catch (e: any) { | ||
| // CALL_EXCEPTION means the contract doesn't implement decimals() | ||
| // Other errors (timeout, network) are temporary and should be retried | ||
| if (e.code === 'CALL_EXCEPTION') { |
Member
There was a problem hiding this comment.
I think CALL_EXCEPTION can be triggered for many issues, not just "method not implemented"
Contributor
Author
There was a problem hiding this comment.
In this particular case, where we try to read data from a read-only function, we're assuming that this error will happen only when the function does not exist, or have a different ABI
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.
Summary
Partially fix https://snapshot-labs.sentry.io/issues/7328652620/?project=4505606761152512&query=is%3Aunresolved&referrer=issue-stream
When a token contract doesn't implement
decimals(), theerc20-balance-ofstrategy throws an unhandled error that bubbles up as a 500. The sequencer marks the proposal asERROR_SYNCand retries indefinitely — generating ~36K Sentry errors (SNAPSHOT-SEQUENCER-48) since March 12.The fix catches
CALL_EXCEPTIONerrors (ethers.js error code for contract reverts) and returns0USD value — same as when CoinGecko has no price data. Temporary errors (timeout, network) are re-thrown so the sequencer can retry.Changes
CALL_EXCEPTIONinerc20-balance-ofstrategy and return 0Test plan
decimals()(e.g. SENSO0xC19B6A4Ac7C7Cc24459F08984Bbd09664af17bD1on mainnet)decimals()are unaffected🤖 Generated with Claude Code