/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see .
*/
import { ResolverMethodMissingError } from 'web3-errors';
import { Contract } from 'web3-eth-contract';
import { isNullish, sha3 } from 'web3-utils';
import { isHexStrict } from 'web3-validator';
import { Address, PayableCallOptions } from 'web3-types';
import { PublicResolverAbi } from './abi/ens/PublicResolver.js';
import { interfaceIds, methodsInInterface } from './config.js';
import { Registry } from './registry.js';
import { namehash } from './utils.js';
// Default public resolver
// https://github.com/ensdomains/resolvers/blob/master/contracts/PublicResolver.sol
export class Resolver {
private readonly registry: Registry;
public constructor(registry: Registry) {
this.registry = registry;
}
private async getResolverContractAdapter(ENSName: string) {
// TODO : (Future 4.1.0 TDB) cache resolver contract if frequently queried same ENS name, refresh cache based on TTL and usage, also limit cache size, optional cache with a flag
return this.registry.getResolver(ENSName);
}
// https://eips.ethereum.org/EIPS/eip-165
// eslint-disable-next-line class-methods-use-this
public async checkInterfaceSupport(
resolverContract: Contract,
methodName: string,
) {
if (isNullish(interfaceIds[methodName]))
throw new ResolverMethodMissingError(
resolverContract.options.address ?? '',
methodName,
);
const supported = await resolverContract.methods
.supportsInterface(interfaceIds[methodName])
.call();
if (!supported)
throw new ResolverMethodMissingError(
resolverContract.options.address ?? '',
methodName,
);
}
public async supportsInterface(ENSName: string, interfaceId: string) {
const resolverContract = await this.getResolverContractAdapter(ENSName);
let interfaceIdParam = interfaceId;
if (!isHexStrict(interfaceIdParam)) {
interfaceIdParam = sha3(interfaceId) ?? '';
if (interfaceId === '') throw new Error('Invalid interface Id');
interfaceIdParam = interfaceIdParam.slice(0, 10);
}
return resolverContract.methods.supportsInterface(interfaceIdParam).call();
}
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
public async getAddress(ENSName: string, coinType: number = 60) {
const resolverContract = await this.getResolverContractAdapter(ENSName);
await this.checkInterfaceSupport(resolverContract, methodsInInterface.addr);
return resolverContract.methods.addr(namehash(ENSName), coinType).call();
}
public async getPubkey(ENSName: string) {
const resolverContract = await this.getResolverContractAdapter(ENSName);
await this.checkInterfaceSupport(resolverContract, methodsInInterface.pubkey);
return resolverContract.methods.pubkey(namehash(ENSName)).call();
}
public async getContenthash(ENSName: string) {
const resolverContract = await this.getResolverContractAdapter(ENSName);
await this.checkInterfaceSupport(resolverContract, methodsInInterface.contenthash);
return resolverContract.methods.contenthash(namehash(ENSName)).call();
}
public async setAddress(
ENSName: string,
address: Address,
txConfig: PayableCallOptions,
) {
const resolverContract = await this.getResolverContractAdapter(ENSName);
await this.checkInterfaceSupport(resolverContract, methodsInInterface.setAddr);
return resolverContract.methods
.setAddr(namehash(ENSName), address)
.send(txConfig);
}
public async getText(
ENSName: string,
key: string,
) {
const resolverContract = await this.getResolverContractAdapter(ENSName);
await this.checkInterfaceSupport(resolverContract, methodsInInterface.text);
return resolverContract.methods
.text(namehash(ENSName), key).call()
}
public async getName(
address: string
) {
const resolverContract = await this.getResolverContractAdapter(address);
await this.checkInterfaceSupport(resolverContract, methodsInInterface.name);
return resolverContract.methods
.name(namehash(address)).call()
}
}