Files: frontend/lib/utils.ts lines 14 to 18; frontend/lib/__tests__/utils.test.ts; frontend/app/hooks/useDeployToken.ts line 284; frontend/app/deploy/DeployForm.tsx line 65
Issue: The plumbing #320 asked for is correct. The schema types supply as a string (initialSupply: integerString), the value stays a string through the form, and useDeployToken routes through one shared helper rather than scaling inline. Then the helper throws the guarantee away on its first line:
export function toBaseUnits(display: number | string, decimals: number): bigint {
const amount = typeof display === "string" ? Number(display) : display;
if (isNaN(amount)) return 0n;
return BigInt(Math.round(amount * (10 ** decimals)));
}
Number(display) puts the value back into an IEEE-754 double before the multiply, which is precisely what #253 and then #320 were raised to stop. Wrapping the result in BigInt does not recover precision that is already gone.
The multiply is exact only while supply * 5^decimals < 2^53, so the safe ceiling collapses as decimals rise:
| decimals |
largest exactly-scaled supply |
| 7 |
115,292,150,460 |
| 9 |
4,611,686,018 |
| 12 |
36,893,488 |
| 18 |
2,361 |
The contract permits decimal <= 18 and the deploy wizard lets the user choose it. Measured against the current helper:
- 1,000,000 supply at 18 decimals mints
999999999999999983222784 instead of 1000000000000000000000000 — 16,777,216 base units short.
- 123,456,789,012,345 supply at 7 decimals is 41,600 base units short.
The reason this passed review is the test. lib/__tests__/utils.test.ts covers 1,000,000 at 7 decimals and 0.1 at 7 decimals — small, round values that survive the float exactly — including the "assert the ScVal built for a 1,000,000 / 7-decimal token" case that #319 specifically asked for. Every assertion passes against the buggy helper, so the test gives false confidence rather than coverage.
Fix: Do the scaling with string arithmetic and never touch Number: split on ., reject more than decimals fraction digits, right-pad the fraction to decimals, concatenate, and BigInt the result. Add the two failing cases above as regression tests, because anything smaller passes either way.
Files:
frontend/lib/utils.tslines 14 to 18;frontend/lib/__tests__/utils.test.ts;frontend/app/hooks/useDeployToken.tsline 284;frontend/app/deploy/DeployForm.tsxline 65Issue: The plumbing #320 asked for is correct. The schema types supply as a string (
initialSupply: integerString), the value stays a string through the form, anduseDeployTokenroutes through one shared helper rather than scaling inline. Then the helper throws the guarantee away on its first line:Number(display)puts the value back into an IEEE-754 double before the multiply, which is precisely what #253 and then #320 were raised to stop. Wrapping the result inBigIntdoes not recover precision that is already gone.The multiply is exact only while
supply * 5^decimals < 2^53, so the safe ceiling collapses as decimals rise:The contract permits
decimal <= 18and the deploy wizard lets the user choose it. Measured against the current helper:999999999999999983222784instead of1000000000000000000000000— 16,777,216 base units short.The reason this passed review is the test.
lib/__tests__/utils.test.tscovers 1,000,000 at 7 decimals and 0.1 at 7 decimals — small, round values that survive the float exactly — including the "assert the ScVal built for a 1,000,000 / 7-decimal token" case that #319 specifically asked for. Every assertion passes against the buggy helper, so the test gives false confidence rather than coverage.Fix: Do the scaling with string arithmetic and never touch
Number: split on., reject more thandecimalsfraction digits, right-pad the fraction todecimals, concatenate, andBigIntthe result. Add the two failing cases above as regression tests, because anything smaller passes either way.