viem
Version:
185 lines • 7.15 kB
JavaScript
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
;