fix: separate cyToken share decimals from underlying decimals (A17-5)#372
Open
thedavidmeister wants to merge 1 commit into
Open
fix: separate cyToken share decimals from underlying decimals (A17-5)#372thedavidmeister wants to merge 1 commit into
thedavidmeister wants to merge 1 commit into
Conversation
…371) Add a required `underlyingDecimals: number` to `CyToken`. Pre-fix the single `decimals` field was used for both the vault share token's decimals and the underlying token's decimals — same name, two different quantities. The type permitted the mismatch and Lock.svelte rendered underlying balances and parsed user input with the share decimals. When a future cyToken's underlying has different decimals than the share (e.g. a 6-decimal underlying paired with an 18-decimal share), the bug fires: - balance display: 1234567n formatted with 18 decimals reads as 0.000000000001234567 instead of 1.234567 - user input "1" parsed with 18 decimals produces 10**18, a 10**12× over-deposit on a 6-decimal underlying - share output formatted with underlying decimals understates by 10**12 Today every entry in `stores.ts` happens to have matching decimals, so the bug is dormant. The type system was the only line of defence and it didn't enforce the distinction. Changes: - `CyToken.underlyingDecimals: number` required - 15 token entries in `stores.ts` updated (each set to match its `decimals` since current data is consistent) - Lock.svelte: 4 underlying-context uses switched to `underlyingDecimals`; 2 share-output uses stay on `decimals` - ~30 inline CyToken fixtures across 19 test files updated - `mockSelectedCyToken`'s writable now defaults to a cysFLR-shaped CyToken so existing tests still see a populated default; exported `DEFAULT_SELECTED_CYTOKEN` for tests that want to reset - vitest-setup.ts wires `selectedCyToken: mockSelectedCyToken` into the `$lib/stores` mock (no-op for existing tests; a consistent injection point for future tests) A runtime test that renders Lock with a mismatched-decimals fixture and asserts the formatter uses underlyingDecimals didn't make this PR. The mock-instance topology between vitest-setup.ts and the test file produces two separate writables for mockSelectedCyToken, so a set() from the test doesn't reach the rendered component. The structural fix relies on the type system enforcing underlyingDecimals. #370 (filed alongside) tracks a foundry fork-test that verifies every entry's on-chain claim against the deployed `decimals()` so the values in stores.ts can't drift from reality. Closes #371. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
CyTokenhad a singledecimalsfield used for both the vault share token's decimals AND the underlying token's decimals — same name, two different quantities. The type permitted the mismatch andLock.svelterendered underlying balances and parsed user input with the share decimals.When a future cyToken's underlying has different decimals than the share (e.g. a 6-decimal USDC underlying paired with an 18-decimal share), the bug fires: a balance of
1234567nformats as0.000000000001234567instead of1.234567, and user input"1"parses to10**18instead of10**6— a10**12× over-deposit. Today every entry instores.tshappens to have matching decimals, so the bug is dormant; the type system was the only line of defence and it didn't enforce the distinction.Changes:
CyToken.underlyingDecimals: number(required) — seesrc/lib/types.tssrc/lib/stores.tsnow setunderlyingDecimals(matching their existingdecimalssince current data is consistent)Lock.svelte: 4 underlying-context uses switched tounderlyingDecimals(parseUnits user input, balance display, MAX-balance set, underlying-balance label); 2 share-output uses stay ondecimalsCyTokentest fixtures across 19 test files updatedmockSelectedCyToken's writable now defaults to a cysFLR-shaped token; exportedDEFAULT_SELECTED_CYTOKENvitest-setup.tswiresselectedCyToken: mockSelectedCyTokeninto the$lib/storesmock (no-op for existing tests)Closes #371. Companion follow-up filed at #370 (foundry fork-test that verifies every entry's on-chain claim).
Test plan
npm run check— 0 svelte-check errorsnpm run lint-check— cleannpx vitest run— 280/280 passA runtime test that renders Lock with a mismatched-decimals fixture is not in this PR. The mock-instance topology (vitest-setup-file mocks vs per-file imports of
mockSelectedCyToken) produces two separate writables, soset()from the test doesn't propagate to the rendered component. The structural fix relies on the type system enforcingunderlyingDecimalsis supplied at everyCyTokenconstruction site, and #370 tracks the on-chain verification that those claims match deployeddecimals().🤖 Generated with Claude Code