import { AvatarResolver } from '@ensdomains/ens-avatar'; import { BaseProvider } from '@ethersproject/providers'; import { strict as assert } from 'assert'; import { JSDOM } from 'jsdom'; import fetch from 'node-fetch'; import { ResolverNotFound, RetrieveURIFailed, TextRecordNotFound, } from '../base'; import { IPFS_GATEWAY } from '../config'; const window = new JSDOM('').window; interface HostMeta { chain_id? : number; namespace? : string; contract_address?: string; token_id? : string; reference_url? : string; } export interface AvatarMetadata { uri : string; animation : string; animation_details: {}; attributes : any[]; created_by : string; event : string; image_data : string; image_url : string; image_details : string; name : string; description? : string; external_link? : string; image? : string; animation_url? : string; hostType : string; host_meta : HostMeta; is_owner : boolean; } export class AvatarMetadata { defaultProvider: any; avtResolver: AvatarResolver; constructor(provider: any, uri: string) { this.defaultProvider = provider; this.avtResolver = new AvatarResolver(provider, { ipfs: IPFS_GATEWAY }); this.uri = uri; } async getImage() { let avatarURI; try { avatarURI = await this.avtResolver.getAvatar(this.uri, { jsdomWindow: window, }); } catch (error: any) { if (error instanceof Error) { console.log(`${this.uri} - error:`, error.message); } if (typeof error === 'string') { console.log(`${this.uri} - error:`, error); } if (error === 'Image is not available') { throw new RetrieveURIFailed(error, 404); } } if (!avatarURI) { throw new TextRecordNotFound( 'There is no avatar set under given address', 404 ); } if (avatarURI?.startsWith('http')) { const response = await fetch(avatarURI); assert(response, 'Response is empty'); const mimeType = response?.headers.get('Content-Type'); const data = await response?.buffer(); return [data, mimeType]; } if (avatarURI?.startsWith('data:')) { // base64 image const mimeType = avatarURI.match(/[^:]\w+\/[\w-+\d.]+(?=;|,)/); const base64data = avatarURI.split('base64,')[1]; assert(base64data, 'base64 format is incorrect: empty data'); assert(mimeType, 'base64 format is incorrect: no mimetype'); const data = Buffer.from(base64data, 'base64'); return [data, mimeType[0]]; } throw new RetrieveURIFailed( 'Unknown type/protocol given for the image source.', 400 ); } async getMeta(networkName: string) { let metadata: any; try { metadata = await this.avtResolver.getMetadata(this.uri); } catch (error: any) { if (error instanceof Error) { console.log(`${this.uri} - error:`, error.message); } if (typeof error === 'string') { console.log(`${this.uri} - error:`, error); } throw new ResolverNotFound( 'There is no resolver set under given address', 404 ); } if (!metadata) { throw new TextRecordNotFound( 'There is no avatar set under given address', 404 ); } if (!metadata.image) { if (metadata.image_url) { metadata.image = metadata.image_url; } else if (metadata.image_data) { metadata.image = `https://metadata.ens.domains/${networkName}/avatar/${this.uri}`; } else { throw new TextRecordNotFound( 'There is no avatar set under given address', 404 ); } } return metadata; } } export async function getAvatarMeta( provider: BaseProvider, name: string, networkName: string ): Promise<any> { const avatar = new AvatarMetadata(provider, name); return await avatar.getMeta(networkName); } export async function getAvatarImage( provider: BaseProvider, name: string ): Promise<any> { const avatar = new AvatarMetadata(provider, name); return await avatar.getImage(); }