From fb9db9052fcb8a98aff2865062051dd0c4a44834 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Mon, 15 Jun 2026 14:12:02 +0100 Subject: [PATCH 1/5] feat(wallet): wire transaction controller --- .github/CODEOWNERS | 1 + README.md | 1 + packages/wallet/CHANGELOG.md | 1 + packages/wallet/package.json | 1 + packages/wallet/src/Wallet.test.ts | 32 +++- .../src/initialization/instances/index.ts | 1 + .../transaction-controller.test.ts | 144 ++++++++++++++++++ .../transaction-controller.ts | 57 +++++++ .../instances/transaction-controller/types.ts | 5 + packages/wallet/src/types.ts | 2 + packages/wallet/tsconfig.build.json | 3 +- packages/wallet/tsconfig.json | 3 +- yarn.lock | 1 + 13 files changed, 243 insertions(+), 9 deletions(-) create mode 100644 packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.test.ts create mode 100644 packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.ts create mode 100644 packages/wallet/src/initialization/instances/transaction-controller/types.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2b66123bd13..049a35be340 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -146,6 +146,7 @@ /packages/wallet/src/initialization/instances/keyring-controller/ @MetaMask/accounts-engineers @MetaMask/core-platform /packages/wallet/src/initialization/instances/remote-feature-flag-controller/ @MetaMask/extension-platform @MetaMask/mobile-platform @MetaMask/core-platform /packages/wallet/src/initialization/instances/storage-service/ @MetaMask/extension-platform @MetaMask/mobile-platform @MetaMask/core-platform +/packages/wallet/src/initialization/instances/transaction-controller/ @MetaMask/confirmations ## Package Release related /packages/account-tree-controller/package.json @MetaMask/accounts-engineers @MetaMask/core-platform diff --git a/README.md b/README.md index 1b8c734bbbb..80a1a9a2251 100644 --- a/README.md +++ b/README.md @@ -626,6 +626,7 @@ linkStyle default opacity:0.5 wallet --> network_controller; wallet --> remote_feature_flag_controller; wallet --> storage_service; + wallet --> transaction_controller; wallet_cli --> base_controller; wallet_cli --> remote_feature_flag_controller; wallet_cli --> storage_service; diff --git a/packages/wallet/CHANGELOG.md b/packages/wallet/CHANGELOG.md index ea64a689723..977ee83d628 100644 --- a/packages/wallet/CHANGELOG.md +++ b/packages/wallet/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - **BREAKING:** Wire `AddressBookController` into the default wallet initialization ([#9291](https://github.com/MetaMask/core/pull/9291)) +- **BREAKING:** Wire `TransactionController` into the default wallet initialization ([#8975](https://github.com/MetaMask/core/pull/8975)) ### Changed diff --git a/packages/wallet/package.json b/packages/wallet/package.json index 28454851761..b68b0ce8691 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -66,6 +66,7 @@ "@metamask/remote-feature-flag-controller": "^4.2.2", "@metamask/scure-bip39": "^2.1.1", "@metamask/storage-service": "^1.0.2", + "@metamask/transaction-controller": "^68.2.0", "@metamask/utils": "^11.11.0" }, "devDependencies": { diff --git a/packages/wallet/src/Wallet.test.ts b/packages/wallet/src/Wallet.test.ts index 2c39a1544ee..dcac6757853 100644 --- a/packages/wallet/src/Wallet.test.ts +++ b/packages/wallet/src/Wallet.test.ts @@ -8,6 +8,7 @@ import { webcrypto } from 'crypto'; import MockEncryptor from '../../keyring-controller/tests/mocks/mockEncryptor'; import * as initializationModule from './initialization/initialization'; import { AlwaysOnlineAdapter } from './initialization/instances/connectivity-controller/always-online-adapter'; +import type { WalletOptions } from './types'; import { importSecretRecoveryPhrase } from './utilities'; import { Wallet } from './Wallet'; @@ -23,8 +24,25 @@ const REMOTE_FEATURE_FLAG_OPTIONS = { }, }; +const TEST_TRANSACTION_CONTROLLER_CONFIGURATION = { + name: 'TransactionController', + getMessenger: (): Messenger => + new Messenger({ namespace: 'TransactionController' }), + init: (): Record => ({}), +}; + +function createWallet(options: WalletOptions): Wallet { + return new Wallet({ + ...options, + initializationConfigurations: [ + ...(options.initializationConfigurations ?? []), + TEST_TRANSACTION_CONTROLLER_CONFIGURATION, + ], + }); +} + async function setupWallet(): Promise { - const wallet = new Wallet({ + const wallet = createWallet({ instanceOptions: { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), @@ -84,7 +102,7 @@ describe('Wallet', () => { }); it('supports passing instance options', async () => { - const wallet = new Wallet({ + const wallet = createWallet({ instanceOptions: { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), @@ -122,7 +140,7 @@ describe('Wallet', () => { class DummyService {} - const wallet = new Wallet({ + const wallet = createWallet({ initializationConfigurations: [ { name: 'KeyringController', @@ -179,7 +197,7 @@ describe('Wallet', () => { NoMeta: { state: {} }, }); - const wallet = new Wallet({ + const wallet = createWallet({ instanceOptions: { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), @@ -321,7 +339,7 @@ describe('Wallet', () => { describe('ConnectivityController', () => { it('reports online connectivity status', () => { - const wallet = new Wallet({ + const wallet = createWallet({ instanceOptions: { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), @@ -356,7 +374,7 @@ describe('Wallet', () => { const vault = '{"data":"iOD5pIcPeRZYQ4WdEMsNYoZ3xBxWBafIU8Cr4nD0X4zBvrOA06tGen3sKQ/ValasXSweLnzH9Fk2frkPYmqeJWBtTNYFwdHPe7P970ThZwreSXN1Sqrx9Ad+YzmIN0y89Yg3KrUodPWaRgIZmgWbfDon6ADPgeEDkX0/GAEYET39O7Rx/gL+rcaTpxnpHPTgHiLbhRHWGsS3z+JVomSqoLAO5XVvrJWenO6R3Nzm62BaJaSPrf/pwstZqhSvxTq8hnQf7aR81hWfwYTxNBVG7TC/dniSQ8K5So6PvUN5nzAqvtzzHT2TagOuxQkX88Zi17P8os21jNmNdA90IGYroD+b/mppyRIgRYWtAUQZH9ji36atEuFupszbg8Qw1iaL3EQyUogC30Cpj9ko5bbqhYgqmFHF0J/kflhPHKuO6d4tgSmhYpTumydQRjxaPnlghIS5YI4W+7p9HVBpb+c6IPUz9y/x3Ngbp+ukJwOnXt2U/eZhXrJzi2z1x/nzPg4fzDJoM7k=","iv":"yrZsyC7dso/q7pQ48YX3vw==","keyMetadata":{"algorithm":"PBKDF2","params":{"iterations":600000}},"salt":"s7nIrMWK1lcZVjfdmES1DBML8Uz4ja2fpm8zUz1lWI0="}'; - const wallet = new Wallet({ + const wallet = createWallet({ state: { KeyringController: { vault, @@ -439,7 +457,7 @@ describe('Wallet', () => { }); it('routes injected instanceOptions through to the controller', async () => { - const wallet = new Wallet({ + const wallet = createWallet({ instanceOptions: { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), diff --git a/packages/wallet/src/initialization/instances/index.ts b/packages/wallet/src/initialization/instances/index.ts index ad8d554b678..a15db956032 100644 --- a/packages/wallet/src/initialization/instances/index.ts +++ b/packages/wallet/src/initialization/instances/index.ts @@ -6,3 +6,4 @@ export { keyringController } from './keyring-controller/keyring-controller'; export { networkController } from './network-controller/network-controller'; export { remoteFeatureFlagController } from './remote-feature-flag-controller/remote-feature-flag-controller'; export { storageService } from './storage-service/storage-service'; +export { transactionController } from './transaction-controller/transaction-controller'; diff --git a/packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.test.ts b/packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.test.ts new file mode 100644 index 00000000000..1e88b0e52d2 --- /dev/null +++ b/packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.test.ts @@ -0,0 +1,144 @@ +import { Messenger } from '@metamask/messenger'; +import { InMemoryStorageAdapter } from '@metamask/storage-service'; +import { TransactionController } from '@metamask/transaction-controller'; + +import type { WalletOptions } from '../../../types'; +import { Wallet } from '../../../Wallet'; +import { defaultConfigurations } from '../../defaults'; +import type { + DefaultActions, + DefaultEvents, + RootMessenger, +} from '../../defaults'; +import { AlwaysOnlineAdapter } from '../connectivity-controller/always-online-adapter'; +import { transactionController } from './transaction-controller'; + +const controllers: TransactionController[] = []; +const wallets: Wallet[] = []; + +const REMOTE_FEATURE_FLAG_OPTIONS = { + clientConfigApiService: { + fetchRemoteFeatureFlags: async (): Promise<{ + remoteFeatureFlags: Record; + cacheTimestamp: number; + }> => ({ remoteFeatureFlags: {}, cacheTimestamp: Date.now() }), + }, +}; + +type ActionHandler = (...args: unknown[]) => unknown; + +type AnyMessenger = Messenger; + +describe('transactionController', () => { + afterEach(async () => { + for (const controller of controllers.splice(0)) { + controller.destroy(); + } + + await Promise.all(wallets.splice(0).map((wallet) => wallet.destroy())); + }); + + it('is registered as a default initialization configuration', () => { + expect(Object.values(defaultConfigurations)).toContain( + transactionController, + ); + }); + + it('initializes a TransactionController with default state', () => { + const rootMessenger = getRootMessenger(); + const messenger = transactionController.getMessenger(rootMessenger); + + const instance = transactionController.init({ + state: undefined, + messenger, + options: {}, + }); + controllers.push(instance); + + expect(instance).toBeInstanceOf(TransactionController); + expect(rootMessenger.call('TransactionController:getState')).toStrictEqual({ + methodData: {}, + transactions: [], + transactionBatches: [], + lastFetchedBlockNumbers: {}, + submitHistory: [], + }); + }); + + it('is initialized by the default Wallet configuration', () => { + const wallet = new Wallet({ + instanceOptions: getInstanceOptions(), + }); + wallets.push(wallet); + + expect(wallet.getInstance('TransactionController')).toBeInstanceOf( + TransactionController, + ); + }); + + it('forwards the provided state to the controller', () => { + const rootMessenger = getRootMessenger(); + const messenger = transactionController.getMessenger(rootMessenger); + + const instance = transactionController.init({ + state: { + lastFetchedBlockNumbers: { '0x1': 123 }, + }, + messenger, + options: {}, + }); + controllers.push(instance); + + expect(instance.state.lastFetchedBlockNumbers).toStrictEqual({ + '0x1': 123, + }); + }); +}); + +function getRootMessenger(): RootMessenger { + const rootMessenger = new Messenger<'Root', DefaultActions, DefaultEvents>({ + namespace: 'Root', + }); + + registerActionHandler( + rootMessenger, + 'NetworkController', + 'NetworkController:getNetworkClientRegistry', + jest.fn().mockReturnValue({}), + ); + + return rootMessenger; +} + +function getInstanceOptions(): WalletOptions['instanceOptions'] { + return { + connectivityController: { + connectivityAdapter: new AlwaysOnlineAdapter(), + }, + networkController: { + infuraProjectId: 'test-infura-project-id', + }, + storageService: { + storage: new InMemoryStorageAdapter(), + }, + remoteFeatureFlagController: REMOTE_FEATURE_FLAG_OPTIONS, + }; +} + +function registerActionHandler( + parent: RootMessenger, + namespace: string, + actionType: string, + handler: ActionHandler, +): void { + const messenger = new Messenger({ + namespace, + parent: parent as unknown as AnyMessenger, + }); + + ( + messenger as unknown as { + registerActionHandler(type: string, handler: ActionHandler): void; + } + ).registerActionHandler(actionType, handler); +} diff --git a/packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.ts b/packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.ts new file mode 100644 index 00000000000..79c16126f81 --- /dev/null +++ b/packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.ts @@ -0,0 +1,57 @@ +import { Messenger } from '@metamask/messenger'; +import type { + TransactionControllerMessenger, + TransactionControllerOptions, +} from '@metamask/transaction-controller'; +import { TransactionController } from '@metamask/transaction-controller'; + +import type { InitializationConfiguration } from '../../types'; + +export type { TransactionControllerInstanceOptions } from './types'; + +export const transactionController: InitializationConfiguration< + TransactionController, + TransactionControllerMessenger +> = { + name: 'TransactionController', + init: ({ state, messenger, options }) => { + return new TransactionController({ + ...options, + messenger, + state, + } as TransactionControllerOptions); + }, + getMessenger: (parent) => { + const messenger: TransactionControllerMessenger = new Messenger({ + namespace: 'TransactionController', + parent, + }); + + parent.delegate({ + messenger, + actions: [ + 'AccountsController:getSelectedAccount', + 'AccountsController:getState', + 'ApprovalController:addRequest', + 'GasFeeController:fetchGasFeeEstimates', + 'KeyringController:getState', + 'KeyringController:signEip7702Authorization', + 'KeyringController:signTransaction', + 'NetworkController:findNetworkClientIdByChainId', + 'NetworkController:getEIP1559Compatibility', + 'NetworkController:getNetworkClientById', + 'NetworkController:getNetworkClientRegistry', + 'NetworkController:getState', + 'RemoteFeatureFlagController:getState', + ], + events: [ + 'AccountActivityService:transactionUpdated', + // TODO: Replace with `NetworkController:stateChanged` once TransactionController migrates. + // eslint-disable-next-line no-restricted-syntax + 'NetworkController:stateChange', + ], + }); + + return messenger; + }, +}; diff --git a/packages/wallet/src/initialization/instances/transaction-controller/types.ts b/packages/wallet/src/initialization/instances/transaction-controller/types.ts new file mode 100644 index 00000000000..54a7865f9f6 --- /dev/null +++ b/packages/wallet/src/initialization/instances/transaction-controller/types.ts @@ -0,0 +1,5 @@ +import type { TransactionControllerOptions } from '@metamask/transaction-controller'; + +export type TransactionControllerInstanceOptions = Partial< + Omit +>; diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index 6a66903dde0..1f330e9bafd 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -11,6 +11,7 @@ import type { KeyringControllerInstanceOptions } from './initialization/instance import type { NetworkControllerInstanceOptions } from './initialization/instances/network-controller/types'; import type { RemoteFeatureFlagControllerInstanceOptions } from './initialization/instances/remote-feature-flag-controller/types'; import type { StorageServiceInstanceOptions } from './initialization/instances/storage-service/types'; +import type { TransactionControllerInstanceOptions } from './initialization/instances/transaction-controller/types'; import type { InitializationConfiguration } from './initialization/types'; export type WalletOptions = { @@ -30,4 +31,5 @@ export type InstanceSpecificOptions = { networkController: NetworkControllerInstanceOptions; remoteFeatureFlagController: RemoteFeatureFlagControllerInstanceOptions; storageService: StorageServiceInstanceOptions; + transactionController?: TransactionControllerInstanceOptions; }; diff --git a/packages/wallet/tsconfig.build.json b/packages/wallet/tsconfig.build.json index bfa1aa578bf..cc15d40bc18 100644 --- a/packages/wallet/tsconfig.build.json +++ b/packages/wallet/tsconfig.build.json @@ -16,7 +16,8 @@ { "path": "../messenger/tsconfig.build.json" }, { "path": "../network-controller/tsconfig.build.json" }, { "path": "../remote-feature-flag-controller/tsconfig.build.json" }, - { "path": "../storage-service/tsconfig.build.json" } + { "path": "../storage-service/tsconfig.build.json" }, + { "path": "../transaction-controller/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/wallet/tsconfig.json b/packages/wallet/tsconfig.json index 9d52f9fb407..24995af5a41 100644 --- a/packages/wallet/tsconfig.json +++ b/packages/wallet/tsconfig.json @@ -14,7 +14,8 @@ { "path": "../messenger/tsconfig.json" }, { "path": "../network-controller/tsconfig.json" }, { "path": "../remote-feature-flag-controller/tsconfig.json" }, - { "path": "../storage-service/tsconfig.json" } + { "path": "../storage-service/tsconfig.json" }, + { "path": "../transaction-controller/tsconfig.json" } ], "include": ["../../types", "./src"] } diff --git a/yarn.lock b/yarn.lock index 9e514a392fc..1cc665f5ab4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9037,6 +9037,7 @@ __metadata: "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/scure-bip39": "npm:^2.1.1" "@metamask/storage-service": "npm:^1.0.2" + "@metamask/transaction-controller": "npm:^68.2.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/jest": "npm:^29.5.14" From 086faaebeef8abac4676d5128247a396a0504c25 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Fri, 3 Jul 2026 16:08:57 +0100 Subject: [PATCH 2/5] chore: fix wallet changelog and lockfile after rebase --- packages/wallet/CHANGELOG.md | 5 ++++- yarn.lock | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/wallet/CHANGELOG.md b/packages/wallet/CHANGELOG.md index 977ee83d628..ef4d9193332 100644 --- a/packages/wallet/CHANGELOG.md +++ b/packages/wallet/CHANGELOG.md @@ -7,12 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- **BREAKING:** Wire `TransactionController` into the default wallet initialization ([#8975](https://github.com/MetaMask/core/pull/8975)) + ## [6.0.0] ### Added - **BREAKING:** Wire `AddressBookController` into the default wallet initialization ([#9291](https://github.com/MetaMask/core/pull/9291)) -- **BREAKING:** Wire `TransactionController` into the default wallet initialization ([#8975](https://github.com/MetaMask/core/pull/8975)) ### Changed diff --git a/yarn.lock b/yarn.lock index 1cc665f5ab4..7c0e66a29c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8797,7 +8797,7 @@ __metadata: languageName: node linkType: hard -"@metamask/transaction-controller@npm:^68.2.2, @metamask/transaction-controller@workspace:packages/transaction-controller": +"@metamask/transaction-controller@npm:^68.2.0, @metamask/transaction-controller@npm:^68.2.2, @metamask/transaction-controller@workspace:packages/transaction-controller": version: 0.0.0-use.local resolution: "@metamask/transaction-controller@workspace:packages/transaction-controller" dependencies: From 5c85d14a164609fa3a5cff1622e6eaa3ddc99da6 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Fri, 3 Jul 2026 16:12:35 +0100 Subject: [PATCH 3/5] chore: fix wallet package constraints and lockfile --- packages/wallet/package.json | 2 +- yarn.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/wallet/package.json b/packages/wallet/package.json index b68b0ce8691..aab11a0b692 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -66,7 +66,7 @@ "@metamask/remote-feature-flag-controller": "^4.2.2", "@metamask/scure-bip39": "^2.1.1", "@metamask/storage-service": "^1.0.2", - "@metamask/transaction-controller": "^68.2.0", + "@metamask/transaction-controller": "^68.2.2", "@metamask/utils": "^11.11.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 7c0e66a29c3..260f003b9aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8797,7 +8797,7 @@ __metadata: languageName: node linkType: hard -"@metamask/transaction-controller@npm:^68.2.0, @metamask/transaction-controller@npm:^68.2.2, @metamask/transaction-controller@workspace:packages/transaction-controller": +"@metamask/transaction-controller@npm:^68.2.2, @metamask/transaction-controller@workspace:packages/transaction-controller": version: 0.0.0-use.local resolution: "@metamask/transaction-controller@workspace:packages/transaction-controller" dependencies: @@ -9037,7 +9037,7 @@ __metadata: "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/scure-bip39": "npm:^2.1.1" "@metamask/storage-service": "npm:^1.0.2" - "@metamask/transaction-controller": "npm:^68.2.0" + "@metamask/transaction-controller": "npm:^68.2.2" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/jest": "npm:^29.5.14" From fb945eacb1f2c5d1c4814d9d7ed8b0ecbf2260eb Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Mon, 6 Jul 2026 10:06:25 +0100 Subject: [PATCH 4/5] refactor(wallet): address PR feedback on TransactionController wiring --- packages/wallet/src/Wallet.test.ts | 42 ++++++++----------- .../transaction-controller.ts | 8 ++-- .../instances/transaction-controller/types.ts | 7 ++-- 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/packages/wallet/src/Wallet.test.ts b/packages/wallet/src/Wallet.test.ts index dcac6757853..d3c67ba3ef3 100644 --- a/packages/wallet/src/Wallet.test.ts +++ b/packages/wallet/src/Wallet.test.ts @@ -8,7 +8,6 @@ import { webcrypto } from 'crypto'; import MockEncryptor from '../../keyring-controller/tests/mocks/mockEncryptor'; import * as initializationModule from './initialization/initialization'; import { AlwaysOnlineAdapter } from './initialization/instances/connectivity-controller/always-online-adapter'; -import type { WalletOptions } from './types'; import { importSecretRecoveryPhrase } from './utilities'; import { Wallet } from './Wallet'; @@ -24,25 +23,8 @@ const REMOTE_FEATURE_FLAG_OPTIONS = { }, }; -const TEST_TRANSACTION_CONTROLLER_CONFIGURATION = { - name: 'TransactionController', - getMessenger: (): Messenger => - new Messenger({ namespace: 'TransactionController' }), - init: (): Record => ({}), -}; - -function createWallet(options: WalletOptions): Wallet { - return new Wallet({ - ...options, - initializationConfigurations: [ - ...(options.initializationConfigurations ?? []), - TEST_TRANSACTION_CONTROLLER_CONFIGURATION, - ], - }); -} - async function setupWallet(): Promise { - const wallet = createWallet({ + const wallet = new Wallet({ instanceOptions: { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), @@ -102,7 +84,7 @@ describe('Wallet', () => { }); it('supports passing instance options', async () => { - const wallet = createWallet({ + const wallet = new Wallet({ instanceOptions: { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), @@ -140,7 +122,7 @@ describe('Wallet', () => { class DummyService {} - const wallet = createWallet({ + const wallet = new Wallet({ initializationConfigurations: [ { name: 'KeyringController', @@ -197,7 +179,7 @@ describe('Wallet', () => { NoMeta: { state: {} }, }); - const wallet = createWallet({ + const wallet = new Wallet({ instanceOptions: { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), @@ -339,7 +321,7 @@ describe('Wallet', () => { describe('ConnectivityController', () => { it('reports online connectivity status', () => { - const wallet = createWallet({ + const wallet = new Wallet({ instanceOptions: { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), @@ -374,7 +356,7 @@ describe('Wallet', () => { const vault = '{"data":"iOD5pIcPeRZYQ4WdEMsNYoZ3xBxWBafIU8Cr4nD0X4zBvrOA06tGen3sKQ/ValasXSweLnzH9Fk2frkPYmqeJWBtTNYFwdHPe7P970ThZwreSXN1Sqrx9Ad+YzmIN0y89Yg3KrUodPWaRgIZmgWbfDon6ADPgeEDkX0/GAEYET39O7Rx/gL+rcaTpxnpHPTgHiLbhRHWGsS3z+JVomSqoLAO5XVvrJWenO6R3Nzm62BaJaSPrf/pwstZqhSvxTq8hnQf7aR81hWfwYTxNBVG7TC/dniSQ8K5So6PvUN5nzAqvtzzHT2TagOuxQkX88Zi17P8os21jNmNdA90IGYroD+b/mppyRIgRYWtAUQZH9ji36atEuFupszbg8Qw1iaL3EQyUogC30Cpj9ko5bbqhYgqmFHF0J/kflhPHKuO6d4tgSmhYpTumydQRjxaPnlghIS5YI4W+7p9HVBpb+c6IPUz9y/x3Ngbp+ukJwOnXt2U/eZhXrJzi2z1x/nzPg4fzDJoM7k=","iv":"yrZsyC7dso/q7pQ48YX3vw==","keyMetadata":{"algorithm":"PBKDF2","params":{"iterations":600000}},"salt":"s7nIrMWK1lcZVjfdmES1DBML8Uz4ja2fpm8zUz1lWI0="}'; - const wallet = createWallet({ + const wallet = new Wallet({ state: { KeyringController: { vault, @@ -441,6 +423,16 @@ describe('Wallet', () => { }); }); + describe('TransactionController', () => { + it('is wired and exposes its state on the wallet messenger', async () => { + const wallet = await setupWallet(); + + expect( + wallet.messenger.call('TransactionController:getState'), + ).toStrictEqual(expect.objectContaining({ transactions: [] })); + }); + }); + describe('RemoteFeatureFlagController', () => { it('is wired and exposes its state on the wallet messenger', async () => { const wallet = await setupWallet(); @@ -457,7 +449,7 @@ describe('Wallet', () => { }); it('routes injected instanceOptions through to the controller', async () => { - const wallet = createWallet({ + const wallet = new Wallet({ instanceOptions: { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), diff --git a/packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.ts b/packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.ts index 79c16126f81..fd4c28ec163 100644 --- a/packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.ts +++ b/packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.ts @@ -1,8 +1,5 @@ import { Messenger } from '@metamask/messenger'; -import type { - TransactionControllerMessenger, - TransactionControllerOptions, -} from '@metamask/transaction-controller'; +import type { TransactionControllerMessenger } from '@metamask/transaction-controller'; import { TransactionController } from '@metamask/transaction-controller'; import type { InitializationConfiguration } from '../../types'; @@ -16,10 +13,11 @@ export const transactionController: InitializationConfiguration< name: 'TransactionController', init: ({ state, messenger, options }) => { return new TransactionController({ + disableSwaps: false, ...options, messenger, state, - } as TransactionControllerOptions); + }); }, getMessenger: (parent) => { const messenger: TransactionControllerMessenger = new Messenger({ diff --git a/packages/wallet/src/initialization/instances/transaction-controller/types.ts b/packages/wallet/src/initialization/instances/transaction-controller/types.ts index 54a7865f9f6..d9e8eb5198e 100644 --- a/packages/wallet/src/initialization/instances/transaction-controller/types.ts +++ b/packages/wallet/src/initialization/instances/transaction-controller/types.ts @@ -1,5 +1,6 @@ import type { TransactionControllerOptions } from '@metamask/transaction-controller'; -export type TransactionControllerInstanceOptions = Partial< - Omit ->; +export type TransactionControllerInstanceOptions = Omit< + TransactionControllerOptions, + 'messenger' | 'state' +> & { disableSwaps?: boolean }; From 2e54b302b4755821e67ceccc967dac5888c7da7a Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Mon, 6 Jul 2026 10:10:44 +0100 Subject: [PATCH 5/5] fix(wallet): destructure disableSwaps to avoid TS2783 duplicate key error --- .../transaction-controller/transaction-controller.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.ts b/packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.ts index fd4c28ec163..6ee57a27e60 100644 --- a/packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.ts +++ b/packages/wallet/src/initialization/instances/transaction-controller/transaction-controller.ts @@ -12,9 +12,11 @@ export const transactionController: InitializationConfiguration< > = { name: 'TransactionController', init: ({ state, messenger, options }) => { + const { disableSwaps = false, ...rest } = options; + return new TransactionController({ - disableSwaps: false, - ...options, + ...rest, + disableSwaps, messenger, state, });