diff --git a/src/Resolution.ts b/src/Resolution.ts index bff70f90..1cab9e8b 100644 --- a/src/Resolution.ts +++ b/src/Resolution.ts @@ -23,7 +23,6 @@ import { ReverseResolutionOptions, UnsLocation, BlockchainType, - UnsBlockchainsSource, } from './types/publicTypes'; import ResolutionError, {ResolutionErrorCode} from './errors/resolutionError'; import DnsUtils from './utils/DnsUtils'; @@ -69,7 +68,29 @@ export default class Resolution { /** * @internal */ - readonly serviceMap: Record; + readonly serviceMap: { + [NamingServiceName.UNS]: ServicesEntry; + [NamingServiceName.ZNS]: ServicesEntry; + [NamingServiceName.ENS]: ServicesEntry; + [NamingServiceName.UNS_BASE]?: ServicesEntry; + }; + + private getService(serviceName: NamingServiceName): ServicesEntry { + const entry = this.serviceMap[serviceName]; + if (!entry) { + throw new ConfigurationError( + ConfigurationErrorCode.NetworkConfigMissing, + { + method: serviceName, + config: + serviceName === NamingServiceName.UNS_BASE + ? `sourceConfig.uns.base` + : `sourceConfig.${serviceName.toLowerCase()}`, + }, + ); + } + return entry; + } constructor(config: {sourceConfig?: SourceConfig; apiKey?: string} = {}) { const uns = this.getUnsConfig(config); @@ -89,10 +110,6 @@ export default class Resolution { usedServices: [uns], native: uns instanceof Uns ? uns : new Uns(), }, - [NamingServiceName.UNS_BASE]: { - usedServices: [unsBase], - native: unsBase instanceof Uns ? unsBase : new Uns(), - }, [NamingServiceName.ZNS]: { usedServices: equalUdApiProviders ? [uns] : [uns, zns], native: zns instanceof Zns ? zns : new Zns(), @@ -102,6 +119,13 @@ export default class Resolution { native: ens instanceof Ens ? ens : new Ens(), }, }; + + if (unsBase) { + this.serviceMap[NamingServiceName.UNS_BASE] = { + usedServices: [unsBase], + native: unsBase instanceof Uns ? unsBase : new Uns(), + }; + } } /** @@ -795,7 +819,7 @@ export default class Resolution { namingService: NamingServiceName, options: NamehashOptions = NamehashOptionsDefault, ): string { - const service = this.serviceMap[namingService]; + const service = this.getService(namingService); if (!service) { throw new ResolutionError(ResolutionErrorCode.UnsupportedService, { namingService, @@ -819,7 +843,7 @@ export default class Resolution { namingService: NamingServiceName, options: NamehashOptions = NamehashOptionsDefault, ): string { - const service = this.serviceMap[namingService]; + const service = this.getService(namingService); if (!service) { throw new ResolutionError(ResolutionErrorCode.UnsupportedService, { namingService, @@ -851,7 +875,7 @@ export default class Resolution { hash: string, namingService: NamingServiceName, ): boolean { - const service = this.serviceMap[namingService]; + const service = this.getService(namingService); if (!service) { throw new ResolutionError(ResolutionErrorCode.UnsupportedService, { namingService, @@ -954,7 +978,7 @@ export default class Resolution { */ async unhash(hash: string, service: NamingServiceName): Promise { hash = fromDecStringToHex(hash); - const services = this.serviceMap[service].usedServices; + const services = this.getService(service).usedServices; // UNS is the only service and ZNS is the one with the lowest priority. // We don't want to access the `native` service, as a user may want to call `UdApi`. const method = services[services.length - 1]; @@ -980,12 +1004,13 @@ export default class Resolution { // But if there are no .zil domains with absent UNS locations (i.e. all the requested .zil domains have been // migrated to UNS), the ZNS call result will be ignored and an error, if there's one, won't be thrown. - const unsPromise = - this.serviceMap.UNS.usedServices[0].locations(nonEnsDomains); + const unsPromise = this.getService( + NamingServiceName.UNS, + ).usedServices[0].locations(nonEnsDomains); // Fetch UNS locations first. If we see that there are no .zil domains with absent locations, we can return early. const unsLocations = await unsPromise; if (zilDomains.length) { - const znsServices = this.serviceMap.ZNS.usedServices; + const znsServices = this.getService(NamingServiceName.ZNS).usedServices; // The actual ZNS service is the last one in the array. const znsService = znsServices[znsServices.length - 1]; const znsPromise = wrapResult(() => znsService.locations(zilDomains)); @@ -1002,9 +1027,9 @@ export default class Resolution { } if (ensDomains.length) { - const ensLocations = await this.serviceMap.ENS.usedServices[0].locations( - ensDomains, - ); + const ensLocations = await this.getService( + NamingServiceName.ENS, + ).usedServices[0].locations(ensDomains); for (const ensDomain in ensLocations) { unsLocations[ensDomain] = ensLocations[ensDomain]; } @@ -1027,7 +1052,7 @@ export default class Resolution { return tokenId; } - const ensService = this.serviceMap['ENS'].native; + const ensService = this.getService(NamingServiceName.ENS).native; const ensDomainName = await ensService.reverseOf(address); if (ensDomainName) { const ensNameHash = ensService.namehash(ensDomainName); @@ -1052,7 +1077,7 @@ export default class Resolution { return this.unhash(tokenId as string, NamingServiceName.UNS); } - const ensService = this.serviceMap['ENS'].native; + const ensService = this.getService(NamingServiceName.ENS).native; const ensDomainName = await ensService.reverseOf(address); if (ensDomainName) { return ensDomainName; @@ -1111,7 +1136,7 @@ export default class Resolution { }); } - const servicePromises = this.serviceMap[serviceName].usedServices.map( + const servicePromises = this.getService(serviceName).usedServices.map( (service) => wrapResult(() => func(service)), ); @@ -1153,7 +1178,7 @@ export default class Resolution { }); } - const servicePromises = this.serviceMap[serviceName].usedServices.map( + const servicePromises = this.getService(serviceName).usedServices.map( (service) => wrapResult(() => func(service)), ); @@ -1183,11 +1208,11 @@ export default class Resolution { ): Promise { let tokenId: string | null = null; - const unsService = this.serviceMap['UNS'].native; + const unsService = this.getService(NamingServiceName.UNS).native; tokenId = await unsService.reverseOf(address, location); if (!tokenId) { - const baseUnsService = this.serviceMap['UNS_BASE'].native; + const baseUnsService = this.getService(NamingServiceName.UNS_BASE).native; tokenId = await baseUnsService.reverseOf(address, location); } @@ -1215,19 +1240,17 @@ export default class Resolution { } const unsSource = config.sourceConfig?.uns; - if (isBlockchainsConfig(unsSource)) { - return new Uns({ - locations: { - Layer1: unsSource.blockchains.eth, - Layer2: unsSource.blockchains.pol, - }, - }); - } - - return isApi(unsSource) ? new UdApi(unsSource) : new Uns(unsSource); + return isApi(unsSource) + ? new UdApi(unsSource) + : new Uns({ + locations: { + Layer1: unsSource?.locations.eth || unsSource?.locations.Layer1, + Layer2: unsSource?.locations.pol || unsSource?.locations.Layer2, + }, + }); } - private getUnsBaseConfig(config: ResolutionConfig): Uns | UdApi { + private getUnsBaseConfig(config: ResolutionConfig): Uns | UdApi | undefined { if (config.apiKey) { return new Uns({ locations: { @@ -1248,16 +1271,16 @@ export default class Resolution { } const unsSource = config.sourceConfig?.uns; - if (isBlockchainsConfig(unsSource)) { - return new Uns({ - locations: { - Layer1: unsSource.blockchains.eth, - Layer2: unsSource.blockchains.base, - }, - }); - } - - return isApi(unsSource) ? new UdApi(unsSource) : new Uns(unsSource); + return isApi(unsSource) + ? new UdApi(unsSource) + : unsSource?.locations.base + ? new Uns({ + locations: { + Layer1: unsSource?.locations.eth || unsSource?.locations.Layer1, + Layer2: unsSource?.locations.base, + }, + }) + : undefined; } private getZnsConfig(config: ResolutionConfig): Zns | UdApi { @@ -1296,7 +1319,3 @@ function isApi(obj: any): obj is Api { typeof obj.api === 'boolean' ); } - -function isBlockchainsConfig(obj: any): obj is UnsBlockchainsSource { - return typeof obj === 'object' && obj !== null && 'blockchains' in obj; -} diff --git a/src/Uns.ts b/src/Uns.ts index ddad785f..92504d70 100644 --- a/src/Uns.ts +++ b/src/Uns.ts @@ -17,6 +17,8 @@ import { Locations, BlockchainType, DomainMetadata, + UnsInternalSource, + UnsLayerSource, } from './types/publicTypes'; import {isValidTwitterSignature} from './utils/TwitterSignatureValidator'; import FetchProvider from './FetchProvider'; @@ -33,7 +35,16 @@ import UnsInternal from './UnsInternal'; import Networking from './utils/Networking'; import SupportedKeys from './config/resolver-keys.json'; -const ensureValidSourceConfig = (source: UnsSource): void => { +// ensures that we either have a valid source config or no source config at all +const ensureValidSourceConfig = ( + source?: UnsInternalSource, +): source is + | {locations: {Layer1: UnsLayerSource; Layer2: UnsLayerSource}} + | undefined => { + if (!source) { + return true; + } + if ( !source.locations || !source.locations.Layer1 || @@ -61,7 +72,7 @@ const ensureValidSourceConfig = (source: UnsSource): void => { }); } - return; + return true; }; /** @@ -72,35 +83,36 @@ export default class Uns extends NamingService { public unsl2: UnsInternal; readonly name: NamingServiceName = NamingServiceName.UNS; - constructor(source?: UnsSource) { + constructor(source?: UnsInternalSource) { super(); - if (source) { - ensureValidSourceConfig(source); - } else { - source = { + // ensures that we either have a valid source config or no source config at all + if (ensureValidSourceConfig(source)) { + // substitute default locations if we didn't get a source config + const {locations} = source || { locations: { Layer1: { url: '', network: 'mainnet', - }, + } as UnsLayerSource, Layer2: { url: '', network: 'polygon-mainnet', - }, + } as UnsLayerSource, }, }; - } - this.unsl1 = new UnsInternal( - UnsLocation.Layer1, - source.locations.Layer1, - source.locations.Layer1.blockchain ?? BlockchainType.ETH, - ); - this.unsl2 = new UnsInternal( - UnsLocation.Layer2, - source.locations.Layer2, - source.locations.Layer2.blockchain ?? BlockchainType.POL, - ); + this.unsl1 = new UnsInternal( + UnsLocation.Layer1, + locations.Layer1, + locations.Layer1.blockchain ?? BlockchainType.ETH, + ); + + this.unsl2 = new UnsInternal( + UnsLocation.Layer2, + locations.Layer2, + locations.Layer2.blockchain ?? BlockchainType.POL, + ); + } } static async autoNetwork(config: { diff --git a/src/types/publicTypes.ts b/src/types/publicTypes.ts index 359a8053..1498da40 100644 --- a/src/types/publicTypes.ts +++ b/src/types/publicTypes.ts @@ -16,12 +16,21 @@ export type UnsLayerSource = NamingServiceSource & { blockchain?: BlockchainType; }; -export type UnsSource = { - locations: {Layer1: UnsLayerSource; Layer2: UnsLayerSource}; +export type UnsInternalSource = { + locations: { + Layer1?: UnsLayerSource; + Layer2?: UnsLayerSource; + }; }; -export type UnsBlockchainsSource = { - blockchains: {eth: UnsLayerSource; pol: UnsLayerSource; base: UnsLayerSource}; +export type UnsSource = { + locations: { + Layer1?: UnsLayerSource; + Layer2?: UnsLayerSource; + eth?: UnsLayerSource; + pol?: UnsLayerSource; + base?: UnsLayerSource; + }; }; export type EnsSource = NamingServiceSource & { @@ -36,7 +45,7 @@ export type ZnsSource = NamingServiceSource & { }; export type SourceConfig = { - uns?: UnsSource | UnsBlockchainsSource | Api; + uns?: UnsSource | Api; zns?: ZnsSource | Api; ens?: EnsSource | Api; };