Skip to content

Commit

Permalink
Convert the ENS controller to the BaseController v2 API (#1134)
Browse files Browse the repository at this point in the history
Convert the ENS controller to the BaseController v2 API
  • Loading branch information
cryptodev-2s authored and MajorLift committed Oct 11, 2023
1 parent 32edda3 commit e6b537c
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 52 deletions.
104 changes: 86 additions & 18 deletions packages/ens-controller/src/EnsController.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ControllerMessenger } from '@metamask/base-controller';
import { toChecksumHexAddress } from '@metamask/controller-utils';
import { EnsController } from './EnsController';

Expand All @@ -11,14 +12,33 @@ const address1Checksum = toChecksumHexAddress(address1);
const address2Checksum = toChecksumHexAddress(address2);
const address3Checksum = toChecksumHexAddress(address3);

const name = 'EnsController';

/**
* Constructs a restricted controller messenger.
*
* @returns A restricted controller messenger.
*/
function getMessenger() {
return new ControllerMessenger().getRestricted<typeof name, never, never>({
name,
});
}

describe('EnsController', () => {
it('should set default state', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(controller.state).toStrictEqual({ ensEntries: {} });
});

it('should add a new ENS entry and return true', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(controller.set('1', name1, address1)).toStrictEqual(true);
expect(controller.state).toStrictEqual({
ensEntries: {
Expand All @@ -34,7 +54,10 @@ describe('EnsController', () => {
});

it('should add a new ENS entry with null address and return true', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(controller.set('1', name1, null)).toStrictEqual(true);
expect(controller.state).toStrictEqual({
ensEntries: {
Expand All @@ -50,7 +73,10 @@ describe('EnsController', () => {
});

it('should update an ENS entry and return true', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(controller.set('1', name1, address1)).toStrictEqual(true);
expect(controller.set('1', name1, address2)).toStrictEqual(true);
expect(controller.state).toStrictEqual({
Expand All @@ -67,7 +93,10 @@ describe('EnsController', () => {
});

it('should update an ENS entry with null address and return true', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(controller.set('1', name1, address1)).toStrictEqual(true);
expect(controller.set('1', name1, null)).toStrictEqual(true);
expect(controller.state).toStrictEqual({
Expand All @@ -84,7 +113,10 @@ describe('EnsController', () => {
});

it('should not update an ENS entry if the address is the same (valid address) and return false', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(controller.set('1', name1, address1)).toStrictEqual(true);
expect(controller.set('1', name1, address1)).toStrictEqual(false);
expect(controller.state).toStrictEqual({
Expand All @@ -101,7 +133,10 @@ describe('EnsController', () => {
});

it('should not update an ENS entry if the address is the same (null) and return false', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(controller.set('1', name1, null)).toStrictEqual(true);
expect(controller.set('1', name1, null)).toStrictEqual(false);
expect(controller.state).toStrictEqual({
Expand All @@ -118,7 +153,10 @@ describe('EnsController', () => {
});

it('should add multiple ENS entries and update without side effects', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(controller.set('1', name1, address1)).toStrictEqual(true);
expect(controller.set('1', name2, address2)).toStrictEqual(true);
expect(controller.set('2', name1, address1)).toStrictEqual(true);
Expand Down Expand Up @@ -149,7 +187,10 @@ describe('EnsController', () => {
});

it('should get ENS entry by chainId and ensName', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(controller.set('1', name1, address1)).toStrictEqual(true);
expect(controller.get('1', name1)).toStrictEqual({
address: address1Checksum,
Expand All @@ -159,19 +200,28 @@ describe('EnsController', () => {
});

it('should return null when getting nonexistent name', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(controller.set('1', name1, address1)).toStrictEqual(true);
expect(controller.get('1', name2)).toBeNull();
});

it('should return null when getting nonexistent chainId', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(controller.set('1', name1, address1)).toStrictEqual(true);
expect(controller.get('2', name1)).toBeNull();
});

it('should throw on attempt to set invalid ENS entry: chainId', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(() => {
controller.set('a', name1, address1);
}).toThrow(
Expand All @@ -181,15 +231,21 @@ describe('EnsController', () => {
});

it('should throw on attempt to set invalid ENS entry: ENS name', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(() => {
controller.set('1', 'foo.eth', address1);
}).toThrow('Invalid ENS name: foo.eth');
expect(controller.state).toStrictEqual({ ensEntries: {} });
});

it('should throw on attempt to set invalid ENS entry: address', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(() => {
controller.set('1', name1, 'foo');
}).toThrow(
Expand All @@ -199,14 +255,20 @@ describe('EnsController', () => {
});

it('should remove an ENS entry and return true', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(controller.set('1', name1, address1)).toStrictEqual(true);
expect(controller.delete('1', name1)).toStrictEqual(true);
expect(controller.state).toStrictEqual({ ensEntries: {} });
});

it('should return false if an ENS entry was NOT deleted', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
controller.set('1', name1, address1);
expect(controller.delete('1', 'bar')).toStrictEqual(false);
expect(controller.delete('2', 'bar')).toStrictEqual(false);
Expand All @@ -224,7 +286,10 @@ describe('EnsController', () => {
});

it('should add multiple ENS entries and remove without side effects', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(controller.set('1', name1, address1)).toStrictEqual(true);
expect(controller.set('1', name2, address2)).toStrictEqual(true);
expect(controller.set('2', name1, address1)).toStrictEqual(true);
Expand All @@ -250,7 +315,10 @@ describe('EnsController', () => {
});

it('should clear all ENS entries', () => {
const controller = new EnsController();
const messenger = getMessenger();
const controller = new EnsController({
messenger,
});
expect(controller.set('1', name1, address1)).toStrictEqual(true);
expect(controller.set('1', name2, address2)).toStrictEqual(true);
expect(controller.set('2', name1, address1)).toStrictEqual(true);
Expand Down
100 changes: 66 additions & 34 deletions packages/ens-controller/src/EnsController.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import {
BaseController,
BaseConfig,
BaseState,
BaseControllerV2,
RestrictedControllerMessenger,
} from '@metamask/base-controller';
import {
normalizeEnsName,
isValidHexAddress,
toChecksumHexAddress,
} from '@metamask/controller-utils';

const name = 'EnsController';

/**
* @type EnsEntry
*
Expand All @@ -17,51 +18,83 @@ import {
* @property ensName - The ENS name
* @property address - Hex address with the ENS name, or null
*/
export interface EnsEntry {
export type EnsEntry = {
chainId: string;
ensName: string;
address: string | null;
}
};

/**
* @type EnsState
* @type EnsControllerState
*
* ENS controller state
* @property ensEntries - Object of ENS entry objects
*/
export interface EnsState extends BaseState {
ensEntries: { [chainId: string]: { [ensName: string]: EnsEntry } };
}
export type EnsControllerState = {
ensEntries: {
[chainId: string]: {
[ensName: string]: EnsEntry;
};
};
};

export type EnsControllerMessenger = RestrictedControllerMessenger<
typeof name,
never,
never,
never,
never
>;

const metadata = {
ensEntries: { persist: true, anonymous: false },
};

const defaultState = {
ensEntries: {},
};

/**
* Controller that manages a list ENS names and their resolved addresses
* by chainId. A null address indicates an unresolved ENS name.
*/
export class EnsController extends BaseController<BaseConfig, EnsState> {
/**
* Name of this controller used during composition
*/
override name = 'EnsController';

export class EnsController extends BaseControllerV2<
typeof name,
EnsControllerState,
EnsControllerMessenger
> {
/**
* Creates an EnsController instance.
*
* @param config - Initial options used to configure this controller.
* @param state - Initial state to set on this controller.
* @param options - Constructor options.
* @param options.messenger - A reference to the messaging system.
* @param options.state - Initial state to set on this controller.
*/
constructor(config?: Partial<BaseConfig>, state?: Partial<EnsState>) {
super(config, state);

this.defaultState = { ensEntries: {} };

this.initialize();
constructor({
messenger,
state,
}: {
messenger: EnsControllerMessenger;
state?: Partial<EnsControllerState>;
}) {
super({
name,
metadata,
messenger,
state: {
...defaultState,
...state,
},
});
}

/**
* Remove all chain Ids and ENS entries from state.
*/
clear() {
this.update({ ensEntries: {} });
this.update((state) => {
state.ensEntries = {};
});
}

/**
Expand All @@ -81,14 +114,13 @@ export class EnsController extends BaseController<BaseConfig, EnsState> {
return false;
}

const ensEntries = Object.assign({}, this.state.ensEntries);
delete ensEntries[chainId][normalizedEnsName];
this.update((state) => {
delete state.ensEntries[chainId][normalizedEnsName];

if (Object.keys(ensEntries[chainId]).length === 0) {
delete ensEntries[chainId];
}

this.update({ ensEntries });
if (Object.keys(state.ensEntries[chainId]).length === 0) {
delete state.ensEntries[chainId];
}
});
return true;
}

Expand Down Expand Up @@ -146,8 +178,8 @@ export class EnsController extends BaseController<BaseConfig, EnsState> {
return false;
}

this.update({
ensEntries: {
this.update((state) => {
state.ensEntries = {
...this.state.ensEntries,
[chainId]: {
...this.state.ensEntries[chainId],
Expand All @@ -157,7 +189,7 @@ export class EnsController extends BaseController<BaseConfig, EnsState> {
ensName: normalizedEnsName,
},
},
},
};
});
return true;
}
Expand Down

0 comments on commit e6b537c

Please sign in to comment.