Skip to content

Commit 0e53d1b

Browse files
committed
Merge branch 'wip-v6.16.0-ens' into wip-v6.17
2 parents 999af5f + f2ffb86 commit 0e53d1b

File tree

4 files changed

+184
-62
lines changed

4 files changed

+184
-62
lines changed

src.ts/providers/abstract-provider.ts

Lines changed: 7 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,12 @@
1414
// migrate the listener to the static event. We also need to maintain a map
1515
// of Signer/ENS name to address so we can sync respond to listenerCount.
1616

17-
import { getAddress, resolveAddress } from "../address/index.js";
18-
import { ZeroAddress } from "../constants/index.js";
19-
import { Contract } from "../contract/index.js";
20-
import { namehash } from "../hash/index.js";
17+
import { resolveAddress } from "../address/index.js";
2118
import { Transaction } from "../transaction/index.js";
2219
import {
2320
concat, dataLength, dataSlice, hexlify, isHexString,
2421
getBigInt, getBytes, getNumber,
25-
isCallException, isError, makeError, assert, assertArgument,
22+
isCallException, makeError, assert, assertArgument,
2623
FetchRequest,
2724
toBeArray, toQuantity,
2825
defineProperties, EventPayload, resolveProperties,
@@ -1189,60 +1186,24 @@ export class AbstractProvider implements Provider {
11891186
});
11901187
}
11911188

1192-
async getResolver(name: string): Promise<null | EnsResolver> {
1193-
return await EnsResolver.fromName(this, name);
1189+
async getResolver(name: string, preferUniversal?: boolean): Promise<null | EnsResolver> {
1190+
return await EnsResolver.fromName(this, name, preferUniversal);
11941191
}
11951192

11961193
async getAvatar(name: string): Promise<null | string> {
1197-
const resolver = await this.getResolver(name);
1194+
const resolver = await this.getResolver(name, true);
11981195
if (resolver) { return await resolver.getAvatar(); }
11991196
return null;
12001197
}
12011198

12021199
async resolveName(name: string): Promise<null | string>{
1203-
const resolver = await this.getResolver(name);
1200+
const resolver = await this.getResolver(name, true);
12041201
if (resolver) { return await resolver.getAddress(); }
12051202
return null;
12061203
}
12071204

12081205
async lookupAddress(address: string): Promise<null | string> {
1209-
address = getAddress(address);
1210-
const node = namehash(address.substring(2).toLowerCase() + ".addr.reverse");
1211-
1212-
try {
1213-
1214-
const ensAddr = await EnsResolver.getEnsAddress(this);
1215-
const ensContract = new Contract(ensAddr, [
1216-
"function resolver(bytes32) view returns (address)"
1217-
], this);
1218-
1219-
const resolver = await ensContract.resolver(node);
1220-
if (resolver == null || resolver === ZeroAddress) { return null; }
1221-
1222-
const resolverContract = new Contract(resolver, [
1223-
"function name(bytes32) view returns (string)"
1224-
], this);
1225-
const name = await resolverContract.name(node);
1226-
1227-
// Failed forward resolution
1228-
const check = await this.resolveName(name);
1229-
if (check !== address) { return null; }
1230-
1231-
return name;
1232-
1233-
} catch (error) {
1234-
// No data was returned from the resolver
1235-
if (isError(error, "BAD_DATA") && error.value === "0x") {
1236-
return null;
1237-
}
1238-
1239-
// Something reerted
1240-
if (isError(error, "CALL_EXCEPTION")) { return null; }
1241-
1242-
throw error;
1243-
}
1244-
1245-
return null;
1206+
return await EnsResolver.lookupAddress(this, address);
12461207
}
12471208

12481209
async waitForTransaction(hash: string, _confirms?: null | number, timeout?: null | number): Promise<null | TransactionReceipt> {

src.ts/providers/ens-resolver.ts

Lines changed: 117 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,21 @@ export class EnsResolver {
175175
// For EIP-2544 names, the ancestor that provided the resolver
176176
#supports2544: null | Promise<boolean>;
177177

178-
#resolver: Contract;
178+
readonly #resolver: Contract;
179179

180-
constructor(provider: AbstractProvider, address: string, name: string) {
180+
readonly #universal: null | Contract;
181+
182+
constructor(provider: AbstractProvider, address: string, name: string, universalResolver?: boolean) {
181183
defineProperties<EnsResolver>(this, { provider, address, name });
182-
this.#supports2544 = null;
184+
185+
if (universalResolver) {
186+
this.#supports2544 = Promise.resolve(true);
187+
this.#universal = createUniversal(provider, address);
188+
} else {
189+
this.#supports2544 = null;
190+
this.#universal = null;
191+
}
192+
183193

184194
this.#resolver = new Contract(address, [
185195
"function supportsInterface(bytes4) view returns (bool)",
@@ -238,18 +248,22 @@ export class EnsResolver {
238248
funcName = "resolve(bytes,bytes)";
239249
}
240250

241-
params.push({
242-
enableCcipRead: true
243-
});
251+
params.push({ enableCcipRead: true });
244252

245253
try {
246-
const result = await this.#resolver[funcName](...params);
254+
let result;
255+
if (this.#universal) {
256+
result = (await this.#universal.resolve(...params)).result;
257+
} else {
258+
result = await this.#resolver[funcName](...params);
259+
}
247260

248261
if (fragment) {
249262
return iface.decodeFunctionResult(fragment, result)[0];
250263
}
251264

252265
return result;
266+
253267
} catch (error: any) {
254268
if (!isError(error, "CALL_EXCEPTION")) { throw error; }
255269
}
@@ -572,11 +586,86 @@ export class EnsResolver {
572586
return null;
573587
}
574588

589+
static async lookupAddress(provider: AbstractProvider, address: string, coinType?: number): Promise<null | string> {
590+
address = getAddress(address);
591+
if (coinType == null) { coinType = 60; }
592+
593+
// We have a Universal resolver, use it
594+
const universal = await getUniversal(provider);
595+
if (universal) {
596+
const result = await universal.reverse(address, coinType, {
597+
enableCcipRead: true
598+
});
599+
600+
return result.primary || null;
601+
}
602+
603+
// Use legacy reverse lookup
604+
605+
assert(coinType === 60, "lookupAddress coinType requires ENS Universal Resolver", "UNSUPPORTED_OPERATION", {
606+
operation: "lookupAddress"
607+
});
608+
609+
try {
610+
// Legacy resolver uses namehash of the lowercase name
611+
const node = namehash(`${ address.toLowerCase().substring(2) }.addr.reverse`);
612+
613+
const ensAddr = await EnsResolver.getEnsAddress(provider);
614+
const ensContract = new Contract(ensAddr, [
615+
"function resolver(bytes32) view returns (address)"
616+
], provider);
617+
618+
const resolver = await ensContract.resolver(node);
619+
if (resolver == null || resolver === ZeroAddress) { return null; }
620+
621+
const resolverContract = new Contract(resolver, [
622+
"function name(bytes32) view returns (string)"
623+
], provider);
624+
625+
const name = await resolverContract.name(node);
626+
627+
// Failed forward resolution
628+
const check = await provider.resolveName(name);
629+
if (check !== address) { return null; }
630+
return name;
631+
632+
} catch (error) {
633+
// No data was returned from the resolver
634+
if (isError(error, "BAD_DATA") && error.value === "0x") {
635+
return null;
636+
}
637+
638+
// Something reverted
639+
if (isError(error, "CALL_EXCEPTION")) { return null; }
640+
641+
throw error;
642+
}
643+
644+
return null;
645+
}
646+
575647
/**
576648
* Resolve to the ENS resolver for %%name%% using %%provider%% or
577649
* ``null`` if unconfigured.
650+
*
651+
* If %%preferUniversal%% and the network supports ENS Universal
652+
* Resolver, a resolver is always returned (even if the name doesn't
653+
* have one) and calls are resolved by it. The Universal Resolver is
654+
* more eth_call efficient, as it performs more on-chain, but is not
655+
* the actual resolver, if for example the setters are required.
578656
*/
579-
static async fromName(provider: AbstractProvider, name: string): Promise<null | EnsResolver> {
657+
static async fromName(provider: AbstractProvider, name: string, preferUniversal?: boolean): Promise<null | EnsResolver> {
658+
659+
// We have a Universal Resolver, use it
660+
const universal = await getUniversal(provider);
661+
if (universal) {
662+
if (preferUniversal) {
663+
return new EnsResolver(provider, <string>universal.target, name, true);
664+
}
665+
666+
const result = await universal.findResolver(dnsEncode(name));
667+
return new EnsResolver(provider, result.resolver, name);
668+
}
580669

581670
let currentName = name;
582671
while (true) {
@@ -604,3 +693,23 @@ export class EnsResolver {
604693
}
605694
}
606695
}
696+
697+
function createUniversal(provider: AbstractProvider, address: string): Contract {
698+
return new Contract(address, [
699+
"function findResolver(bytes) view returns (address resolver, bytes32 node, uint offset)",
700+
"function resolve(bytes name, bytes data) view returns (bytes result, address resolver)",
701+
"function reverse(bytes name, uint coinType) view returns (string primary, address resolver, address reverseResolver)",
702+
"error HttpError(uint16 statusCode, string statusMessage)"
703+
], provider);
704+
}
705+
706+
async function getUniversal(provider: AbstractProvider): Promise<null | Contract> {
707+
const network = await provider.getNetwork();
708+
709+
const ensPlugin = network.getPlugin<EnsPlugin>("org.ethers.plugins.network.Ens");
710+
if (ensPlugin && ensPlugin.universalResolver) {
711+
return createUniversal(provider, ensPlugin.universalResolver);
712+
}
713+
714+
return null;
715+
}

src.ts/providers/network.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import type { TransactionLike } from "../transaction/index.js";
1818
import type { NetworkPlugin } from "./plugins-network.js";
1919

2020

21+
const inspect = Symbol.for("nodejs.util.inspect.custom");
22+
2123
/**
2224
* A Networkish can be used to allude to a Network, by specifing:
2325
* - a [[Network]] object
@@ -31,6 +33,7 @@ export type Networkish = Network | number | bigint | string | {
3133
//layerOneConnection?: Provider,
3234
ensAddress?: string,
3335
ensNetwork?: number
36+
ensUniversalResolver?: string;
3437
};
3538

3639

@@ -77,6 +80,16 @@ export class Network {
7780
this.#plugins = new Map();
7881
}
7982

83+
[inspect](): string { return this.toString(); }
84+
85+
toString(): string {
86+
const plugins: Array<string> = [ ];
87+
for (const plugin of this.#plugins.values()) {
88+
plugins.push(plugin.toString());
89+
}
90+
return `Network { name: ${ this.name }, chainId: ${ this.chainId }, plugins: [ ${ plugins.join(", ") } ] }`
91+
}
92+
8093
/**
8194
* Returns a JSON-compatible representation of a Network.
8295
*/
@@ -253,8 +266,9 @@ export class Network {
253266

254267
const custom = new Network(<string>(network.name), <number>(network.chainId));
255268

256-
if ((<any>network).ensAddress || (<any>network).ensNetwork != null) {
257-
custom.attachPlugin(new EnsPlugin((<any>network).ensAddress, (<any>network).ensNetwork));
269+
const n: any = network;
270+
if (n.ensAddress || n.ensNetwork != null || n.ensUniversalResolver) {
271+
custom.attachPlugin(new EnsPlugin(n.ensAddress, n.ensNetwork, n.ensUniversalResolver));
258272
}
259273

260274
//if ((<any>network).layerOneConnection) {
@@ -284,6 +298,7 @@ export class Network {
284298

285299
type Options = {
286300
ensNetwork?: number;
301+
ensUniversalResolver?: string;
287302
altNames?: Array<string>;
288303
plugins?: Array<NetworkPlugin>;
289304
};
@@ -359,7 +374,7 @@ function injectCommonNetworks(): void {
359374

360375
// We use 0 to disable ENS
361376
if (options.ensNetwork != null) {
362-
network.attachPlugin(new EnsPlugin(null, options.ensNetwork));
377+
network.attachPlugin(new EnsPlugin(null, options.ensNetwork, options.ensUniversalResolver));
363378
}
364379

365380
network.attachPlugin(new GasCostPlugin());
@@ -382,12 +397,19 @@ function injectCommonNetworks(): void {
382397
}
383398
}
384399

385-
registerEth("mainnet", 1, { ensNetwork: 1, altNames: [ "homestead" ] });
400+
// Proxy address
401+
const ensUniversalResolver = "0xeEeEEEeE14D718C2B47D9923Deab1335E144EeEe";
402+
403+
registerEth("mainnet", 1, {
404+
ensUniversalResolver, ensNetwork: 1, altNames: [ "homestead" ]
405+
});
386406
registerEth("ropsten", 3, { ensNetwork: 3 });
387407
registerEth("rinkeby", 4, { ensNetwork: 4 });
388408
registerEth("goerli", 5, { ensNetwork: 5 });
389409
registerEth("kovan", 42, { ensNetwork: 42 });
390-
registerEth("sepolia", 11155111, { ensNetwork: 11155111 });
410+
registerEth("sepolia", 11155111, {
411+
ensUniversalResolver, ensNetwork: 11155111
412+
});
391413
registerEth("holesky", 17000, { ensNetwork: 17000 });
392414

393415
registerEth("classic", 61, { });

0 commit comments

Comments
 (0)