fix(rcart): fix floating-point precision in wallet payment amounts

Replace parseFloat * 10^decimals with string-based decimal parsing to
avoid precision loss for 18-decimal tokens (ETH). Affects both MetaMask
and EncryptID payment paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-12 21:11:03 +00:00
parent f5ac038803
commit 8345648d61
1 changed files with 16 additions and 4 deletions

View File

@ -216,7 +216,7 @@ class FolkPaymentPage extends HTMLElement {
if (p.token === 'ETH') {
// Native ETH transfer
const weiAmount = BigInt(Math.round(parseFloat(effectiveAmount) * 1e18));
const weiAmount = this.parseTokenAmount(effectiveAmount, 18);
txHash = await signer.sendTransaction({
from: this.walletAccount,
to: p.recipientAddress,
@ -229,7 +229,7 @@ class FolkPaymentPage extends HTMLElement {
if (!usdcAddress) throw new Error('USDC not supported on this chain');
const decimals = p.token === 'USDC' ? 6 : 18;
const rawAmount = BigInt(Math.round(parseFloat(effectiveAmount) * (10 ** decimals)));
const rawAmount = this.parseTokenAmount(effectiveAmount, decimals);
// transfer(address to, uint256 amount) — selector: 0xa9059cbb
const recipient = p.recipientAddress.slice(2).toLowerCase().padStart(64, '0');
@ -303,7 +303,7 @@ class FolkPaymentPage extends HTMLElement {
txHash = await client.sendTransaction({
account,
to: p.recipientAddress as `0x${string}`,
value: BigInt(Math.round(parseFloat(effectiveAmount) * 1e18)),
value: this.parseTokenAmount(effectiveAmount, 18),
chain,
});
} else {
@ -312,7 +312,7 @@ class FolkPaymentPage extends HTMLElement {
if (!usdcAddress) throw new Error('USDC not supported on this chain');
const decimals = p.token === 'USDC' ? 6 : 18;
const rawAmount = BigInt(Math.round(parseFloat(effectiveAmount) * (10 ** decimals)));
const rawAmount = this.parseTokenAmount(effectiveAmount, decimals);
const recipient = p.recipientAddress.slice(2).toLowerCase().padStart(64, '0');
const amountHex = rawAmount.toString(16).padStart(64, '0');
@ -347,6 +347,18 @@ class FolkPaymentPage extends HTMLElement {
return this.customAmount || '0';
}
/**
* Parse a decimal amount string into a BigInt with the given decimals.
* Avoids floating-point precision loss for 18-decimal tokens.
* e.g. parseTokenAmount("1.5", 18) 1500000000000000000n
*/
private parseTokenAmount(amount: string, decimals: number): bigint {
const parts = amount.split('.');
const whole = parts[0] || '0';
let frac = (parts[1] || '').slice(0, decimals).padEnd(decimals, '0');
return BigInt(whole) * BigInt(10 ** decimals) + BigInt(frac);
}
// ── Status update ──
private async updatePaymentStatus(status: string, method: string, txHash?: string | null, transakOrderId?: string | null) {