Files: frontend/components/forms/MintForm.tsx line 78; TransferForm.tsx line 67; BurnForm.tsx line 66; frontend/app/dashboard/[contractId]/components/admin/adminActions.ts lines 111 to 113; frontend/app/dashboard/[contractId]/components/TransferPanel.tsx line 200; frontend/app/my-account/components/OutgoingAllowancesSection.tsx line 70; frontend/app/dashboard/[contractId]/components/admin/MintCard.tsx line 195
Issue: Wave 8 found two scaling paths that disagreed. There are now five, and the worst three ignore the token's decimals altogether:
// MintForm.tsx:78, TransferForm.tsx:67, BurnForm.tsx:66 — identical
BigInt(Math.floor(parseFloat(formData.amount) * 1e7)),
1e7 is hardcoded. These three forms never read decimals. On a 6-decimal token every mint, transfer and burn is inflated 10x; on an 18-decimal token it is short by a factor of 10^11. The token contract accepts any decimal <= 18, and the deploy wizard offers the choice, so this is reachable by design rather than by accident.
The new admin code introduced a second float helper instead of importing the shared one:
// adminActions.ts:111 — parallel to lib/utils.ts:14, same bug, different name
export function scaleAmount(amount: string, decimals: number): bigint {
return BigInt(Math.round(parseFloat(amount) * 10 ** decimals));
}
Every one of the 14 registered admin actions routes through scaleAmount, so the AdminPanel split inherited #395's precision loss wholesale. TransferPanel.tsx:200 is a third inline copy (Math.round(parseFloat(data.amount) * 10 ** tokenDecimals)). OutgoingAllowancesSection.tsx:70 does the inverse in floating point for display (Number(amount) / 10 ** tokenInfo.decimals), and MintCard.tsx:195 sums a batch-mint total with acc + Number(curr.amount).
This is the same drift channel that produced #83 and, before it, #29 and #54. Five hand-written copies of one conversion is why it keeps recurring.
Fix: Land #395's exact helper first, export it as the only scaling primitive along with its inverse fromBaseUnits, then delete the other four implementations and thread real decimals into the three forms. Add a lint rule or a unit test that fails on a literal 1e7 or 10 ** in app/ and components/, since this is the fourth wave to find hand-rolled scaling.
Files:
frontend/components/forms/MintForm.tsxline 78;TransferForm.tsxline 67;BurnForm.tsxline 66;frontend/app/dashboard/[contractId]/components/admin/adminActions.tslines 111 to 113;frontend/app/dashboard/[contractId]/components/TransferPanel.tsxline 200;frontend/app/my-account/components/OutgoingAllowancesSection.tsxline 70;frontend/app/dashboard/[contractId]/components/admin/MintCard.tsxline 195Issue: Wave 8 found two scaling paths that disagreed. There are now five, and the worst three ignore the token's decimals altogether:
1e7is hardcoded. These three forms never readdecimals. On a 6-decimal token every mint, transfer and burn is inflated 10x; on an 18-decimal token it is short by a factor of 10^11. The token contract accepts anydecimal <= 18, and the deploy wizard offers the choice, so this is reachable by design rather than by accident.The new admin code introduced a second float helper instead of importing the shared one:
Every one of the 14 registered admin actions routes through
scaleAmount, so the AdminPanel split inherited #395's precision loss wholesale.TransferPanel.tsx:200is a third inline copy (Math.round(parseFloat(data.amount) * 10 ** tokenDecimals)).OutgoingAllowancesSection.tsx:70does the inverse in floating point for display (Number(amount) / 10 ** tokenInfo.decimals), andMintCard.tsx:195sums a batch-mint total withacc + Number(curr.amount).This is the same drift channel that produced #83 and, before it, #29 and #54. Five hand-written copies of one conversion is why it keeps recurring.
Fix: Land #395's exact helper first, export it as the only scaling primitive along with its inverse
fromBaseUnits, then delete the other four implementations and thread realdecimalsinto the three forms. Add a lint rule or a unit test that fails on a literal1e7or10 **inapp/andcomponents/, since this is the fourth wave to find hand-rolled scaling.