Skip to content

feat(transaction-controller): gate internal validations on explicit isInternal flag#8633

Open
matthewwalsh0 wants to merge 2 commits intomainfrom
feat/transaction-controller-is-internal
Open

feat(transaction-controller): gate internal validations on explicit isInternal flag#8633
matthewwalsh0 wants to merge 2 commits intomainfrom
feat/transaction-controller-is-internal

Conversation

@matthewwalsh0
Copy link
Copy Markdown
Member

@matthewwalsh0 matthewwalsh0 commented Apr 29, 2026

Explanation

The TransactionController currently distinguishes "internal" (MetaMask-initiated) requests from "external" (dapp-initiated) requests by checking whether the request origin matches ORIGIN_METAMASK. Several validations in addTransaction and addTransactionBatch are skipped for internal requests:

  • Permitted-address check
  • EIP-7702 authorization rejection
  • Calls-with-data targeting an internal account
  • Duplicate batchId check
  • Batch size limit
  • Dapp-suggested gas fee handling and userFeeLevel selection

Inferring trust from the origin string couples the controller's security posture to whatever logic the consuming client happens to use when populating that field. A bug, refactor, or oversight further up the stack that lets a non-internal request reach the controller with origin: ORIGIN_METAMASK would silently bypass these validations.

This PR replaces that implicit check with an explicit, optional isInternal: boolean flag on both AddTransactionOptions and TransactionBatchRequest. The flag defaults to false, so the controller treats all requests as external unless the caller opts in. The flag is persisted on TransactionMeta and TransactionBatchMeta so downstream logic (notably userFeeLevel selection in gas-fees.ts) reads it directly rather than re-deriving trust from the origin.

The origin field is unchanged in shape and semantics — it still flows through to the ApprovalController as the display origin and is still attached to TransactionMeta for analytics, history, and dapp-suggested-gas display. It is simply no longer used as a trust signal.

BREAKING: validations are no longer gated on origin

Callers that previously relied on origin: ORIGIN_METAMASK (or an absent origin) to suppress these validations must now pass isInternal: true explicitly. Without that flag the controller will:

  • Reject EIP-7702 transactions
  • Enforce the permittedAddresses check on the from address
  • Enforce the batch size limit
  • Reject calls-with-data targeting an internal account
  • Reject duplicate batchIds
  • Treat the transaction as dapp-suggested for userFeeLevel

Internal MetaMask consumers (extension, mobile, and other core packages such as transaction-pay-controller, bridge-status-controller, perps-controller, and eip-5792-middleware) need to audit their call sites and pass isInternal: true for every request that genuinely originates inside MetaMask.

References

N/A

Changelog

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've highlighted breaking changes using the "BREAKING" category above as appropriate
  • I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes

Note

Medium Risk
Breaking change that alters how transaction/batch requests are classified as trusted, affecting validation and gas-fee behavior; incorrect caller updates could cause new rejections or changed fee-level selection.

Overview
Adds an explicit optional isInternal flag to addTransaction/addTransactionBatch requests and persists it on TransactionMeta/TransactionBatchMeta.

BREAKING: internal-only bypasses previously inferred from origin === ORIGIN_METAMASK (or missing origin) are now gated by isInternal, impacting origin validation, duplicate batchId handling, batch request validation (internal-account call-data + size limit), and dapp-suggested gas fee / userFeeLevel selection. Tests and related helpers are updated to use isInternal instead of ORIGIN_METAMASK checks.

Reviewed by Cursor Bugbot for commit e84d726. Bugbot is set up for automated code reviews on this repo. Configure here.

@matthewwalsh0
Copy link
Copy Markdown
Member Author

@metamaskbot publish-preview

@github-actions
Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]
@metamask-previews/[email protected]

…sInternal flag

Replace implicit `origin === ORIGIN_METAMASK` trust checks in
`addTransaction` and `addTransactionBatch` with an explicit, optional
`isInternal` flag (defaulting to `false`). The flag is persisted on
`TransactionMeta` and `TransactionBatchMeta` so downstream logic can
read it without re-deriving trust from the origin string.

BREAKING: Setting `origin` to `ORIGIN_METAMASK` (or omitting `origin`)
no longer skips the external-request validations. Callers that need
that behavior must now pass `isInternal: true` explicitly.
@matthewwalsh0 matthewwalsh0 force-pushed the feat/transaction-controller-is-internal branch from 702a30e to e84d726 Compare May 1, 2026 15:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants