Skip to content

Commit

Permalink
Fix/default network (#4557)
Browse files Browse the repository at this point in the history
## Explanation

<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?
* Are there any changes whose purpose might not obvious to those
unfamiliar with the domain?
* If your primary goal was to update one package but you found you had
to update another one along the way, why did you do so?
* If you had to upgrade a dependency, why did you do so?
-->
The network configuration gets updated whenever the network state
changes, but until the first state change it might be invalid. So we
need to initialise with the selected network from NetworkController.
- Initial network set with selectedNetworkClientId from `getState` of
`NetworkController`
- Provider removed from constructor, as we can get the provider from
selectedNetworkClient

## References

<!--
Are there any issues that this pull request is tied to? Are there other
links that reviewers should consult to understand these changes better?

For example:

* Fixes #12345
* Related to #67890
-->
* Fixes #843 

## Changelog

<!--
If you're making any consumer-facing changes, list those changes here as
if you were updating a changelog, using the template below as a guide.

(CATEGORY is one of BREAKING, ADDED, CHANGED, DEPRECATED, REMOVED, or
FIXED. For security-related issues, follow the Security Advisory
process.)

Please take care to name the exact pieces of the API you've added or
changed (e.g. types, interfaces, functions, or methods).

If there are any breaking changes, make sure to offer a solution for
consumers to follow once they upgrade to the changes.

Finally, if you're only making changes to development scripts or tests,
you may replace the template below with "None".
-->

### `@metamask/ens-controller`

- **BREAKING**: Provider removed from constructor, instead depends on
selectedNetworkClient for provider
- **FIXED**: Initial network set with selectedNetworkClientId from
`getState` of `NetworkController`.

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [x] I've highlighted breaking changes using the "BREAKING" category
above as appropriate
  • Loading branch information
kanthesha authored and AugmentedMode committed Jul 30, 2024
1 parent d9f41c3 commit 587e3e8
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 91 deletions.
104 changes: 39 additions & 65 deletions packages/ens-controller/src/EnsController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import {
toHex,
InfuraNetworkType,
} from '@metamask/controller-utils';
import type {
NetworkController,
NetworkState,
} from '@metamask/network-controller';
import { defaultState as defaultNetworkState } from '@metamask/network-controller';

import type {
Expand All @@ -19,6 +23,7 @@ import { EnsController, DEFAULT_ENS_NETWORK_MAP } from './EnsController';
import type {
EnsControllerState,
EnsControllerMessenger,
AllowedActions,
} from './EnsController';

const defaultState: EnsControllerState = {
Expand Down Expand Up @@ -74,23 +79,48 @@ const name = 'EnsController';
* @returns A restricted controller messenger.
*/
function getRootMessenger(): RootMessenger {
return new ControllerMessenger();
return new ControllerMessenger<
ExtractAvailableAction<EnsControllerMessenger> | AllowedActions,
ExtractAvailableEvent<EnsControllerMessenger> | never
>();
}

/**
* Constructs the messenger restricted to EnsController actions and events.
*
* @param rootMessenger - The root messenger to base the restricted messenger
* off of.
* @param getNetworkClientByIdMock - Optional mock version of `getNetworkClientById`.
* @returns A restricted controller messenger.
*/
function getRestrictedMessenger(rootMessenger: RootMessenger) {
return rootMessenger.getRestricted<
'EnsController',
'NetworkController:getNetworkClientById'
>({
function getRestrictedMessenger(
rootMessenger: RootMessenger,
getNetworkClientByIdMock?: NetworkController['getNetworkClientById'],
) {
const mockNetworkState = jest.fn<NetworkState, []>().mockReturnValue({
...defaultNetworkState,
selectedNetworkClientId: InfuraNetworkType.mainnet,
});

rootMessenger.registerActionHandler(
'NetworkController:getState',
mockNetworkState,
);

if (!getNetworkClientByIdMock) {
getNetworkClientByIdMock = buildMockGetNetworkClientById();
}
rootMessenger.registerActionHandler(
'NetworkController:getNetworkClientById',
getNetworkClientByIdMock,
);

return rootMessenger.getRestricted<'EnsController', AllowedActions['type']>({
name,
allowedActions: ['NetworkController:getNetworkClientById'],
allowedActions: [
'NetworkController:getNetworkClientById',
'NetworkController:getState',
],
allowedEvents: [],
});
}
Expand Down Expand Up @@ -174,19 +204,13 @@ describe('EnsController', () => {
it('should clear ensResolutionsByAddress state propery on networkDidChange', async () => {
const rootMessenger = getRootMessenger();
const ensControllerMessenger = getRestrictedMessenger(rootMessenger);
const getNetworkClientById = buildMockGetNetworkClientById();
rootMessenger.registerActionHandler(
'NetworkController:getNetworkClientById',
getNetworkClientById,
);
const controller = new EnsController({
messenger: ensControllerMessenger,
state: {
ensResolutionsByAddress: {
[address1Checksum]: 'peaksignal.eth',
},
},
provider: getProvider(),
onNetworkDidChange: (listener) => {
listener({
...defaultNetworkState,
Expand Down Expand Up @@ -492,14 +516,8 @@ describe('EnsController', () => {
it('should return undefined when network is loading', async function () {
const rootMessenger = getRootMessenger();
const ensControllerMessenger = getRestrictedMessenger(rootMessenger);
const getNetworkClientById = buildMockGetNetworkClientById();
rootMessenger.registerActionHandler(
'NetworkController:getNetworkClientById',
getNetworkClientById,
);
const ens = new EnsController({
messenger: ensControllerMessenger,
provider: getProvider(),
onNetworkDidChange: (listener) => {
listener({
...defaultNetworkState,
Expand All @@ -512,19 +530,17 @@ describe('EnsController', () => {

it('should return undefined when network is not ens supported', async function () {
const rootMessenger = getRootMessenger();
const ensControllerMessenger = getRestrictedMessenger(rootMessenger);
const getNetworkClientById = buildMockGetNetworkClientById({
'AAAA-AAAA-AAAA-AAAA': buildCustomNetworkClientConfiguration({
chainId: '0x9999999',
}),
});
rootMessenger.registerActionHandler(
'NetworkController:getNetworkClientById',
const ensControllerMessenger = getRestrictedMessenger(
rootMessenger,
getNetworkClientById,
);
const ens = new EnsController({
messenger: ensControllerMessenger,
provider: getProvider(),
onNetworkDidChange: (listener) => {
listener({
...defaultNetworkState,
Expand All @@ -537,11 +553,6 @@ describe('EnsController', () => {

it('should only resolve an ENS name once', async () => {
const rootMessenger = getRootMessenger();
const getNetworkClientById = buildMockGetNetworkClientById();
rootMessenger.registerActionHandler(
'NetworkController:getNetworkClientById',
getNetworkClientById,
);
const ensControllerMessenger = getRestrictedMessenger(rootMessenger);
const ethProvider = new providersModule.Web3Provider(getProvider());
jest.spyOn(ethProvider, 'resolveName').mockResolvedValue(address1);
Expand All @@ -552,7 +563,6 @@ describe('EnsController', () => {

const ens = new EnsController({
messenger: ensControllerMessenger,
provider: getProvider(),
onNetworkDidChange: (listener) => {
listener({
...defaultNetworkState,
Expand All @@ -567,18 +577,12 @@ describe('EnsController', () => {

it('should fail if lookupAddress through an error', async () => {
const rootMessenger = getRootMessenger();
const getNetworkClientById = buildMockGetNetworkClientById();
rootMessenger.registerActionHandler(
'NetworkController:getNetworkClientById',
getNetworkClientById,
);
const ensControllerMessenger = getRestrictedMessenger(rootMessenger);
const ethProvider = new providersModule.Web3Provider(getProvider());
jest.spyOn(ethProvider, 'lookupAddress').mockRejectedValue('error');
jest.spyOn(providersModule, 'Web3Provider').mockReturnValue(ethProvider);
const ens = new EnsController({
messenger: ensControllerMessenger,
provider: getProvider(),
onNetworkDidChange: (listener) => {
listener({
...defaultNetworkState,
Expand All @@ -592,18 +596,12 @@ describe('EnsController', () => {

it('should fail if lookupAddress returns a null value', async () => {
const rootMessenger = getRootMessenger();
const getNetworkClientById = buildMockGetNetworkClientById();
rootMessenger.registerActionHandler(
'NetworkController:getNetworkClientById',
getNetworkClientById,
);
const ensControllerMessenger = getRestrictedMessenger(rootMessenger);
const ethProvider = new providersModule.Web3Provider(getProvider());
jest.spyOn(ethProvider, 'lookupAddress').mockResolvedValue(null);
jest.spyOn(providersModule, 'Web3Provider').mockReturnValue(ethProvider);
const ens = new EnsController({
messenger: ensControllerMessenger,
provider: getProvider(),
onNetworkDidChange: (listener) => {
listener({
...defaultNetworkState,
Expand All @@ -617,11 +615,6 @@ describe('EnsController', () => {

it('should fail if resolveName through an error', async () => {
const rootMessenger = getRootMessenger();
const getNetworkClientById = buildMockGetNetworkClientById();
rootMessenger.registerActionHandler(
'NetworkController:getNetworkClientById',
getNetworkClientById,
);
const ensControllerMessenger = getRestrictedMessenger(rootMessenger);
const ethProvider = new providersModule.Web3Provider(getProvider());
jest
Expand All @@ -631,7 +624,6 @@ describe('EnsController', () => {
jest.spyOn(providersModule, 'Web3Provider').mockReturnValue(ethProvider);
const ens = new EnsController({
messenger: ensControllerMessenger,
provider: getProvider(),
onNetworkDidChange: (listener) => {
listener({
...defaultNetworkState,
Expand All @@ -645,11 +637,6 @@ describe('EnsController', () => {

it('should fail if resolveName returns a null value', async () => {
const rootMessenger = getRootMessenger();
const getNetworkClientById = buildMockGetNetworkClientById();
rootMessenger.registerActionHandler(
'NetworkController:getNetworkClientById',
getNetworkClientById,
);
const ensControllerMessenger = getRestrictedMessenger(rootMessenger);
const ethProvider = new providersModule.Web3Provider(getProvider());
jest.spyOn(ethProvider, 'resolveName').mockResolvedValue(null);
Expand All @@ -659,7 +646,6 @@ describe('EnsController', () => {
jest.spyOn(providersModule, 'Web3Provider').mockReturnValue(ethProvider);
const ens = new EnsController({
messenger: ensControllerMessenger,
provider: getProvider(),
onNetworkDidChange: (listener) => {
listener({
...defaultNetworkState,
Expand All @@ -673,11 +659,6 @@ describe('EnsController', () => {

it('should fail if registred address is zero x error address', async () => {
const rootMessenger = getRootMessenger();
const getNetworkClientById = buildMockGetNetworkClientById();
rootMessenger.registerActionHandler(
'NetworkController:getNetworkClientById',
getNetworkClientById,
);
const ensControllerMessenger = getRestrictedMessenger(rootMessenger);
const ethProvider = new providersModule.Web3Provider(getProvider());
jest
Expand All @@ -689,7 +670,6 @@ describe('EnsController', () => {
jest.spyOn(providersModule, 'Web3Provider').mockReturnValue(ethProvider);
const ens = new EnsController({
messenger: ensControllerMessenger,
provider: getProvider(),
onNetworkDidChange: (listener) => {
listener({
...defaultNetworkState,
Expand All @@ -703,11 +683,6 @@ describe('EnsController', () => {

it('should fail if the name is registered to a different address than the reverse resolved', async () => {
const rootMessenger = getRootMessenger();
const getNetworkClientById = buildMockGetNetworkClientById();
rootMessenger.registerActionHandler(
'NetworkController:getNetworkClientById',
getNetworkClientById,
);
const ensControllerMessenger = getRestrictedMessenger(rootMessenger);

const ethProvider = new providersModule.Web3Provider(getProvider());
Expand All @@ -718,7 +693,6 @@ describe('EnsController', () => {
jest.spyOn(providersModule, 'Web3Provider').mockReturnValue(ethProvider);
const ens = new EnsController({
messenger: ensControllerMessenger,
provider: getProvider(),
onNetworkDidChange: (listener) => {
listener({
...defaultNetworkState,
Expand Down
68 changes: 42 additions & 26 deletions packages/ens-controller/src/EnsController.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import type {
ExternalProvider,
JsonRpcFetchFunc,
} from '@ethersproject/providers';
import { Web3Provider } from '@ethersproject/providers';
import type { RestrictedControllerMessenger } from '@metamask/base-controller';
import { BaseController } from '@metamask/base-controller';
Expand All @@ -17,6 +13,7 @@ import {
} from '@metamask/controller-utils';
import type {
NetworkControllerGetNetworkClientByIdAction,
NetworkControllerGetStateAction,
NetworkState,
} from '@metamask/network-controller';
import type { Hex } from '@metamask/utils';
Expand Down Expand Up @@ -72,7 +69,9 @@ export type EnsControllerState = {
ensResolutionsByAddress: { [key: string]: string };
};

type AllowedActions = NetworkControllerGetNetworkClientByIdAction;
export type AllowedActions =
| NetworkControllerGetNetworkClientByIdAction
| NetworkControllerGetStateAction;

export type EnsControllerMessenger = RestrictedControllerMessenger<
typeof name,
Expand Down Expand Up @@ -113,20 +112,17 @@ export class EnsController extends BaseController<
* @param options.registriesByChainId - Map between chain IDs and ENS contract addresses.
* @param options.messenger - A reference to the messaging system.
* @param options.state - Initial state to set on this controller.
* @param options.provider - Provider instance.
* @param options.onNetworkDidChange - Allows subscribing to network controller networkDidChange events.
*/
constructor({
registriesByChainId = DEFAULT_ENS_NETWORK_MAP,
messenger,
state = {},
provider,
onNetworkDidChange,
}: {
registriesByChainId?: Record<number, Hex>;
messenger: EnsControllerMessenger;
state?: Partial<EnsControllerState>;
provider?: ExternalProvider | JsonRpcFetchFunc;
onNetworkDidChange?: (
listener: (networkState: NetworkState) => void,
) => void;
Expand All @@ -153,26 +149,12 @@ export class EnsController extends BaseController<
},
});

if (provider && onNetworkDidChange) {
this.#setDefaultEthProvider(registriesByChainId);

if (onNetworkDidChange) {
onNetworkDidChange(({ selectedNetworkClientId }) => {
this.resetState();
const selectedNetworkClient = this.messagingSystem.call(
'NetworkController:getNetworkClientById',
selectedNetworkClientId,
);
const currentChainId = selectedNetworkClient.configuration.chainId;

if (this.#getChainEnsSupport(currentChainId)) {
this.#ethProvider = new Web3Provider(provider, {
chainId: convertHexToDecimal(currentChainId),
name: CHAIN_ID_TO_ETHERS_NETWORK_NAME_MAP[
currentChainId as ChainId
],
ensAddress: registriesByChainId[parseInt(currentChainId, 16)],
});
} else {
this.#ethProvider = null;
}
this.#setEthProvider(selectedNetworkClientId, registriesByChainId);
});
}
}
Expand Down Expand Up @@ -295,6 +277,40 @@ export class EnsController extends BaseController<
return true;
}

#setDefaultEthProvider(registriesByChainId?: Record<number, Hex>) {
const { selectedNetworkClientId } = this.messagingSystem.call(
'NetworkController:getState',
);
this.#setEthProvider(selectedNetworkClientId, registriesByChainId);
}

#setEthProvider(
selectedNetworkClientId: string,
registriesByChainId?: Record<number, Hex>,
) {
const {
configuration: { chainId: currentChainId },
provider,
} = this.messagingSystem.call(
'NetworkController:getNetworkClientById',
selectedNetworkClientId,
);

if (
registriesByChainId &&
registriesByChainId[parseInt(currentChainId, 16)] &&
this.#getChainEnsSupport(currentChainId)
) {
this.#ethProvider = new Web3Provider(provider, {
chainId: convertHexToDecimal(currentChainId),
name: CHAIN_ID_TO_ETHERS_NETWORK_NAME_MAP[currentChainId as ChainId],
ensAddress: registriesByChainId[parseInt(currentChainId, 16)],
});
} else {
this.#ethProvider = null;
}
}

/**
* Check if the chain supports ENS.
*
Expand Down

0 comments on commit 587e3e8

Please sign in to comment.