Carried over from MetaMask/utils#303, which was open when @metamask/utils moved into core.
toWei silently accepts fractional wei and returns the wrong number. Confirmed against main today:
import { toWei } from '@metamask/utils';
toWei('0.5', 'wei'); // 5n
toWei('1.5', 'wei'); // 6n
toWei('0.9', 'wei'); // 9n
toWei('1', 'wei'); // 1n
Wei is the base unit, so anything with a fractional part should be rejected rather than scaled.
The cause is in packages/utils/src/unitsConversion.ts:
const unitLengths = Object.fromEntries(
Object.entries(unitMap).map(([key, value]) => [key, value.length - 1 || 1]),
) as Record<EthereumUnit, number>;
Wei's raw value is '1', so value.length - 1 is 0, which is the correct precision for the base unit. The || 1 fallback then turns that 0 into 1, so the fraction gets treated as one decimal place of a larger unit.
The original PR has the fix plus tests.
Carried over from MetaMask/utils#303, which was open when
@metamask/utilsmoved into core.toWeisilently accepts fractional wei and returns the wrong number. Confirmed againstmaintoday:Wei is the base unit, so anything with a fractional part should be rejected rather than scaled.
The cause is in
packages/utils/src/unitsConversion.ts:Wei's raw value is
'1', sovalue.length - 1is0, which is the correct precision for the base unit. The|| 1fallback then turns that0into1, so the fraction gets treated as one decimal place of a larger unit.The original PR has the fix plus tests.