Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/shield-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- Make start and stop idempotent ([#6817](https://github.com/MetaMask/core/pull/6817))

## [0.3.1]

### Changed
Expand Down
24 changes: 18 additions & 6 deletions packages/shield-controller/src/ShieldController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,24 @@ describe('ShieldController', () => {
it('should trigger checkCoverage when a new transaction is added', async () => {
const { baseMessenger, backend } = setup();
const txMeta = generateMockTxMeta();
const coverageResultReceived = new Promise<void>((resolve) => {
baseMessenger.subscribe(
'ShieldController:coverageResultReceived',
(_coverageResult) => resolve(),
);
});
const coverageResultReceived = setupCoverageResultReceived(baseMessenger);
baseMessenger.publish(
'TransactionController:stateChange',
{ transactions: [txMeta] } as TransactionControllerState,
undefined as never,
);
expect(await coverageResultReceived).toBeUndefined();
expect(backend.checkCoverage).toHaveBeenCalledWith({ txMeta });
});

it('should tolerate calling start and stop multiple times', async () => {
const { backend, baseMessenger, controller } = setup();
controller.stop();
controller.stop();
controller.start();
controller.start();
const txMeta = generateMockTxMeta();
const coverageResultReceived = setupCoverageResultReceived(baseMessenger);
baseMessenger.publish(
'TransactionController:stateChange',
{ transactions: [txMeta] } as TransactionControllerState,
Expand Down
13 changes: 13 additions & 0 deletions packages/shield-controller/src/ShieldController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ export class ShieldController extends BaseController<
previousSignatureRequests: Record<string, SignatureRequest> | undefined,
) => void;

#started: boolean;

constructor(options: ShieldControllerOptions) {
const {
messenger,
Expand All @@ -177,9 +179,15 @@ export class ShieldController extends BaseController<
this.#handleTransactionControllerStateChange.bind(this);
this.#signatureControllerStateChangeHandler =
this.#handleSignatureControllerStateChange.bind(this);
this.#started = false;
}

start() {
if (this.#started) {
return;
}
this.#started = true;

this.messagingSystem.subscribe(
'TransactionController:stateChange',
this.#transactionControllerStateChangeHandler,
Expand All @@ -194,6 +202,11 @@ export class ShieldController extends BaseController<
}

stop() {
if (!this.#started) {
return;
}
this.#started = false;

Copy link

Choose a reason for hiding this comment

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

Bug: State Update Timing Causes Subscription Inconsistency

The #started flag is updated before subscription/unsubscription operations. If these operations fail, the controller's #started state won't reflect its actual subscription status. This inconsistency can cause start() or stop() to return early, leaving the controller partially subscribed or still receiving events.

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. We might need to fix this in a later version.

this.messagingSystem.unsubscribe(
'TransactionController:stateChange',
this.#transactionControllerStateChangeHandler,
Expand Down
Loading