Last active
January 11, 2025 19:25
-
-
Save 1forh/c268e1b0358b317fd10ac79e9dc6d99c to your computer and use it in GitHub Desktop.
Get an image of an NFT from the Algorand blockchain
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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