UNPKG

viem

Version:

TypeScript Interface for Ethereum

185 lines • 7.15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isImageUri = isImageUri; exports.getGateway = getGateway; exports.resolveAvatarUri = resolveAvatarUri; exports.getJsonImage = getJsonImage; exports.getMetadataAvatarUri = getMetadataAvatarUri; exports.parseAvatarUri = parseAvatarUri; exports.parseNftUri = parseNftUri; exports.getNftTokenUri = getNftTokenUri; const readContract_js_1 = require("../../../actions/public/readContract.js"); const ens_js_1 = require("../../../errors/ens.js"); const networkRegex = /(?<protocol>https?:\/\/[^\/]*|ipfs:\/|ipns:\/|ar:\/)?(?<root>\/)?(?<subpath>ipfs\/|ipns\/)?(?<target>[\w\-.]+)(?<subtarget>\/.*)?/; const ipfsHashRegex = /^(Qm[1-9A-HJ-NP-Za-km-z]{44,}|b[A-Za-z2-7]{58,}|B[A-Z2-7]{58,}|z[1-9A-HJ-NP-Za-km-z]{48,}|F[0-9A-F]{50,})(\/(?<target>[\w\-.]+))?(?<subtarget>\/.*)?$/; const base64Regex = /^data:([a-zA-Z\-/+]*);base64,([^"].*)/; const dataURIRegex = /^data:([a-zA-Z\-/+]*)?(;[a-zA-Z0-9].*?)?(,)/; async function isImageUri(uri) { try { const res = await fetch(uri, { method: 'HEAD' }); if (res.status === 200) { const contentType = res.headers.get('content-type'); return contentType?.startsWith('image/'); } return false; } catch (error) { if (typeof error === 'object' && typeof error.response !== 'undefined') { return false; } if (!globalThis.hasOwnProperty('Image')) return false; return new Promise((resolve) => { const img = new Image(); img.onload = () => { resolve(true); }; img.onerror = () => { resolve(false); }; img.src = uri; }); } } function getGateway(custom, defaultGateway) { if (!custom) return defaultGateway; if (custom.endsWith('/')) return custom.slice(0, -1); return custom; } function resolveAvatarUri({ uri, gatewayUrls, }) { const isEncoded = base64Regex.test(uri); if (isEncoded) return { uri, isOnChain: true, isEncoded }; const ipfsGateway = getGateway(gatewayUrls?.ipfs, 'https://ipfs.io'); const arweaveGateway = getGateway(gatewayUrls?.arweave, 'https://arweave.net'); const networkRegexMatch = uri.match(networkRegex); const { protocol, subpath, target, subtarget = '', } = networkRegexMatch?.groups || {}; const isIPNS = protocol === 'ipns:/' || subpath === 'ipns/'; const isIPFS = protocol === 'ipfs:/' || subpath === 'ipfs/' || ipfsHashRegex.test(uri); if (uri.startsWith('http') && !isIPNS && !isIPFS) { let replacedUri = uri; if (gatewayUrls?.arweave) replacedUri = uri.replace(/https:\/\/arweave.net/g, gatewayUrls?.arweave); return { uri: replacedUri, isOnChain: false, isEncoded: false }; } if ((isIPNS || isIPFS) && target) { return { uri: `${ipfsGateway}/${isIPNS ? 'ipns' : 'ipfs'}/${target}${subtarget}`, isOnChain: false, isEncoded: false, }; } if (protocol === 'ar:/' && target) { return { uri: `${arweaveGateway}/${target}${subtarget || ''}`, isOnChain: false, isEncoded: false, }; } let parsedUri = uri.replace(dataURIRegex, ''); if (parsedUri.startsWith('<svg')) { parsedUri = `data:image/svg+xml;base64,${btoa(parsedUri)}`; } if (parsedUri.startsWith('data:') || parsedUri.startsWith('{')) { return { uri: parsedUri, isOnChain: true, isEncoded: false, }; } throw new ens_js_1.EnsAvatarUriResolutionError({ uri }); } function getJsonImage(data) { if (typeof data !== 'object' || (!('image' in data) && !('image_url' in data) && !('image_data' in data))) { throw new ens_js_1.EnsAvatarInvalidMetadataError({ data }); } return data.image || data.image_url || data.image_data; } async function getMetadataAvatarUri({ gatewayUrls, uri, }) { try { const res = await fetch(uri).then((res) => res.json()); const image = await parseAvatarUri({ gatewayUrls, uri: getJsonImage(res), }); return image; } catch { throw new ens_js_1.EnsAvatarUriResolutionError({ uri }); } } async function parseAvatarUri({ gatewayUrls, uri, }) { const { uri: resolvedURI, isOnChain } = resolveAvatarUri({ uri, gatewayUrls }); if (isOnChain) return resolvedURI; const isImage = await isImageUri(resolvedURI); if (isImage) return resolvedURI; throw new ens_js_1.EnsAvatarUriResolutionError({ uri }); } function parseNftUri(uri_) { let uri = uri_; if (uri.startsWith('did:nft:')) { uri = uri.replace('did:nft:', '').replace(/_/g, '/'); } const [reference, asset_namespace, tokenID] = uri.split('/'); const [eip_namespace, chainID] = reference.split(':'); const [erc_namespace, contractAddress] = asset_namespace.split(':'); if (!eip_namespace || eip_namespace.toLowerCase() !== 'eip155') throw new ens_js_1.EnsAvatarInvalidNftUriError({ reason: 'Only EIP-155 supported' }); if (!chainID) throw new ens_js_1.EnsAvatarInvalidNftUriError({ reason: 'Chain ID not found' }); if (!contractAddress) throw new ens_js_1.EnsAvatarInvalidNftUriError({ reason: 'Contract address not found', }); if (!tokenID) throw new ens_js_1.EnsAvatarInvalidNftUriError({ reason: 'Token ID not found' }); if (!erc_namespace) throw new ens_js_1.EnsAvatarInvalidNftUriError({ reason: 'ERC namespace not found' }); return { chainID: Number.parseInt(chainID), namespace: erc_namespace.toLowerCase(), contractAddress: contractAddress, tokenID, }; } async function getNftTokenUri(client, { nft }) { if (nft.namespace === 'erc721') { return (0, readContract_js_1.readContract)(client, { address: nft.contractAddress, abi: [ { name: 'tokenURI', type: 'function', stateMutability: 'view', inputs: [{ name: 'tokenId', type: 'uint256' }], outputs: [{ name: '', type: 'string' }], }, ], functionName: 'tokenURI', args: [BigInt(nft.tokenID)], }); } if (nft.namespace === 'erc1155') { return (0, readContract_js_1.readContract)(client, { address: nft.contractAddress, abi: [ { name: 'uri', type: 'function', stateMutability: 'view', inputs: [{ name: '_id', type: 'uint256' }], outputs: [{ name: '', type: 'string' }], }, ], functionName: 'uri', args: [BigInt(nft.tokenID)], }); } throw new ens_js_1.EnsAvatarUnsupportedNamespaceError({ namespace: nft.namespace }); } //# sourceMappingURL=utils.js.map