Skip to content

Commit 8eba9b2

Browse files
Normalize addresses in name controller (#1732)
Normalize addresses and chain IDs in the NameController state by storing them in lowercase. Replace the getChainId callback with the variation option in the SetNameRequest and UpdateProposedNamesRequest.
1 parent 59e0492 commit 8eba9b2

File tree

10 files changed

+295
-70
lines changed

10 files changed

+295
-70
lines changed

packages/name-controller/src/NameController.test.ts

+192-10
Large diffs are not rendered by default.

packages/name-controller/src/NameController.ts

+73-33
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {
1111
import { NameType } from './types';
1212

1313
const DEFAULT_UPDATE_DELAY = 60 * 2; // 2 Minutes
14+
const DEFAULT_VARIATION = '';
1415

1516
const controllerName = 'NameController';
1617

@@ -71,7 +72,6 @@ export type NameControllerMessenger = RestrictedControllerMessenger<
7172
>;
7273

7374
export type NameControllerOptions = {
74-
getChainId: () => string;
7575
messenger: NameControllerMessenger;
7676
providers: NameProvider[];
7777
state?: Partial<NameControllerState>;
@@ -83,6 +83,7 @@ export type UpdateProposedNamesRequest = {
8383
type: NameType;
8484
sourceIds?: string[];
8585
onlyUpdateAfterDelay?: boolean;
86+
variation?: string;
8687
};
8788

8889
export type UpdateProposedNamesResult = {
@@ -94,6 +95,7 @@ export type SetNameRequest = {
9495
type: NameType;
9596
name: string | null;
9697
sourceId?: string;
98+
variation?: string;
9799
};
98100

99101
/**
@@ -104,8 +106,6 @@ export class NameController extends BaseControllerV2<
104106
NameControllerState,
105107
NameControllerMessenger
106108
> {
107-
#getChainId: () => string;
108-
109109
#providers: NameProvider[];
110110

111111
#updateDelay: number;
@@ -114,14 +114,12 @@ export class NameController extends BaseControllerV2<
114114
* Construct a Name controller.
115115
*
116116
* @param options - Controller options.
117-
* @param options.getChainId - Callback that returns the chain ID of the current network.
118117
* @param options.messenger - Restricted controller messenger for the name controller.
119118
* @param options.providers - Array of name provider instances to propose names.
120119
* @param options.state - Initial state to set on the controller.
121120
* @param options.updateDelay - The delay in seconds before a new request to a source should be made.
122121
*/
123122
constructor({
124-
getChainId,
125123
messenger,
126124
providers,
127125
state,
@@ -134,7 +132,6 @@ export class NameController extends BaseControllerV2<
134132
state: { ...getDefaultState(), ...state },
135133
});
136134

137-
this.#getChainId = getChainId;
138135
this.#providers = providers;
139136
this.#updateDelay = updateDelay ?? DEFAULT_UPDATE_DELAY;
140137
}
@@ -147,14 +144,15 @@ export class NameController extends BaseControllerV2<
147144
* @param request.sourceId - Optional ID of the source of the proposed name.
148145
* @param request.type - Type of value to set the name for.
149146
* @param request.value - Value to set the name for.
147+
* @param request.variation - Variation of the raw value to set the name for. The chain ID if the type is Ethereum address.
150148
*/
151149
setName(request: SetNameRequest) {
152150
this.#validateSetNameRequest(request);
153151

154-
const { value, type, name, sourceId: requestSourceId } = request;
152+
const { value, type, name, sourceId: requestSourceId, variation } = request;
155153
const sourceId = requestSourceId ?? null;
156154

157-
this.#updateEntry(value, type, (entry: NameEntry) => {
155+
this.#updateEntry(value, type, variation, (entry: NameEntry) => {
158156
entry.name = name;
159157
entry.sourceId = sourceId;
160158
});
@@ -167,19 +165,18 @@ export class NameController extends BaseControllerV2<
167165
* @param request.value - Value to update the proposed names for.
168166
* @param request.type - Type of value to update the proposed names for.
169167
* @param request.sourceIds - Optional array of source IDs to limit which sources are used by the providers. If not provided, all sources in all providers will be used.
168+
* @param request.variation - Variation of the raw value to update proposed names for. The chain ID if the type is Ethereum address.
170169
* @returns The updated proposed names for the value.
171170
*/
172171
async updateProposedNames(
173172
request: UpdateProposedNamesRequest,
174173
): Promise<UpdateProposedNamesResult> {
175174
this.#validateUpdateProposedNamesRequest(request);
176175

177-
const chainId = this.#getChainId();
178-
179176
const providerResponses = (
180177
await Promise.all(
181178
this.#providers.map((provider) =>
182-
this.#getProviderResponse(request, chainId, provider),
179+
this.#getProviderResponse(request, provider),
183180
),
184181
)
185182
).filter((response) => Boolean(response)) as NameProviderResult[];
@@ -194,10 +191,10 @@ export class NameController extends BaseControllerV2<
194191
request: UpdateProposedNamesRequest,
195192
providerResponses: NameProviderResult[],
196193
) {
197-
const { value, type } = request;
194+
const { value, type, variation } = request;
198195
const currentTime = this.#getCurrentTimeSeconds();
199196

200-
this.#updateEntry(value, type, (entry: NameEntry) => {
197+
this.#updateEntry(value, type, variation, (entry: NameEntry) => {
201198
this.#removeDormantProposedNames(entry.proposedNames, type);
202199

203200
for (const providerResponse of providerResponses) {
@@ -268,27 +265,30 @@ export class NameController extends BaseControllerV2<
268265

269266
async #getProviderResponse(
270267
request: UpdateProposedNamesRequest,
271-
chainId: string,
272268
provider: NameProvider,
273269
): Promise<NameProviderResult | undefined> {
274270
const {
275271
value,
276272
type,
277273
sourceIds: requestedSourceIds,
278274
onlyUpdateAfterDelay,
275+
variation,
279276
} = request;
280277

281-
const variationKey = this.#getTypeVariationKey(type);
278+
/* istanbul ignore next */
279+
const variationKey = variation ?? DEFAULT_VARIATION;
282280
const supportedSourceIds = this.#getSourceIds(provider, type);
283281
const currentTime = this.#getCurrentTimeSeconds();
282+
const normalizedValue = this.#normalizeValue(value, type);
284283

285284
const matchingSourceIds = supportedSourceIds.filter((sourceId) => {
286285
if (requestedSourceIds && !requestedSourceIds.includes(sourceId)) {
287286
return false;
288287
}
289288

290289
if (onlyUpdateAfterDelay) {
291-
const entry = this.state.names[type]?.[value]?.[variationKey] ?? {};
290+
const entry =
291+
this.state.names[type]?.[normalizedValue]?.[variationKey] ?? {};
292292
const proposedNamesEntry = entry.proposedNames?.[sourceId] ?? {};
293293
const lastRequestTime = proposedNamesEntry.lastRequestTime ?? 0;
294294
const updateDelay = proposedNamesEntry.updateDelay ?? this.#updateDelay;
@@ -306,10 +306,10 @@ export class NameController extends BaseControllerV2<
306306
}
307307

308308
const providerRequest: NameProviderRequest = {
309-
chainId,
310-
value,
309+
value: this.#normalizeValue(value, type),
311310
type,
312311
sourceIds: requestedSourceIds ? matchingSourceIds : undefined,
312+
variation: this.#normalizeVariation(variationKey, type),
313313
};
314314

315315
let responseError: unknown | undefined;
@@ -374,65 +374,85 @@ export class NameController extends BaseControllerV2<
374374
};
375375
}
376376

377+
#normalizeValue(value: string, type: NameType): string {
378+
/* istanbul ignore next */
379+
switch (type) {
380+
case NameType.ETHEREUM_ADDRESS:
381+
return value.toLowerCase();
382+
383+
default:
384+
return value;
385+
}
386+
}
387+
388+
#normalizeVariation(variation: string, type: NameType): string {
389+
/* istanbul ignore next */
390+
switch (type) {
391+
case NameType.ETHEREUM_ADDRESS:
392+
return variation.toLowerCase();
393+
394+
default:
395+
return variation;
396+
}
397+
}
398+
377399
#updateEntry(
378400
value: string,
379401
type: NameType,
402+
variation: string | undefined,
380403
callback: (entry: NameEntry) => void,
381404
) {
382-
const variationKey = this.#getTypeVariationKey(type);
405+
/* istanbul ignore next */
406+
const variationKey = variation ?? DEFAULT_VARIATION;
407+
const normalizedValue = this.#normalizeValue(value, type);
408+
const normalizedVariation = this.#normalizeVariation(variationKey, type);
383409

384410
this.update((state) => {
385411
const typeEntries = state.names[type] || {};
386412
state.names[type] = typeEntries;
387413

388-
const variationEntries = typeEntries[value] || {};
389-
typeEntries[value] = variationEntries;
414+
const variationEntries = typeEntries[normalizedValue] || {};
415+
typeEntries[normalizedValue] = variationEntries;
390416

391-
const entry = variationEntries[variationKey] ?? {
417+
const entry = variationEntries[normalizedVariation] ?? {
392418
proposedNames: {},
393419
name: null,
394420
sourceId: null,
395421
};
396-
variationEntries[variationKey] = entry;
422+
variationEntries[normalizedVariation] = entry;
397423

398424
callback(entry);
399425
});
400426
}
401427

402-
#getTypeVariationKey(type: NameType): string {
403-
switch (type) {
404-
default: {
405-
return this.#getChainId();
406-
}
407-
}
408-
}
409-
410428
#getCurrentTimeSeconds(): number {
411429
return Math.round(Date.now() / 1000);
412430
}
413431

414432
#validateSetNameRequest(request: SetNameRequest) {
415-
const { name, value, type, sourceId } = request;
433+
const { name, value, type, sourceId, variation } = request;
416434
const errorMessages: string[] = [];
417435

418436
this.#validateValue(value, errorMessages);
419437
this.#validateType(type, errorMessages);
420438
this.#validateName(name, errorMessages);
421439
this.#validateSourceId(sourceId, type, name, errorMessages);
440+
this.#validateVariation(variation, type, errorMessages);
422441

423442
if (errorMessages.length) {
424443
throw new Error(errorMessages.join(' '));
425444
}
426445
}
427446

428447
#validateUpdateProposedNamesRequest(request: UpdateProposedNamesRequest) {
429-
const { value, type, sourceIds } = request;
448+
const { value, type, sourceIds, variation } = request;
430449
const errorMessages: string[] = [];
431450

432451
this.#validateValue(value, errorMessages);
433452
this.#validateType(type, errorMessages);
434453
this.#validateSourceIds(sourceIds, type, errorMessages);
435454
this.#validateDuplicateSourceIds(type, errorMessages);
455+
this.#validateVariation(variation, type, errorMessages);
436456

437457
if (errorMessages.length) {
438458
throw new Error(errorMessages.join(' '));
@@ -536,6 +556,26 @@ export class NameController extends BaseControllerV2<
536556
}
537557
}
538558

559+
#validateVariation(
560+
variation: string | undefined,
561+
type: string,
562+
errorMessages: string[],
563+
) {
564+
if (type !== NameType.ETHEREUM_ADDRESS) {
565+
return;
566+
}
567+
568+
if (
569+
!variation?.length ||
570+
typeof variation !== 'string' ||
571+
!variation.match(/^0x[0-9A-Fa-f]+$/u)
572+
) {
573+
errorMessages.push(
574+
`Must specify a chain ID in hexidecimal format for variation when using '${type}' type.`,
575+
);
576+
}
577+
}
578+
539579
#getAllSourceIds(type: NameType): string[] {
540580
return (
541581
this.#providers

packages/name-controller/src/providers/ens.test.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ describe('ENSNameProvider', () => {
3939

4040
const response = await provider.getProposedNames({
4141
value: VALUE_MOCK,
42-
chainId: CHAIN_ID_MOCK,
42+
variation: CHAIN_ID_MOCK,
4343
type: NameType.ETHEREUM_ADDRESS,
4444
});
4545

@@ -59,7 +59,7 @@ describe('ENSNameProvider', () => {
5959

6060
await provider.getProposedNames({
6161
value: VALUE_MOCK,
62-
chainId: CHAIN_ID_MOCK,
62+
variation: CHAIN_ID_MOCK,
6363
type: NameType.ETHEREUM_ADDRESS,
6464
});
6565

@@ -75,7 +75,7 @@ describe('ENSNameProvider', () => {
7575

7676
const response = await provider.getProposedNames({
7777
value: VALUE_MOCK,
78-
chainId: CHAIN_ID_MOCK,
78+
variation: CHAIN_ID_MOCK,
7979
type: NameType.ETHEREUM_ADDRESS,
8080
});
8181

@@ -96,7 +96,7 @@ describe('ENSNameProvider', () => {
9696
await expect(
9797
provider.getProposedNames({
9898
value: VALUE_MOCK,
99-
chainId: CHAIN_ID_MOCK,
99+
variation: CHAIN_ID_MOCK,
100100
type: NameType.ETHEREUM_ADDRESS,
101101
}),
102102
).rejects.toThrow('TestError');

packages/name-controller/src/providers/ens.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export class ENSNameProvider implements NameProvider {
5555
};
5656
}
5757

58-
const { value, chainId } = request;
58+
const { value, variation: chainId } = request;
5959

6060
log('Invoking callback', { value, chainId });
6161

0 commit comments

Comments
 (0)