Skip to content

Conversation

@Kriys94
Copy link

@Kriys94 Kriys94 commented Jan 12, 2026

Description

This PR adds a new required rawAmount field to the BalanceStruct and FungibleAssetAmountStruct types in the keyring API. This field stores the raw blockchain balance without decimals applied, providing precision for calculations using BigInt/BigNumber.

Motivation

The Problem

When snaps return balance data via keyring_getAccountBalances, the amount field contains the human-readable value with decimals already applied (e.g., "1.5" for 1.5 SOL). However, this causes two issues:

Double division bug: If amount is already normalized ("1.5"), dividing by decimals again produces incorrect values
Precision loss: parseFloat() has limited precision (~15-17 significant digits), causing data loss for large numbers:
parseFloat("1000000000000000001"); // → 1000000000000000000 (last digit lost!)
parseFloat("1000000000000000001"); // → 1000000000000000000 (last digit lost!)

This PR
Add a rawAmount field that stores the exact blockchain value as a string:
{
amount: "1.5", // UI amount (backward compatible)
rawAmount: "1500000000", // Raw blockchain value (precise)
unit: "SOL"
}
{ amount: "1.5", // UI amount (backward compatible) rawAmount: "1500000000", // Raw blockchain value (precise) unit: "SOL"}

This enables:

Backward compatibility: Existing consumers continue using amount
Precision: New consumers use rawAmount with BigInt/BigNumber for exact calculations

Changes

@metamask/keyring-api
BalanceStruct - Used by keyring_getAccountBalances response:

export const BalanceStruct = object({
  amount: StringNumberStruct,   // Human-readable (e.g., "1.5")
  unit: string(),               // Symbol (e.g., "SOL")
  rawAmount: string(),          // NEW: Raw value (e.g., "1500000000")
});

export const BalanceStruct = object({ amount: StringNumberStruct, // Human-readable (e.g., "1.5") unit: string(), // Symbol (e.g., "SOL") rawAmount: string(), // NEW: Raw value (e.g., "1500000000")});

FungibleAssetAmountStruct - Used by AccountBalancesUpdated event and transaction assets:

export const FungibleAssetAmountStruct = object({
  unit: string(),
  amount: StringNumberStruct,
  rawAmount: string(),          // NEW
});

Snap Updates (Solana, Tron, BTC)
Updated getAccountBalances to return both fields:

acc[asset.assetType] = {
  unit: asset.symbol,
  amount: asset.uiAmount,
  rawAmount: asset.rawAmount,  // NEW
};

Breaking Change
This is a breaking change - all snaps implementing keyring_getAccountBalances must now return rawAmount. All snaps have been updated accordingly.

Testing
Updated unit tests for BalanceStruct validation
Updated type definition tests (*.test-d.ts)
Updated mock data in KeyringClient.test.ts and SnapKeyring.test.ts


Note

Breaking change: precise balances via rawAmount

  • Require rawAmount in Balance and FungibleAssetAmount (balance.ts, asset.ts); keep human-readable amount for compatibility
  • Update AssetStruct, transaction fee/participant examples, and docs to include rawAmount; refine comments for clarity
  • Enforce rawAmount in AccountBalancesUpdated payloads; adjust validation and type tests (events.test-d.ts, balance.test(-d).ts, asset.test(-d).ts, transaction.test(-d).ts)
  • Propagate changes to snap bridge/client tests: balances, fees, and mocked responses now include rawAmount (SnapKeyring.test.ts, KeyringClient.test.ts)
  • Changelog notes breaking change and rationale

Written by Cursor Bugbot for commit 7b64a6a. This will update automatically on new commits. Configure here.

@Kriys94 Kriys94 changed the title feat(Asset): return raw assets amount feat(keyring-api): return raw assets amount Jan 13, 2026
@Kriys94 Kriys94 marked this pull request as ready for review January 13, 2026 14:10
@Kriys94 Kriys94 requested a review from a team as a code owner January 13, 2026 14:10
* The raw blockchain balance without decimals applied (e.g., "1500000000" for 1.5 SOL).
* This provides precision for calculations using BigInt/BigNumber.
*/
rawAmount: string(),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rawAmount lacks numeric validation unlike amount field

Medium Severity

The rawAmount field uses string() for validation while amount uses StringNumberStruct which validates the string is numeric. This inconsistency allows non-numeric values like "hello" or "" to pass validation for rawAmount. Since the PR states rawAmount is intended for "calculations using BigInt/BigNumber", invalid non-numeric strings would cause runtime errors when consumers attempt BigInt(rawAmount). Using the same StringNumberStruct (or a stricter integer-only pattern) would provide consistent validation and catch invalid values at the API boundary rather than during calculations.

Additional Locations (1)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants