@infinixjoyd/metaplex-auth-service
Version:
A client library for nft.storage designed for metaplex NFT uploads
112 lines (111 loc) • 5.49 kB
JavaScript
import { File } from '../platform.js';
import { NFTStorage } from 'nft.storage';
import { ensureValidMetadata, } from '../metadata/index.js';
import { makeGatewayURL, makeIPFSURI } from '../utils.js';
import { Blockstore } from '../platform.js';
/**
* Encodes the given NFT metadata and asset files into CARs that can be uploaded to
* NFT.Storage.
*
* First, the `imageFile` and any `additionalAssetFiles` are packed into a CAR,
* and the root CID of this "asset CAR" is used to create IPFS URIs and gateway
* URLs for each file in the NFT bundle.
*
* The input metadata is then modified:
*
* - The `image` field is set to an HTTP gateway URL for the `imageFile`
* - If `animation_url` contains a filename that matches the `name` of any
* of the `additionalAssetFiles`, its value will be set to an HTTP gateway URL
* for that file.
* - If any entries in `properties.files` have a `uri` that matches the `name`
* of `imageFile` or any of the `additionalAssetFiles`, it will be replaced
* by _two_ entries in the output metadata. One will contain an `ipfs://` uri
* with `cdn == false`, and the other will have an HTTP gateway URL, with
* `cdn == true`.
*
* This updated metadata is then serialized and packed into a second car.
* Both CARs are returned in a {@link PackagedNFT} object, which also contains
* the updated metadata object and links to the metadata.
*
* Note that this function does NOT store anything with NFT.Storage. The links
* in the returned {@link PackagedNFT} will not resolve until the CARs have been
* uploaded. Use {@link NFTStorageMetaplexor.storePreparedNFT} to upload.
*
* @param metadata a JS object containing (hopefully) valid Metaplex NFT metadata
* @param imageFile a File object containing image data.
* @param [opts]
* @param opts.additionalAssetFiles any additional asset files (animations, higher resolution variants, etc)
* @param opts.blockstore blockstore to use when importing data. if not provided, a temporary blockstore will be created
* @param opts.validateSchema if true, validate the metadata against a JSON schema before processing. off by default
* @param opts.gatewayHost the hostname of an IPFS HTTP gateway to use in metadata links. Defaults to "nftstorage.link" if not set.
* @returns
*/
export async function prepareMetaplexNFT(metadata, imageFile, opts = {}) {
const metaplexMetadata = opts.validateSchema
? ensureValidMetadata(metadata)
: metadata;
const additionalAssetFiles = opts.additionalAssetFiles || [];
const blockstore = opts.blockstore || new Blockstore();
const assetFiles = [imageFile, ...additionalAssetFiles];
const encodedAssets = await NFTStorage.encodeDirectory(assetFiles, {
blockstore,
});
const imageFilename = imageFile.name || 'image.png';
const additionalFilenames = additionalAssetFiles.map((f) => f.name);
const linkedMetadata = replaceFileRefsWithIPFSLinks(metaplexMetadata, imageFilename, additionalFilenames, encodedAssets.cid.toString(), opts.gatewayHost);
const metadataFile = new File([JSON.stringify(linkedMetadata)], 'metadata.json');
const encodedMetadata = await NFTStorage.encodeDirectory([metadataFile], {
blockstore,
});
const metadataGatewayURL = makeGatewayURL(encodedMetadata.cid.toString(), 'metadata.json', opts.gatewayHost);
const metadataURI = makeIPFSURI(encodedMetadata.cid.toString(), 'metadata.json');
return {
metadata: linkedMetadata,
encodedMetadata,
encodedAssets,
metadataGatewayURL,
metadataURI,
blockstore,
};
}
function replaceFileRefsWithIPFSLinks(metadata, imageFilename, additionalFilenames, assetRootCID, gatewayHost) {
const imageGatewayURL = makeGatewayURL(assetRootCID, imageFilename, gatewayHost);
// since we may have skipped schema validation, we need to make sure properties.files exists
const properties = metadata.properties || {};
const originalFiles = properties.files || [];
// For each entry in properties.files, we check to see if the `uri` field matches the filename
// of any uploaded files. If so, we add two entries to the output `properties.files` array -
// one with a gateway URL with `cdn = true`, and one `ipfs://` uri with `cdn = false`.
// If the uri does not match the filename of any uploaded files, it is included as is.
const files = originalFiles.flatMap((f) => {
if (f.uri === imageFilename || additionalFilenames.includes(f.uri)) {
return [
{
...f,
uri: makeGatewayURL(assetRootCID, f.uri, gatewayHost),
cdn: true,
},
{
...f,
uri: makeIPFSURI(assetRootCID, f.uri),
cdn: false,
},
];
}
return [f];
});
// If animation_url matches a filename, replace with gateway url
let animation_url = metadata.animation_url;
if (animation_url && additionalFilenames.includes(animation_url)) {
animation_url = makeGatewayURL(assetRootCID, animation_url, gatewayHost);
}
return {
...metadata,
image: imageGatewayURL,
animation_url,
properties: {
...metadata.properties,
files,
},
};
}