Skip to content

fetchJettonBalance hard-codes 9 decimals for any non-TON jetton (USDT shows 1000× too small) #2

Description

@sepivip

What happens

When <TonPayButton> is configured for a non-TON asset (e.g. asset="EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs", USDT mainnet), the "Insufficient Funds" modal shows a balance 1000× smaller than reality — a wallet with 20 USDT displays as 0.02 USDT.

Root cause

fetchJettonBalance in packages/ui-react/src/hooks/useWalletBalance.ts reads:

const decimals = wallet.jetton?.decimals ?? 9;

But the toncenter v3 /api/v3/jetton/wallets response returns wallet.jetton as the master address string, not an object:

{
  "jetton_wallets": [{
    "balance": "20000000",
    "jetton": "0:B113A994B5024A16719F69139328EB759596C38A25F59028B146FECDC3621DFE"
  }],
  "metadata": {
    "0:B113...": {
      "token_info": [{
        "extra": { "decimals": "6" }
      }]
    }
  }
}

So .decimals on a string is undefined → falls back to 9. USDT has 6 decimals → balance shown as raw / 10^9 instead of raw / 10^6.

Downstream consequence

Any merchant integrating USDT sees users wrongly routed to the "Top up with crypto" view. Users who pay via that QR flow bypass the handlePay callback — so the merchant's reference never gets attached, the transfer completes on-chain, but the order stays pending forever (pay.ton.org only indexes transfers that were built through the SDK handshake with a reference).

Suggested fix

Resolve decimals from metadata[masterAddr].token_info[0].extra.decimals (where toncenter v3 actually puts them), with a fallback for the legacy shape in case the response evolves:

let decimals = 9;
const masterAddr = typeof wallet.jetton === "string" ? wallet.jetton : null;
const fromMetadata = masterAddr
  ? data?.metadata?.[masterAddr]?.token_info?.[0]?.extra?.decimals
  : undefined;
if (fromMetadata != null) {
  const parsed = Number(fromMetadata);
  if (Number.isFinite(parsed)) decimals = parsed;
} else if (typeof wallet.jetton?.decimals === "number") {
  decimals = wallet.jetton.decimals;
}

Affects every jetton whose decimals ≠ 9. Version observed: @ton-pay/ui-react@0.3.2.

Happy to open a PR if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions