MetaMask / MetaMask/metamask-extension
[Bug]: Added protection (enforced simulations) overwrites the dapp's gas limit with a zero-margin estimate on non-Trusted contracts, even when the option is off
- Dominant language
- TypeScript
- Stars
- 13.2k
- Forks
- 5.6k
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 451
Description
### Describe the bug
When a dapp sends eth_sendTransaction with an explicit gas limit, MetaMask replaces that limit with a fresh eth_estimateGas result, with no buffer, whenever the contract being called is not rated Trusted by Blockaid's address scan (Benign included). We see this on Ethereum and Base. It happens as soon as the confirmation opens, whether or not the user enables "Added protection". The dapp's fees are kept; only the gas limit is overwritten.
For contracts whose gas use rises between the estimate and inclusion, a zero-margin limit makes transactions run out of gas. Lending markets such as Aave, Compound and Morpho update interest on the first transaction in each block, so a deposit estimated in one block can need 5-12% more gas when it lands a few blocks later. Our dapp sets the gas limit to estimate x 1.3 to prevent exactly this, and MetaMask discards it.
Where it happens in the source (v13.50.0):
- shared/lib/transaction/enforced-simulations.ts: isEnforcedSimulationsEligible() returns true when any recipient's cached address-scan result is anything other than Trusted.
- ui/pages/confirmations/components/rows/enforced-simulations-row/enforced-simulations-row.tsx: when eligible, calls applyTransactionContainersExisting() on load, with an empty container list when Added protection is not enabled by default.
- app/scripts/lib/transaction/containers/util.ts, applyTransactionContainers(): deletes gas and gasLimit from txParams, calls TransactionController:estimateGas, and writes the raw result back to txParams.gas, even when no container is applied.
### Expected behavior
1. When no container is applied (Added protection off), leave the dapp-supplied gas limit unchanged.
2. When a container is applied, never set a gas limit below the one the dapp supplied, and add a margin to the new estimate. Wrapping adds gas, and state can change before inclusion.
### Screenshots/Recordings
_No response_
### Steps to reproduce
1. On Ethereum or Base, use a dapp that sends eth_sendTransaction with an explicit gas value above the estimate (for example, estimate x 1.3) to a contract that Blockaid's address scan rates Benign rather than Trusted.
2. When the confirmation opens, leave "Added protection" unticked.
3. Open Edit network fee -> Advanced. The gas limit shown is the bare eth_estimateGas result, not the dapp's value.
4. Confirm. The transaction is sent with the bare estimate as its gas limit.
Per the code above, a recipient rated Trusted is not eligible, and its dapp-supplied limit is kept.
### Error messages or log output
```shell
What a zero-margin limit does to these contracts, on Base, 13 September 2026: a USDC deposit into an ERC-4626 vault ran out of gas with its limit equal to the bare estimate (limit 547,111, gas used 541,288), failing inside the nested Morpho call:
https://basescan.org/tx/0x890f555ac73cc9ef1b82cfcf26360e38646173c0a7939cc65d8168ab8430fbee
(This one was sent before our dapp began supplying its own limit, so MetaMask's estimate was used as is.)
On forks of Ethereum and Base, every one of our 18 vault and portfolio deposits sent 30 seconds after its estimate with a zero-margin limit ran out of gas. With estimate x 1.3, all deposits and withdrawals succeeded, even when sent an hour after the estimate.
```
### Where was this bug found?
Live version (from official store)
### Version
13.46.1
### Build type
None
### Browser
Chrome
### Operating system
MacOS
### Hardware wallet
_No response_
### Additional context
We build ACCRUE (https://accrue.cc), a USDC vault platform on Ethereum and Base. Blockaid rates our contracts Benign but not Trusted, so every MetaMask user depositing with us is affected. We are happy to provide traces or test transactions.
### Severity
_No response_
Contributor guide
Research direction
Start in shared/lib/transaction/enforced-simulations.ts and trace the confirmation path in ui/pages/confirmations/components/rows/enforced-simulations-row/enforced-simulations-row.tsx into app/scripts/lib/transaction/containers/util.ts. Reproduce with an explicit gas value and Added protection off, then verify that an empty container list preserves the dapp's gas limit and that an applied container never lowers it or removes its margin.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- blockchain, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100