UNPKG

@authereum/resolution

Version:
140 lines (122 loc) 3.85 kB
import { keccak_256 as sha3 } from 'js-sha3'; import NamingService from './NamingService'; import ResolutionError, { ResolutionErrorCode } from './errors/resolutionError'; import { BlockhanNetworkUrlMap, isNullAddress, NetworkIdMap, nodeHash, } from './types'; import { invert } from './utils'; import Contract from './utils/contract'; import { NamingServiceName, SourceDefinition, ResolutionMethod } from './publicTypes'; export abstract class EthereumNamingService extends NamingService { readonly name: NamingServiceName; protected readerContract: Contract; static readonly UrlMap: BlockhanNetworkUrlMap = { 1: 'https://main-rpc.linkpool.io', 3: 'https://ropsten-rpc.linkpool.io', }; static readonly NetworkNameMap = { mainnet: 1, ropsten: 3, rinkeby: 4, goerli: 5, kovan: 42, }; static readonly NetworkIdMap: NetworkIdMap = invert( EthereumNamingService.NetworkNameMap, ); constructor(source: SourceDefinition = {}, name: ResolutionMethod) { super(source, name); if (this.registryAddress) { this.readerContract = this.buildContract( this.readerAbi(), this.registryAddress, ); } } protected abstract defaultRegistry(network: number): string | undefined; protected abstract readerAbi(): any; private networkFromUrl(url: string | undefined): string | undefined { if (!url) { return undefined; } for (const key in EthereumNamingService.NetworkNameMap) { // eslint-disable-next-line no-prototype-builtins if (!EthereumNamingService.NetworkNameMap.hasOwnProperty(key)) { continue; } if (url.indexOf(key) >= 0) { return EthereumNamingService.NetworkNameMap[key]; } } return undefined; } protected normalizeSource(source: SourceDefinition): SourceDefinition { source = { ...source }; source.network = typeof source.network == 'string' ? EthereumNamingService.NetworkNameMap[source.network] : source.network || this.networkFromUrl(source.url) || 1; if (!source.provider && !source.url) { source.url = source.network ? EthereumNamingService.UrlMap[source.network] : undefined; } source.registry = source.registry ? source.registry : this.defaultRegistry(source.network as number); return source; } isSupportedNetwork(): boolean { return this.registryAddress != null; } childhash( parent: nodeHash, label: string, ): nodeHash { parent = parent.replace(/^0x/, ''); const childHash = sha3(label); // eslint-disable-next-line no-undef return sha3(Buffer.from(parent + childHash, 'hex')); } protected async callMethod( contract: Contract, method: string, params: (string | string[])[], ): Promise<any> { try { const result = await contract.call(method, params); return result[0]; } catch (error) { const { message }: { message: string } = error; if ( message.match(/Invalid JSON RPC response/) || message.match(/legacy access request rate exceeded/) ) { throw new ResolutionError(ResolutionErrorCode.NamingServiceDown, { method: this.name, }); } throw error; } } protected buildContract(abi: any, address: string): Contract { return new Contract(abi, address, this.provider); } protected async throwOwnershipError( domain: string, ownerPromise?: Promise<string | null>, ): Promise<void> { const owner = ownerPromise ? await ownerPromise : await this.owner(domain); if (isNullAddress(owner)) { throw new ResolutionError(ResolutionErrorCode.UnregisteredDomain, { domain, }); } throw new ResolutionError(ResolutionErrorCode.UnspecifiedResolver, { domain, }); } }