Skip to content
Draft
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
2 changes: 2 additions & 0 deletions packages/wallet/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Overrides the `fetch` implementation used by `NetworkController`'s RPC service. Defaults to `globalThis.fetch`. Allows platform-specific fetch implementations (e.g. React Native) to be injected.
- Add optional `logger` field to `WalletOptions` ([#XXXX](https://github.com/MetaMask/core/pull/XXXX))
- When provided, emits an `info`-level log after each controller finishes initializing: `[wallet] ${ControllerName}: initialized`. Defaults to no-op (no output). Pass `console` during development to observe initialization order.
- Accept optional `initializationConfigurations` in the `Wallet` constructor ([#XXXX](https://github.com/MetaMask/core/pull/XXXX))
- Allows callers to override or extend the default set of controller configurations.

[Unreleased]: https://github.com/MetaMask/core/
21 changes: 21 additions & 0 deletions packages/wallet/src/Wallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,27 @@ describe('Wallet', () => {
expect(Object.keys(wallet.state)).toStrictEqual(['WithMeta', 'NoMeta']);
});

it('initializes additional controllers passed via initializationConfigurations', () => {
const customInit = jest
.fn()
.mockReturnValue({ instance: {} as never });
const customMessenger = jest.fn().mockReturnValue({} as never);

wallet = new Wallet({
...options,
initializationConfigurations: [
{
name: 'CustomController',
init: customInit,
messenger: customMessenger,
},
],
});

expect(customInit).toHaveBeenCalledTimes(1);
expect(customMessenger).toHaveBeenCalledTimes(1);
});

it('publishes Wallet:destroyed exactly once on destroy', async () => {
wallet = new Wallet(options);

Expand Down
12 changes: 11 additions & 1 deletion packages/wallet/src/Wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ import type {
RootMessenger,
} from './initialization';
import { initialize } from './initialization';
import type { InitializationConfiguration } from './initialization/types';
import type { WalletOptions } from './types';

export class Wallet {
// messenger is typed against the default controller set. Action/event types for
// any additional configurations passed to the constructor are not reflected here.
public readonly messenger: RootMessenger<DefaultActions, DefaultEvents>;

readonly #instances: DefaultInstances;
Expand All @@ -23,14 +26,21 @@ export class Wallet {

#destroyed = false;

constructor({ state, ...options }: WalletOptions) {
constructor({
state,
initializationConfigurations,
...options
}: WalletOptions & {
initializationConfigurations?: InitializationConfiguration<unknown, unknown>[];
}) {
this.messenger = new Messenger({
namespace: 'Wallet',
});

this.#instances = initialize({
state: state ?? {},
messenger: this.messenger,
initializationConfigurations,
options,
});

Expand Down
17 changes: 16 additions & 1 deletion packages/wallet/src/initialization/initialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,32 @@ import type { InitializationConfiguration } from './types';
export type InitializeArgs = {
state: Record<string, Json>;
messenger: RootMessenger;
initializationConfigurations?: InitializationConfiguration<
unknown,
unknown
>[];
options: WalletOptions;
};

export function initialize({
state,
messenger,
initializationConfigurations = [],
options,
}: InitializeArgs): DefaultInstances {
const overriddenConfiguration = initializationConfigurations.map(
(config) => config.name,
);

const configurationEntries = initializationConfigurations.concat(
Object.values(defaultConfigurations).filter(
(config) => !overriddenConfiguration.includes(config.name),
),
);

const instances: Record<string, unknown> = {};

for (const config of Object.values(defaultConfigurations) as InitializationConfiguration<unknown, unknown>[]) {
for (const config of configurationEntries) {
const { name } = config;

const instanceState = state[name];
Expand Down
Loading