Skip to content

Instantly share code, notes, and snippets.

@1forh
Last active January 11, 2025 19:25
Show Gist options
  • Save 1forh/c268e1b0358b317fd10ac79e9dc6d99c to your computer and use it in GitHub Desktop.
Save 1forh/c268e1b0358b317fd10ac79e9dc6d99c to your computer and use it in GitHub Desktop.
Get an image of an NFT from the Algorand blockchain
import axios from 'axios';
import * as mfsha2 from 'multiformats/hashes/sha2';
import * as digest from 'multiformats/hashes/digest';
import { CID } from 'multiformats/cid';
const REGEX = 'template-ipfs://{ipfscid:(?<version>[01]):(?<codec>[a-z0-9-]+):(?<field>[a-z0-9-]+):(?<hash>[a-z0-9-]+)}';
const parseCID = (url, reserveAddress) => {
var matches = url.match(REGEX);
if (!matches) {
if (url.startsWith('template-ipfs://')) throw 'unsupported template-ipfs spec';
return url;
}
const version = parseInt(matches.groups.version);
const codec = matches.groups.codec;
const field = matches.groups.field;
const hash = matches.groups.hash;
if (field != 'reserve') throw "unsupported ipfscid field '" + field + "', only reserve is currently supported";
var codecId;
switch (codec) {
case 'raw':
codecId = 0x55;
break;
case 'dag-pb':
codecId = 0x70;
break;
default:
throw "unknown multicodec type '" + codec + "' in ipfscid spec";
}
const address = algosdk.decodeAddress(reserveAddress);
var mhdigest;
switch (hash) {
case 'sha2-256':
mhdigest = digest.create(mfsha2.sha256.code, address.publicKey);
break;
default:
throw "unknown hash type '" + hash + "' in ipfscid spec";
}
if (version == 0) {
if (codec != 'dag-pb' || hash != 'sha2-256') throw 'cid v0 must always be dag-pb and sha2-256 codec/hash type';
return CID.createV0(mhdigest);
} else {
return CID.createV1(codecId, mhdigest);
}
}
const getAsset = async (assetId) => {
if (!assetId) return null;
const apiUrl = `https://mainnet-idx.algonode.cloud/v2/assets/${assetId}`;
const { data } = await axios.get(apiUrl);
return data?.asset;
};
const getIpfsImageUrl = (hash, { width, quality } = { width: 1000, quality: 70 }) => {
return `https://ipfs.algonode.dev/ipfs/${hash}?optimizer=image&width=${width}&quality=${quality}`;
};
export const getNftImage = async (nft) => {
if (!nft) return '';
const assetDetails = await getAsset(nft.id || nft.index || nft.assetId || nft.asa_id);
if (!assetDetails.params || !assetDetails.params.url) return '';
if (assetDetails.params.url.includes('https://gateway.pinata.cloud/ipfs/')) {
return assetDetails.params.url;
}
if (assetDetails.params.url.includes('https://ipfs.infura.io/ipfs')) {
const ipfs = assetDetails.params.url.split('.io/ipfs')[1];
return getIpfsImageUrl(ipfs);
}
if (assetDetails.params.url.includes('ipfs://') && !assetDetails.params.url.includes('template-ipfs')) {
return getIpfsImageUrl(assetDetails.params.url.split('ipfs://')[1]);
}
if (
assetDetails.params.url.includes('template-ipfs://{ipfscid:1:raw:reserve:sha2-256}') ||
assetDetails.params.url.includes('template-ipfs://{ipfscid:0:dag-pb:reserve:sha2-256}')
) {
const reserve = assetDetails.params.reserve;
const cid = parseCID(assetDetails.params.url, reserve);
const ipfs = cid.toString();
try {
const resp = await fetch(getIpfsImageUrl(ipfs));
if (resp.ok) {
const json = await resp.json();
return getIpfsImageUrl(json.image.split('ipfs://')[1]);
}
} catch (error) {}
return getIpfsImageUrl(ipfs);
} else {
const ipfs = nft.params.url.split('ipfs://')[1];
return getIpfsImageUrl(ipfs);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment