@yoroi/resolver
Version:
Yoroi domain resolver
153 lines (131 loc) • 3.75 kB
text/typescript
import {Api, Resolver} from '@yoroi/types'
import {fetchData, FetchData, getApiError, isLeft} from '@yoroi/common'
import {z} from 'zod'
import {AxiosRequestConfig} from 'axios'
const initialDeps = {request: fetchData} as const
export const unstoppableApiGetCryptoAddress = (
{apiKey}: {apiKey: string},
{request}: {request: FetchData} = initialDeps,
) => {
return async (
resolve: Resolver.Receiver['resolve'],
fetcherConfig?: AxiosRequestConfig,
): Promise<string> => {
if (!resolve.includes('.')) throw new Resolver.Errors.InvalidDomain()
if (!isUnstoppableDomain(resolve))
throw new Resolver.Errors.UnsupportedTld()
const config = {
url: `${unstoppableApiConfig.mainnet.getCryptoAddress}${resolve}`,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
} as const
try {
const response = await request<UnstoppableApiGetCryptoAddressResponse>(
config,
fetcherConfig,
)
if (isLeft(response)) {
const error = response.error as UnstoppableApiGetCryptoAddressError
if (error.responseData?.message?.includes('Unsupported TLD'))
throw new Resolver.Errors.UnsupportedTld()
throw getApiError(error)
} else {
// parsing
const safeParsedAdaResponse = UnstoppableApiAdaResponseSchema.safeParse(
response.value.data,
)
const safeParsedGeneralResponse =
UnstoppableApiGeneralResponseSchema.safeParse(response.value.data)
// checking
const hasCardanoAddress = safeParsedAdaResponse.success
const hasOtherBlockchainAddress = safeParsedGeneralResponse.success
if (hasCardanoAddress)
return response.value.data.records['crypto.ADA.address']
if (hasOtherBlockchainAddress)
throw new Resolver.Errors.WrongBlockchain()
throw new Resolver.Errors.InvalidResponse()
}
} catch (error: unknown) {
throw getUnstoppableApiError(error)
}
}
}
// https://docs.unstoppabledomains.com/openapi/resolution/#operation/DomainsController.getDomain
export type UnstoppableApiGetCryptoAddressResponse = {
meta: {
domain: string
tokenId: string
namehash: string
blockchain: string
networkId: number
owner: string
resolver: string
registry: string
reverse: boolean
type: string
}
records: {
'crypto.ADA.address': string
}
}
export type UnstoppableApiGetCryptoAddressError = {
responseData: {
message: string
}
status: number
message: string
}
const UnstoppableApiAdaResponseSchema = z.object({
records: z.object({
'crypto.ADA.address': z.string(),
}),
})
const UnstoppableApiGeneralResponseSchema = z.object({
meta: z.object({
blockchain: z.string(),
}),
records: z.record(z.string(), z.string()),
})
// curl https://api.unstoppabledomains.com/resolve/supported_tlds
export const unstoppableSupportedTlds = [
'.x',
'.polygon',
'.nft',
'.crypto',
'.blockchain',
'.bitcoin',
'.dao',
'.888',
'.wallet',
'.binanceus',
'.hi',
'.klever',
'.kresus',
'.anime',
'.manga',
'.go',
'.altimist',
'.pudgy',
'.unstoppable',
'.austin',
'.bitget',
'.pog',
'.clay',
'.zil',
'.eth',
] as const
export const isUnstoppableDomain = (value: string) => {
return unstoppableSupportedTlds.some((tld) => value.endsWith(tld))
}
export const unstoppableApiConfig = {
mainnet: {
getCryptoAddress: 'https://api.unstoppabledomains.com/resolve/domains/',
},
} as const
export const getUnstoppableApiError = (error: unknown) => {
if (error instanceof Api.Errors.NotFound)
return new Resolver.Errors.NotFound()
return error
}