diff --git a/docs/code-guidelines/controller-guidelines.md b/docs/code-guidelines/controller-guidelines.md index 6367a1a50d7..e41d85b648e 100644 --- a/docs/code-guidelines/controller-guidelines.md +++ b/docs/code-guidelines/controller-guidelines.md @@ -30,6 +30,177 @@ The name of the controller should reflect its responsibility. If, when creating Each public method and each state property of a controller should have a purpose, and the name of the method or state property should be readable and should reflect the purpose clearly. If something does not need to be public, it should be made private; if it is unnecessary, it should be removed. +## Define, but do not export, a name for the controller + +Every controller has a name, which is used to namespace not only the controller's messenger actions and events, but also the controller's state data when composed with other controllers. + +The name should be defined in a constant called `CONTROLLER_NAME` so that it can be easily changed if the need arises. The name should be used to initialize the messenger, and it should also be passed to the `BaseController` constructor. + +The constant should be used to define actions and events. It may be exported from the file in which it is defined, but should not listed as an export of the package. + +🚫 **The messenger namespace is not defined as a constant, but is repeated** + +```typescript +export type TransactionsControllerStateChangedEvent = + ControllerStateChangedEvent< + // 🚫 Name is repeated. + 'TransactionsController', + TransactionsControllerState + >; + +export type TransactionsControllerTransactionApprovedEvent = { + // 🚫 Name is repeated. + type: 'TransactionsController:transactionApprovedEvent'; + payload: [transaction: Transaction]; +}; + +export type TransactionsControllerEvents = + | TransactionsControllerStateChangedEvent + | TransactionsControllerTransactionApprovedEvent; + +export type TransactionsControllerMessenger = Messenger< + // 🚫 Name is repeated. + 'TransactionsController', + never, + TransactionsControllerEvents +>; + +// 🚫 Name is repeated. +export class TransactionsController extends BaseController<'TransactionsController' /*, ... */> { + constructor(/* ... */) { + // 🚫 Name is repeated. + super({ name: 'TransactionsController' /* ... */ }); + + // ... + } +} +``` + +🚫 **The messenger namespace is defined as a constant, but it is called `controllerName` (legacy name)** + +```typescript +// 🚫 Uses legacy name. +const controllerName = 'TransactionsController'; + +export type TransactionsControllerStateChangedEvent = ControllerStateChangedEvent< + typeof controllerName, + TransactionsControllerState +>; + +export type TransactionsControllerTransactionApprovedEvent = { + type: `${typeof controllerName}:transactionApprovedEvent`; + payload: [transaction: Transaction] +}; + +export type TransactionsControllerEvents = + | TransactionsControllerStateChangedEvent + | TransactionsControllerTransactionApprovedEvent + +export type TransactionsControllerMessenger = Messenger< + typeof controllerName, + never, + TransactionsControllerEvents +>; + +export class TransactionsController extends BaseController; + +export type TransactionsControllerTransactionApprovedEvent = { + type: `${typeof CONTROLLER_NAME}:transactionApprovedEvent`; + payload: [transaction: Transaction] +}; + +export type TransactionsControllerEvents = + | TransactionsControllerStateChangedEvent + | TransactionsControllerTransactionApprovedEvent + +export type TransactionsControllerMessenger = Messenger< + typeof CONTROLLER_NAME, + never, + TransactionsControllerEvents +>; + +export class TransactionsController extends BaseController; + +export type TransactionsControllerTransactionApprovedEvent = { + type: `${typeof CONTROLLER_NAME}:transactionApprovedEvent`; + payload: [transaction: Transaction] +}; + +export type TransactionsControllerEvents = + | TransactionsControllerStateChangedEvent + | TransactionsControllerTransactionApprovedEvent + +export type TransactionsControllerMessenger = Messenger< + typeof CONTROLLER_NAME, + never, + TransactionsControllerEvents +>; + +export class TransactionsController extends BaseController; -export type CurrencyRateControllerActions = GetCurrencyRateState; +export type CurrencyRateControllerActions = + | CurrencyRateControllerGetStateAction + | CurrencyRateControllerMethodActions; type AllowedActions = | NetworkControllerGetNetworkClientByIdAction @@ -168,6 +176,11 @@ export class CurrencyRateController extends StaticIntervalPollingController { }); }); + describe('withController', () => { + it('throws if the controller is locked', async () => { + await withController( + { skipVaultCreation: true }, + async ({ controller }) => { + await expect(controller.withController(jest.fn())).rejects.toThrow( + KeyringControllerErrorMessage.ControllerLocked, + ); + }, + ); + }); + + it('provides the current keyrings to the callback', async () => { + await withController(async ({ controller, initialState }) => { + await controller.withController(async (restrictedController) => { + expect(restrictedController.keyrings).toHaveLength(1); + expect(restrictedController.keyrings[0].metadata).toStrictEqual( + initialState.keyrings[0].metadata, + ); + }); + }); + }); + + it('returns the result of the callback', async () => { + await withController(async ({ controller }) => { + const result = await controller.withController(async () => 'hello'); + expect(result).toBe('hello'); + }); + }); + + it('throws if the callback returns a raw keyring instance', async () => { + await withController(async ({ controller }) => { + await expect( + controller.withController(async (restrictedController) => { + return restrictedController.keyrings[0].keyring; + }), + ).rejects.toThrow( + KeyringControllerErrorMessage.UnsafeDirectKeyringAccess, + ); + }); + }); + + it('throws if the callback returns a raw keyring (v2) instance', async () => { + await withController(async ({ controller }) => { + await expect( + controller.withController(async (restrictedController) => { + return restrictedController.keyrings[0].keyringV2; + }), + ).rejects.toThrow( + KeyringControllerErrorMessage.UnsafeDirectKeyringAccess, + ); + }); + }); + + describe('addNewKeyring', () => { + it('creates an initialized keyring and stages it for commit', async () => { + const mockAddress = '0x4584d2B4905087A100420AFfCe1b2d73fC69B8E4'; + stubKeyringClassWithAccount(MockKeyring, mockAddress); + + await withController( + { keyringBuilders: [keyringBuilderFactory(MockKeyring)] }, + async ({ controller }) => { + await controller.withController(async (restrictedController) => { + const entry = await restrictedController.addNewKeyring( + MockKeyring.type, + ); + + expect(entry.keyring).toBeInstanceOf(MockKeyring); + expect(entry.metadata.id).toBeDefined(); + }); + + expect(controller.state.keyrings).toHaveLength(2); + }, + ); + }); + + it('populates keyringV2 when a V2 builder is registered for the type', async () => { + await withController(async ({ controller }) => { + await controller.withController(async (restrictedController) => { + const entry = await restrictedController.addNewKeyring( + KeyringTypes.simple, + ); + + expect(entry.keyringV2).toBeDefined(); + }); + }); + }); + + it('appears immediately in restrictedController.keyrings', async () => { + const mockAddress = '0x4584d2B4905087A100420AFfCe1b2d73fC69B8E4'; + stubKeyringClassWithAccount(MockKeyring, mockAddress); + + await withController( + { keyringBuilders: [keyringBuilderFactory(MockKeyring)] }, + async ({ controller }) => { + await controller.withController(async (restrictedController) => { + expect(restrictedController.keyrings).toHaveLength(1); + await restrictedController.addNewKeyring(MockKeyring.type); + expect(restrictedController.keyrings).toHaveLength(2); + }); + }, + ); + }); + + it('destroys created keyrings and does not commit them if the callback throws', async () => { + const mockAddress = '0x4584d2B4905087A100420AFfCe1b2d73fC69B8E4'; + stubKeyringClassWithAccount(MockKeyring, mockAddress); + const destroySpy = jest + .spyOn(MockKeyring.prototype, 'destroy') + .mockResolvedValue(undefined); + + await withController( + { keyringBuilders: [keyringBuilderFactory(MockKeyring)] }, + async ({ controller }) => { + await expect( + controller.withController(async (restrictedController) => { + await restrictedController.addNewKeyring(MockKeyring.type); + throw new Error('Oops'); + }), + ).rejects.toThrow('Oops'); + + expect(destroySpy).toHaveBeenCalledTimes(1); + expect(controller.state.keyrings).toHaveLength(1); + }, + ); + }); + }); + + describe('removeKeyring', () => { + it('removes a keyring by id and commits the removal', async () => { + const mockAddress = '0x4584d2B4905087A100420AFfCe1b2d73fC69B8E4'; + stubKeyringClassWithAccount(MockKeyring, mockAddress); + + await withController( + { keyringBuilders: [keyringBuilderFactory(MockKeyring)] }, + async ({ controller }) => { + await controller.addNewKeyring(MockKeyring.type); + const idToRemove = controller.state.keyrings[1].metadata.id; + + await controller.withController(async (restrictedController) => { + await restrictedController.removeKeyring(idToRemove); + }); + + expect(controller.state.keyrings).toHaveLength(1); + expect( + controller.state.keyrings.find( + (k) => k.metadata.id === idToRemove, + ), + ).toBeUndefined(); + }, + ); + }); + + it('disappears from restrictedController.keyrings immediately', async () => { + const mockAddress = '0x4584d2B4905087A100420AFfCe1b2d73fC69B8E4'; + stubKeyringClassWithAccount(MockKeyring, mockAddress); + + await withController( + { keyringBuilders: [keyringBuilderFactory(MockKeyring)] }, + async ({ controller }) => { + await controller.addNewKeyring(MockKeyring.type); + const idToRemove = controller.state.keyrings[1].metadata.id; + + await controller.withController(async (restrictedController) => { + expect(restrictedController.keyrings).toHaveLength(2); + await restrictedController.removeKeyring(idToRemove); + expect(restrictedController.keyrings).toHaveLength(1); + }); + }, + ); + }); + + it('destroys the removed keyring', async () => { + const mockAddress = '0x4584d2B4905087A100420AFfCe1b2d73fC69B8E4'; + stubKeyringClassWithAccount(MockKeyring, mockAddress); + const destroySpy = jest + .spyOn(MockKeyring.prototype, 'destroy') + .mockResolvedValue(undefined); + + await withController( + { keyringBuilders: [keyringBuilderFactory(MockKeyring)] }, + async ({ controller }) => { + await controller.addNewKeyring(MockKeyring.type); + const idToRemove = controller.state.keyrings[1].metadata.id; + + await controller.withController(async (restrictedController) => { + await restrictedController.removeKeyring(idToRemove); + }); + + expect(destroySpy).toHaveBeenCalledTimes(1); + }, + ); + }); + + it('throws KeyringNotFound for an unknown id', async () => { + await withController(async ({ controller }) => { + await expect( + controller.withController(async (restrictedController) => { + await restrictedController.removeKeyring('non-existent-id'); + }), + ).rejects.toThrow(KeyringControllerErrorMessage.KeyringNotFound); + }); + }); + + it('throws CannotRemovePrimaryHdKeyring when attempting to remove the primary HD keyring', async () => { + await withController(async ({ controller }) => { + const primaryId = controller.state.keyrings[0].metadata.id; + + await expect( + controller.withController(async (restrictedController) => { + await restrictedController.removeKeyring(primaryId); + }), + ).rejects.toThrow( + KeyringControllerErrorMessage.CannotRemovePrimaryKeyring, + ); + + expect(controller.state.keyrings).toHaveLength(1); + }); + }); + + it('destroys a keyring that was created then removed within the same callback', async () => { + const mockAddress = '0x4584d2B4905087A100420AFfCe1b2d73fC69B8E4'; + stubKeyringClassWithAccount(MockKeyring, mockAddress); + const destroySpy = jest + .spyOn(MockKeyring.prototype, 'destroy') + .mockResolvedValue(undefined); + + await withController( + { keyringBuilders: [keyringBuilderFactory(MockKeyring)] }, + async ({ controller }) => { + await controller.withController(async (restrictedController) => { + const { metadata } = await restrictedController.addNewKeyring( + MockKeyring.type, + ); + await restrictedController.removeKeyring(metadata.id); + }); + + expect(destroySpy).toHaveBeenCalledTimes(1); + expect(controller.state.keyrings).toHaveLength(1); + }, + ); + }); + }); + + it('rolls back on error', async () => { + await withController(async ({ controller, initialState }) => { + await expect( + controller.withController(async (restrictedController) => { + await restrictedController.addNewKeyring(KeyringTypes.simple); + throw new Error('Oops'); + }), + ).rejects.toThrow('Oops'); + + expect(controller.state.keyrings).toHaveLength( + initialState.keyrings.length, + ); + expect(await controller.getAccounts()).toStrictEqual( + initialState.keyrings[0].accounts, + ); + }); + }); + + it('does not update the vault if no keyrings change', async () => { + await withController(async ({ controller, encryptor }) => { + const encryptSpy = jest.spyOn(encryptor, 'encrypt'); + + await controller.withController(async () => { + // no-op + }); + + expect(encryptSpy).not.toHaveBeenCalled(); + }); + }); + }); + describe('withKeyringUnsafe', () => { it('calls the given function without acquiring the lock', async () => { await withController(async ({ controller }) => { @@ -5025,6 +5300,28 @@ describe('KeyringController', () => { }); }); + describe('withController', () => { + it('should call withController', async () => { + await withController(async ({ messenger }) => { + const operation = jest.fn().mockResolvedValue('result'); + + const actionReturnValue = await messenger.call( + 'KeyringController:withController', + operation, + ); + + expect(operation).toHaveBeenCalledWith( + expect.objectContaining({ + keyrings: expect.any(Array), + addNewKeyring: expect.any(Function), + removeKeyring: expect.any(Function), + }), + ); + expect(actionReturnValue).toBe('result'); + }); + }); + }); + describe('addNewKeyring', () => { it('should call addNewKeyring', async () => { const mockKeyringMetadata: KeyringMetadata = { diff --git a/packages/keyring-controller/src/KeyringController.ts b/packages/keyring-controller/src/KeyringController.ts index 415a912b9d6..3b8ef6aa691 100644 --- a/packages/keyring-controller/src/KeyringController.ts +++ b/packages/keyring-controller/src/KeyringController.ts @@ -67,6 +67,7 @@ const MESSENGER_EXPOSED_METHODS = [ 'patchUserOperation', 'signUserOperation', 'addNewAccount', + 'withController', 'withKeyring', 'withKeyringUnsafe', 'withKeyringV2', @@ -232,7 +233,10 @@ export type KeyringMetadata = { name: string; }; -type KeyringEntry = { +/** + * A keyring entry, including the keyring instance (+ v2 instance) and its metadata. + */ +export type KeyringEntry = { /** * The keyring instance. */ @@ -249,6 +253,37 @@ type KeyringEntry = { metadata: KeyringMetadata; }; +/** + * A restricted view of the {@link KeyringController} exposed to the callback + * passed to {@link KeyringController.withController}. + * + * It provides a read-only live view of all keyrings and the ability to stage + * keyring additions and removals atomically within a single transaction. + */ +export type RestrictedController = { + /** + * Read-only live view of all keyrings in the current transaction (original + * keyrings plus any added, minus any removed so far in this callback). + */ + readonly keyrings: readonly KeyringEntry[]; + /** + * Create a new keyring of the given type and stage it for commit. The new + * entry is immediately visible in {@link RestrictedController.keyrings}. + * + * @param type - The type of keyring to create. + * @param opts - Optional data to pass to the keyring builder. + * @returns The newly created `{ keyring, metadata }` entry. + */ + addNewKeyring(type: string, opts?: unknown): Promise; + /** + * Stage the keyring with the given id for removal. The keyring is + * immediately removed from {@link RestrictedController.keyrings}. + * + * @param id - The id of the keyring to remove. + */ + removeKeyring(id: string): Promise; +}; + /** * A strategy for importing an account */ @@ -668,10 +703,7 @@ function isSerializedKeyringsArray( async function displayForKeyring({ keyring, metadata, -}: { - keyring: EthKeyring; - metadata: KeyringMetadata; -}): Promise { +}: KeyringEntry): Promise { const accounts = await keyring.getAccounts(); return { @@ -1765,13 +1797,7 @@ export class KeyringController< CallbackResult = void, >( selector: KeyringSelector, - operation: ({ - keyring, - metadata, - }: { - keyring: SelectedKeyring; - metadata: KeyringMetadata; - }) => Promise, + operation: ({ keyring, metadata }: KeyringEntry) => Promise, // eslint-disable-next-line @typescript-eslint/unified-signatures options: | { createIfMissing?: false } @@ -1798,13 +1824,7 @@ export class KeyringController< CallbackResult = void, >( selector: KeyringSelector, - operation: ({ - keyring, - metadata, - }: { - keyring: SelectedKeyring; - metadata: KeyringMetadata; - }) => Promise, + operation: ({ keyring, metadata }: KeyringEntry) => Promise, ): Promise; async withKeyring< @@ -2069,6 +2089,122 @@ export class KeyringController< ); } + /** + * Execute an operation against all keyrings as a mutually exclusive atomic + * operation. The operation receives a {@link RestrictedController} instance + * that exposes a read-only live view of all keyrings as well as + * `addNewKeyring` and `removeKeyring` methods to stage mutations. + * + * The method automatically persists changes at the end of the function + * execution, or rolls back the changes if an error is thrown. + * + * @param operation - Function to execute with the restricted controller. + * @returns Promise resolving to the result of the function execution. + * @template CallbackResult - The type of the value resolved by the callback function. + */ + async withController( + operation: ( + restrictedController: RestrictedController, + ) => Promise, + ): Promise { + this.#assertIsUnlocked(); + + return this.#persistOrRollback(async () => { + // Track created and removed keyrings during the operation execution. + const createdEntries = new Set(); + const removedEntries = new Set(); + + // Copy of the current keyrings that is mutated during the operation execution. + const restrictedEntries = [...this.#keyrings]; + + // The restricted controller proxies the current keyrings and allows staging + // mutations that are only applied to the real keyrings if the operation + // completes successfully. This allows us to have a single source of truth + // for the keyrings during the operation execution, and to automatically + // roll back any changes if an error is thrown. + const restrictedController: RestrictedController = { + // We freeze the array to prevent direct mutations, but the keyring instances + // themselves are not frozen, allowing safe read-only access. + get keyrings() { + return Object.freeze([...restrictedEntries]); + }, + + // Method to create a new keyring and adds it to the restricted entries. + addNewKeyring: async (type: string, opts?: unknown) => { + const entry = await this.#createKeyring(type, opts); + + restrictedEntries.push(entry); + createdEntries.add(entry); + + return entry; + }, + + // Method to remove a keyring from the restricted entries. + removeKeyring: async (id: string) => { + const index = restrictedEntries.findIndex( + (entry) => entry.metadata.id === id, + ); + if (index === -1) { + throw new KeyringControllerError( + KeyringControllerErrorMessage.KeyringNotFound, + ); + } + + this.#assertNotRemovingPrimaryKeyring( + restrictedEntries[index], + restrictedEntries, + ); + + const [removed] = restrictedEntries.splice(index, 1) as [ + KeyringEntry, + ]; + removedEntries.add(removed); + }, + }; + + const destroyKeyrings = async ( + entries: Iterable, + ): Promise => { + await Promise.all( + [...entries].map(({ keyring, keyringV2 }) => + this.#destroyKeyring(keyring, keyringV2), + ), + ); + }; + + let result: CallbackResult; + try { + result = await operation(restrictedController); + } catch (error) { + await destroyKeyrings(createdEntries); + + throw error; + } + + await destroyKeyrings(removedEntries); + + // We update the real keyrings only after the operation completes successfully, so that + // they will be persisted in the vault. + this.#keyrings = restrictedEntries; + + // As usual, we want to prevent returning direct references to keyring instances, so we check + // the result for any unsafe direct access before returning. + for (const { keyring, keyringV2 } of [ + ...this.#keyrings, + // We also check for keyrings that got removed during the operation, since the result could + // still have references to them. + ...removedEntries, + ]) { + this.#assertNoUnsafeDirectKeyringAccess(result, keyring); + if (keyringV2) { + this.#assertNoUnsafeDirectKeyringAccess(result, keyringV2); + } + } + + return result; + }); + } + async getAccountKeyringType(account: string): Promise { this.#assertIsUnlocked(); @@ -2969,6 +3105,27 @@ export class KeyringController< } } + /** + * Assert that the given keyring entry is not the primary HD keyring. + * + * @param entry - The keyring entry to check. + * @param keyrings - The current list of keyring entries. + * @throws If the entry is the primary keyring. + */ + #assertNotRemovingPrimaryKeyring( + entry: KeyringEntry, + keyrings: KeyringEntry[], + ): void { + if ( + keyrings[0] === entry && + entry.keyring.type === (KeyringTypes.hd as string) + ) { + throw new KeyringControllerError( + KeyringControllerErrorMessage.CannotRemovePrimaryKeyring, + ); + } + } + /** * Lock the controller mutex before executing the given function, * and release it after the function is resolved or after an diff --git a/packages/keyring-controller/src/constants.ts b/packages/keyring-controller/src/constants.ts index a5b71a6408a..f97cdd646cb 100644 --- a/packages/keyring-controller/src/constants.ts +++ b/packages/keyring-controller/src/constants.ts @@ -39,4 +39,5 @@ export enum KeyringControllerErrorMessage { LastAccountInPrimaryKeyring = 'KeyringController - Last account in primary keyring cannot be removed', EncryptionKeyNotSet = 'KeyringController - Encryption key not set', KeyringV2NotSupported = 'KeyringController - The selected keyring does not support the KeyringV2 API.', + CannotRemovePrimaryKeyring = 'KeyringController - Cannot remove the primary keyring', } diff --git a/packages/keyring-controller/src/index.ts b/packages/keyring-controller/src/index.ts index 9ac85f95897..4d570f0c98e 100644 --- a/packages/keyring-controller/src/index.ts +++ b/packages/keyring-controller/src/index.ts @@ -19,6 +19,7 @@ export type { KeyringControllerPrepareUserOperationAction, KeyringControllerPatchUserOperationAction, KeyringControllerSignUserOperationAction, + KeyringControllerWithControllerAction, KeyringControllerWithKeyringAction, KeyringControllerWithKeyringUnsafeAction, KeyringControllerWithKeyringV2Action, diff --git a/packages/keyring-controller/tests/mocks/mockKeyring.ts b/packages/keyring-controller/tests/mocks/mockKeyring.ts index 4bfbeb77c52..89bff0f1abd 100644 --- a/packages/keyring-controller/tests/mocks/mockKeyring.ts +++ b/packages/keyring-controller/tests/mocks/mockKeyring.ts @@ -32,4 +32,8 @@ export class MockKeyring implements EthKeyring { async deserialize(_: unknown): Promise { return Promise.resolve(); } + + async destroy(): Promise { + return Promise.resolve(); + } } diff --git a/packages/perps-controller/CHANGELOG.md b/packages/perps-controller/CHANGELOG.md index 1a5dd9af885..cd05410c1db 100644 --- a/packages/perps-controller/CHANGELOG.md +++ b/packages/perps-controller/CHANGELOG.md @@ -22,7 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed -- Drop the dead `spotState` parameter from `adaptAccountStateFromSDK`. Spot balances are layered on by `addSpotBalanceToAccountState`, which enforces the USDC-only policy via `SPOT_COLLATERAL_COINS`; removing the dormant branch keeps one source of truth and prevents a future caller from silently getting ALL-coins behavior ([#8560](https://github.com/MetaMask/core/pull/8560)) +- **BREAKING:** Drop the dead `spotState` parameter from `adaptAccountStateFromSDK`. Spot balances are layered on by `addSpotBalanceToAccountState`, which enforces the USDC-only policy via `SPOT_COLLATERAL_COINS`; removing the dormant branch keeps one source of truth and prevents a future caller from silently getting ALL-coins behavior ([#8560](https://github.com/MetaMask/core/pull/8560)) ### Fixed diff --git a/packages/sample-controllers/src/sample-gas-prices-controller.ts b/packages/sample-controllers/src/sample-gas-prices-controller.ts index 770a94c449e..c354c90da3f 100644 --- a/packages/sample-controllers/src/sample-gas-prices-controller.ts +++ b/packages/sample-controllers/src/sample-gas-prices-controller.ts @@ -22,7 +22,7 @@ import type { SampleGasPricesServiceFetchGasPricesAction } from './sample-gas-pr * controller's actions and events and to namespace the controller's state data * when composed with other controllers. */ -export const controllerName = 'SampleGasPricesController'; +const CONTROLLER_NAME = 'SampleGasPricesController'; // === STATE === @@ -94,7 +94,7 @@ const MESSENGER_EXPOSED_METHODS = ['updateGasPrices'] as const; * Retrieves the state of the {@link SampleGasPricesController}. */ export type SampleGasPricesControllerGetStateAction = ControllerGetStateAction< - typeof controllerName, + typeof CONTROLLER_NAME, SampleGasPricesControllerState >; @@ -117,7 +117,7 @@ type AllowedActions = */ export type SampleGasPricesControllerStateChangeEvent = ControllerStateChangeEvent< - typeof controllerName, + typeof CONTROLLER_NAME, SampleGasPricesControllerState >; @@ -138,7 +138,7 @@ type AllowedEvents = NetworkControllerStateChangeEvent; * {@link SampleGasPricesController}. */ export type SampleGasPricesControllerMessenger = Messenger< - typeof controllerName, + typeof CONTROLLER_NAME, SampleGasPricesControllerActions | AllowedActions, SampleGasPricesControllerEvents | AllowedEvents >; @@ -219,7 +219,7 @@ export type SampleGasPricesControllerMessenger = Messenger< * ``` */ export class SampleGasPricesController extends BaseController< - typeof controllerName, + typeof CONTROLLER_NAME, SampleGasPricesControllerState, SampleGasPricesControllerMessenger > { @@ -246,7 +246,7 @@ export class SampleGasPricesController extends BaseController< super({ messenger, metadata: gasPricesControllerMetadata, - name: controllerName, + name: CONTROLLER_NAME, state: { ...getDefaultSampleGasPricesControllerState(), ...state, diff --git a/packages/sample-controllers/src/sample-petnames-controller.ts b/packages/sample-controllers/src/sample-petnames-controller.ts index 4124688f96d..a8b18844537 100644 --- a/packages/sample-controllers/src/sample-petnames-controller.ts +++ b/packages/sample-controllers/src/sample-petnames-controller.ts @@ -17,7 +17,7 @@ import type { SamplePetnamesControllerMethodActions } from './sample-petnames-co * controller's actions and events and to namespace the controller's state data * when composed with other controllers. */ -export const controllerName = 'SamplePetnamesController'; +const CONTROLLER_NAME = 'SamplePetnamesController'; // === STATE === @@ -70,7 +70,7 @@ const MESSENGER_EXPOSED_METHODS = ['assignPetname'] as const; * Retrieves the state of the {@link SamplePetnamesController}. */ export type SamplePetnamesControllerGetStateAction = ControllerGetStateAction< - typeof controllerName, + typeof CONTROLLER_NAME, SamplePetnamesControllerState >; @@ -91,7 +91,7 @@ type AllowedActions = never; */ export type SamplePetnamesControllerStateChangeEvent = ControllerStateChangeEvent< - typeof controllerName, + typeof CONTROLLER_NAME, SamplePetnamesControllerState >; @@ -112,7 +112,7 @@ type AllowedEvents = never; * {@link SamplePetnamesController}. */ export type SamplePetnamesControllerMessenger = Messenger< - typeof controllerName, + typeof CONTROLLER_NAME, SamplePetnamesControllerActions | AllowedActions, SamplePetnamesControllerEvents | AllowedEvents >; @@ -167,7 +167,7 @@ export type SamplePetnamesControllerMessenger = Messenger< * ``` */ export class SamplePetnamesController extends BaseController< - typeof controllerName, + typeof CONTROLLER_NAME, SamplePetnamesControllerState, SamplePetnamesControllerMessenger > { @@ -189,7 +189,7 @@ export class SamplePetnamesController extends BaseController< super({ messenger, metadata: samplePetnamesControllerMetadata, - name: controllerName, + name: CONTROLLER_NAME, state: { ...getDefaultPetnamesControllerState(), ...state, diff --git a/packages/transaction-pay-controller/CHANGELOG.md b/packages/transaction-pay-controller/CHANGELOG.md index 412c41f9c0e..d599896821b 100644 --- a/packages/transaction-pay-controller/CHANGELOG.md +++ b/packages/transaction-pay-controller/CHANGELOG.md @@ -17,8 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump `@metamask/assets-controller` from `^6.0.0` to `^6.1.0` ([#8559](https://github.com/MetaMask/core/pull/8559)) - Bump `@metamask/assets-controllers` from `^104.2.0` to `^104.3.0` ([#8559](https://github.com/MetaMask/core/pull/8559)) -- Bump `@metamask/bridge-controller` from `^70.1.1` to `^71.0.0` ([#8569](https://github.com/MetaMask/core/pull/8569)) -- Bump `@metamask/bridge-status-controller` from `^70.0.5` to `^71.0.0` ([#8569](https://github.com/MetaMask/core/pull/8569)) +- Bump `@metamask/bridge-controller` from `^70.1.1` to `^70.2.0` ([#8571](https://github.com/MetaMask/core/pull/8571)) +- Bump `@metamask/bridge-status-controller` from `^70.0.5` to `^71.0.0` ([#8571](https://github.com/MetaMask/core/pull/8571)) ## [19.2.2] diff --git a/packages/transaction-pay-controller/package.json b/packages/transaction-pay-controller/package.json index 9a1eece5e33..460fb50d5fe 100644 --- a/packages/transaction-pay-controller/package.json +++ b/packages/transaction-pay-controller/package.json @@ -60,7 +60,7 @@ "@metamask/assets-controller": "^6.1.0", "@metamask/assets-controllers": "^104.3.0", "@metamask/base-controller": "^9.1.0", - "@metamask/bridge-controller": "^71.0.0", + "@metamask/bridge-controller": "^70.2.0", "@metamask/bridge-status-controller": "^71.0.0", "@metamask/controller-utils": "^11.20.0", "@metamask/gas-fee-controller": "^26.1.1", diff --git a/yarn.lock b/yarn.lock index 975a7d3ff66..d50feba1e21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3009,7 +3009,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/bridge-controller@npm:^71.0.0, @metamask/bridge-controller@workspace:packages/bridge-controller": +"@metamask/bridge-controller@npm:^70.2.0, @metamask/bridge-controller@workspace:packages/bridge-controller": version: 0.0.0-use.local resolution: "@metamask/bridge-controller@workspace:packages/bridge-controller" dependencies: @@ -3063,7 +3063,7 @@ __metadata: "@metamask/accounts-controller": "npm:^37.2.0" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" - "@metamask/bridge-controller": "npm:^71.0.0" + "@metamask/bridge-controller": "npm:^70.2.0" "@metamask/controller-utils": "npm:^11.20.0" "@metamask/gas-fee-controller": "npm:^26.1.1" "@metamask/keyring-controller": "npm:^25.2.0" @@ -5665,7 +5665,7 @@ __metadata: "@metamask/assets-controllers": "npm:^104.3.0" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" - "@metamask/bridge-controller": "npm:^71.0.0" + "@metamask/bridge-controller": "npm:^70.2.0" "@metamask/bridge-status-controller": "npm:^71.0.0" "@metamask/controller-utils": "npm:^11.20.0" "@metamask/gas-fee-controller": "npm:^26.1.1"