MetaMask / MetaMask/metamask-extension
Signature confirmation crashes with "BigNumber Error: times() number type has more than 15 significant digits" when rendering `eth_signTypedData_v4` in the popup window
- Dominant language
- TypeScript
- Stars
- 13.2k
- Forks
- 5.6k
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 451
Description
## Describe the bug
When a dApp requests `eth_signTypedData_v4` and MetaMask is running as a **popup / detached window**, the confirmation screen fails to render. Instead of the signature UI, MetaMask shows **"MetaMask encountered an error - Your information can't be shown."**
The error card reports:
```
BigNumber Error: times() number type has more than 15 significant digits: 0.07619191169906189
Code: BigNumber Error
```
The **identical** request renders and signs correctly when MetaMask is docked to the **side panel**. So the defect is specific to the popup rendering / typed-data decoding path, not to the request.
This reproduces with all other browser extensions disabled and is independent of any third-party extension. It also reproduces across Chromium browsers (confirmed in both Chrome and Brave), so it is not browser-specific.
## Steps to reproduce
1. Run MetaMask in the default **popup** mode (not docked to the side panel).
2. Go to `polymarket.com`, connect the wallet on Polygon, and place a CLOB order (Buy / Sell).
3. Polymarket requests `eth_signTypedData_v4` for a `Polymarket CTF Exchange` `Order` (payload below).
4. The popup shows "MetaMask encountered an error" with the BigNumber error, instead of the signature confirmation.
5. Dock MetaMask to the **side panel** and retry the identical order -> it renders and signs correctly.
## Expected behavior
The popup renders the typed-data signature confirmation (same as the side panel) and lets the user sign or reject. A display-only value that cannot be formatted should degrade to "details unavailable" rather than crashing the whole confirmation.
## Analysis
`bignumber.js` in DEBUG mode throws when a `BigNumber` is constructed from - or multiplied by - a primitive JS `number` with more than 15 significant digits. `0.07619191169906189` has 17. Somewhere in the popup's signature-decode / preview path a raw float is passed into `new BigNumber(x)` / `.times(x)` instead of a string. Every frame in the stack is a MetaMask bundle:
```
BigNumber Error: times() number type has more than 15 significant digits: 0.07619191169906189
at $ (vendor-cdd60c62..js:3:39085) // BigNumber error ctor
at new N (vendor-cdd60c62..js:3:37286) // new BigNumber(...)
at _.times._.mul (vendor-cdd60c62..js:3:48852) // .times()
at _ (133..js:1:106345) // signature decode / preview
at p (133..js:1:104321)
at g (133..js:1:104967)
at v (133..js:1:5667)
at G (133..js:1:157066)
... React reconciler (vendor-5a94f17d..js) ...
```
Note: the wallet's simulation returns `{ "stateChanges": [], "error": "Unsupported signature" }` for this request. That is expected and unrelated (an off-chain order signature has no on-chain state change); it is not the cause of the crash.
## Request payload
```json
{
"method": "eth_signTypedData_v4",
"origin": "https://polymarket.com",
"params": [
"0x",
{
"domain": {
"name": "Polymarket CTF Exchange",
"version": "2",
"chainId": "137",
"verifyingContract": "0xe111180000d2663c0091e4f400237545b87b996b"
},
"primaryType": "Order",
"message": {
"salt": "1673202389268",
"maker": "0x",
"signer": "0x",
"tokenId": "113899799237951007160327893359215790094139814320491939136784205037742098170284",
"makerAmount": "1000000",
"takerAmount": "1004010",
"timestamp": "1783622778949",
"side": "0",
"signatureType": "2",
"metadata": "0x0000000000000000000000000000000000000000000000000000000000000000",
"builder": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
}
]
}
```
All `message` fields are integer strings / hex. No float is present in the payload - the `0.07619191169906189` value is derived inside MetaMask's preview rendering.
## Suggested fix
1. Construct BigNumbers from **strings**, never from primitive JS numbers, in the signature-decode / preview path: `new BigNumber(String(x))` / `bn.times(x.toString())` (or round with `.toFixed()` first).
2. Wrap the preview computation so a formatting failure degrades gracefully instead of unmounting the whole confirmation - a display-only value should never block signing.
3. Reconcile the popup path with the side-panel path, which already handles this value correctly.
## Environment
- **MetaMask** 13.38.2 - **popup / detached-window** mode reproduces; **side panel** does not.
- Reproduced in **two Chromium browsers** on macOS (arm64):
- Chrome 149.0.7827.201 (Official Build, arm64)
- Brave 1.92.134 (Official Build, arm64) / Chromium 150.0.7871.63
- macOS (Darwin 24.5.0).
- dApp: `polymarket.com`, Polymarket CTF Exchange v2 `Order`, chain 137 (Polygon).
---
Reported by Yoaz Shmider, Kerberus Cyber Security .
Contributor guide
Research direction
Start by tracing the popup signature-decode and preview path referenced by the stack, then compare it with the side-panel rendering path, using the vendor-cdd60c62 and 133 bundle references as entry points. Reproduce with the supplied eth_signTypedData_v4 payload and verify that the popup renders the confirmation and handles an unformatable display value without blocking signing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100