MetaMask / MetaMask/metamask-extension
Privacy: full unsigned transaction is sent to the closed-source tx-sentinel backend before signing
- Dominant language
- TypeScript
- Stars
- 13.2k
- Forks
- 5.6k
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 451
Description
### What is this about?
When a transaction confirmation screen opens, MetaMask sends the complete transaction (from, to, full data/calldata, value, gas params) to https://tx-sentinel-.api.cx.metamask.io/ via the private infura_simulateTransactions JSON-RPC method, to produce the "Estimated changes" preview. This happens before the user signs, and repeats every ~3s while the screen is open — so even transactions the user ultimately rejects have already had their full intent transmitted.
The domains are disclosed in privacy-snapshot.json, and the payload is unsigned (cannot be broadcast). I'm not reporting an exploit. I'd like to raise a privacy / trust / user-control concern and ask for documentation and options.
## Concerns
1. Pre-signature intent disclosure — full transaction intent (address + full calldata + dApp origin via the security-alerts call) leaves the device before any signature, including for later-rejected transactions.
2. Possible front-running / MEV information asymmetry — because the full swap intent (input token, amount, target pool) is visible to the backend before the transaction reaches the public mempool, a party that could observe the payload (e.g. a compromised backend, an insider, or a network MITM) might gain an earlier, pre-mempool view of what a user is about to sign, and could in principle position a sandwich/front-run ahead of the broadcast. To be clear, this is not transaction replay — the payload is unsigned and cannot be broadcast; the concern is a possible timing-based information advantage, and it may be amplified by a single endpoint aggregating intent from many users in structured form, earlier than the public mempool would.
3. Closed-source trust root — tx-sentinel has no public source (unlike MetaMask/core). Its output influences what users see before signing; there's no way to audit how results are produced, and a compromise could return misleading previews.
4. No data-handling disclosure — the domain is listed, but there's no public statement of what fields are logged, retention, or whether payloads are used beyond simulation.
5. Custom RPC not honored — users with their own/self-hosted node still send intent to MetaMask's backend; simulation bypasses the configured RPC. eth_simulateV1 now exists as a standards-based alternative.
## Existing mitigations (acknowledged)
Unsigned payload (not broadcastable); toggle (useTransactionSimulations); domains in privacy-snapshot.json; final balance-change math is in open-source MetaMask/core.
## Requests
1. Document the infura_simulateTransactions payload: exact fields, timing/frequency, retention, usage.
2. Open-source tx-sentinel, or publish a data-handling statement for it.
3. Allow a user-configurable simulation endpoint (self-host, or route via the user's RPC using eth_simulateV1).
4. Make the toggle copy explicit that the full transaction is sent to MetaMask servers pre-signature.
---
### Scenario
_No response_
### Design
_No response_
### Technical Details
This is a design/privacy concern, not a malfunction, so there are no crash-style reproduction steps. The endpoint behavior can be reproduced anonymously (no auth required):
curl -s -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"infura_simulateTransactions","params":[{"transactions":[{"from":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045","to":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045","value":"0x1"}]}]}' \
https://tx-sentinel-ethereum-mainnet.api.cx.metamask.io/
Returns a full stateDiff / callTrace / fees object. The same endpoint also answers standard methods (eth_blockNumber, eth_chainId → 0x1), i.e. it is a public JSON-RPC gateway exposing a private infura_simulateTransactions extension method. In the extension, the full confirmation-page transaction is what gets sent as the params here, before signing.
---
### Threat Modeling Framework
- What are we working on? Pre-signature transaction simulation via a MetaMask backend.
- What can go wrong? Intent linkage (address ↔ IP ↔ dApp ↔ calldata) before signing, including rejected txs; a possible front-running surface from pre-mempool intent visibility aggregated across users; misleading previews if the backend is compromised; no opt-out short of losing the feature.
- What are we going to do about it? Docs, open-sourcing/disclosure, configurable endpoint, clearer toggle.
- Did we do a good job? Users can audit or self-host the simulation path; disclosure matches reality.
---
### Acceptance Criteria
_No response_
### Stakeholder review needed before the work gets merged
- [ ] Engineering (needed in most cases)
- [ ] Design
- [ ] Product
- [ ] QA (automation tests are required to pass before merging PRs but not all changes are covered by automation tests - please review if QA is needed beyond automation tests)
- [ ] Security
- [ ] Legal
- [ ] Marketing
- [ ] Management (please specify)
- [ ] Other (please specify)
### References
- privacy-snapshot.json (tx-sentinel domains)
- #15576, #16281
- Swagger: tx-sentinel-.api.cx.metamask.io/swagger/
---
Contributor guide
Research direction
Start by reading privacy-snapshot.json, the referenced issues #15576 and #16281, and the tx-sentinel Swagger endpoint. Trace where transaction simulations and the useTransactionSimulations toggle are handled, then confirm the requested scope with maintainers. Done would require agreed acceptance criteria for disclosure, endpoint configuration, and user-facing privacy wording.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend-api-design, documentation, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100