Skip to content

feat: shield controller wire in wallet package#9616

Open
tuna1207 wants to merge 8 commits into
mainfrom
feat/shield-controller-mm-wallet
Open

feat: shield controller wire in wallet package#9616
tuna1207 wants to merge 8 commits into
mainfrom
feat/shield-controller-mm-wallet

Conversation

@tuna1207

@tuna1207 tuna1207 commented Jul 23, 2026

Copy link
Copy Markdown
Member

Explanation

Adds ShieldController as a default initialized unit in @metamask/wallet.

@metamask/wallet is the shared controller-integration layer for extension, mobile, and other clients. Build-specific and runtime values are injected via instanceOptions rather than hardcoded.

Breaking changes

  • BREAKING: instanceOptions.shieldController is now required with at least baseUrl and fetchFunction.
  • When passing a full backend override, baseUrl and fetchFunction remain required at the type level but are unused for backend construction.
  • Hosts must register AuthenticationController and SignatureController on the wallet root messenger.
  • Hosts must explicitly call ShieldController:start after wiring - wallet initialization constructs the controller but does not auto-start it.

References

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 communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

High Risk
Breaking change for all wallet consumers; Shield touches transaction/signature coverage and auth token wiring, so misconfiguration or missing peer controllers could affect security-related flows.

Overview
Breaking: @metamask/wallet now initializes ShieldController by default and requires a new instanceOptions.shieldController slot (baseUrl, fetchFunction, or an injected backend).

The wallet adds a shield initialization module that builds ShieldRemoteBackend by default (optional getAccessToken, polling, history limits, normalizeSignatureRequest), delegates AuthenticationController:getBearerToken and transaction/signature stateChange events on the root messenger, and does not auto-start Shield—hosts must register AuthenticationController and SignatureController and call ShieldController:start explicitly.

wallet-cli passes the production ruleset URL (https://ruleset-engine.api.cx.metamask.io) and fetch for the daemon. shield-controller tests update the default API host to the same URL. Package deps add @metamask/shield-controller and @metamask/signature-controller; CODEOWNERS and README dependency graph are updated.

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

@tuna1207
tuna1207 marked this pull request as ready for review July 23, 2026 00:58
@tuna1207
tuna1207 requested review from a team as code owners July 23, 2026 00:58
@tuna1207
tuna1207 temporarily deployed to default-branch July 23, 2026 00:58 — with GitHub Actions Inactive

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0fc9607. Configure here.

Comment thread packages/wallet-cli/src/daemon/wallet-factory.ts
@tuna1207
tuna1207 requested a review from a team as a code owner July 23, 2026 01:29
options: ShieldControllerInstanceOptions;
}) => {
return new ShieldController({
messenger: messenger as unknown as ShieldControllerMessenger,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why need casting here?

Comment on lines +62 to +64
transactionHistoryLimit: options.transactionHistoryLimit,
coverageHistoryLimit: options.coverageHistoryLimit,
normalizeSignatureRequest: options.normalizeSignatureRequest,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just forward instead of hard assigning each.

Suggested change
transactionHistoryLimit: options.transactionHistoryLimit,
coverageHistoryLimit: options.coverageHistoryLimit,
normalizeSignatureRequest: options.normalizeSignatureRequest,
...options,

import type { SignatureStateChange } from '@metamask/signature-controller';
import type { TransactionControllerStateChangeEvent } from '@metamask/transaction-controller';

export type ShieldBackend = Pick<

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to modify this?

I've noticed that this is being used in ShieldControllerInstanceOptions. I don't think we will ever provide the custom backend instance here.

Comment on lines +21 to +27
export type ShieldControllerInitializationMessenger = Messenger<
'ShieldController',
ShieldControllerActions | AuthenticationControllerGetBearerTokenAction,
| ShieldControllerEvents
| SignatureStateChange
| TransactionControllerStateChangeEvent
>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Messenger should come from the controller package right? Why?

Comment on lines +29 to +45
export type ShieldControllerInstanceOptions = {
/**
* When set, used as-is; `baseUrl`, `fetchFunction`, `getAccessToken`, and
* `captureException` are ignored for backend construction.
*/
backend?: ShieldBackend;
/** Required when building the default `ShieldRemoteBackend`. */
baseUrl: string;
fetchFunction: typeof fetch;
getAccessToken?: () => Promise<string>;
captureException?: (error: Error) => void;
getCoverageResultTimeout?: number;
getCoverageResultPollInterval?: number;
transactionHistoryLimit?: number;
coverageHistoryLimit?: number;
normalizeSignatureRequest?: NormalizeSignatureRequestFn;
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use direct type reference from controller, instead of redefining or overrides.

Suggested change
export type ShieldControllerInstanceOptions = {
/**
* When set, used as-is; `baseUrl`, `fetchFunction`, `getAccessToken`, and
* `captureException` are ignored for backend construction.
*/
backend?: ShieldBackend;
/** Required when building the default `ShieldRemoteBackend`. */
baseUrl: string;
fetchFunction: typeof fetch;
getAccessToken?: () => Promise<string>;
captureException?: (error: Error) => void;
getCoverageResultTimeout?: number;
getCoverageResultPollInterval?: number;
transactionHistoryLimit?: number;
coverageHistoryLimit?: number;
normalizeSignatureRequest?: NormalizeSignatureRequestFn;
};
export type ShieldControllerInstanceOptions =
ConstructorParameters<typeof ShieldController>[0];

transactionController?: TransactionControllerInstanceOptions;
passkeyController?: PasskeyControllerInstanceOptions;
seedlessOnboardingController?: SeedlessOnboardingControllerInstanceOptions;
shieldController: ShieldControllerInstanceOptions;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why shieldController is required type here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most controllers/services are optional here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants