/* eslint-disable eslint-comments/disable-enable-pair */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable func-names */ import clsx from 'clsx' import { Alert } from 'components/Alert' import Anchor from 'components/Anchor' import Button from 'components/Button' import { CollectionInfo } from 'components/CollectionInfo' import { Conditional } from 'components/Conditional' import { StyledInput } from 'components/forms/StyledInput' import { MetadataModal } from 'components/MetadataModal' import { setBaseTokenUri, setImage, useCollectionStore } from 'contexts/collection' import type { NextPage } from 'next' import { NextSeo } from 'next-seo' import type { ChangeEvent } from 'react' import { useState } from 'react' import { toast } from 'react-hot-toast' import type { UploadServiceType } from 'services/upload' import { upload } from 'services/upload' import { withMetadata } from 'utils/layout' import { links } from 'utils/links' import { naturalCompare } from 'utils/sort' import { getAssetType } from '../../utils/getAssetType' type UploadMethod = 'new' | 'existing' const UploadPage: NextPage = () => { const baseTokenURI = useCollectionStore().base_token_uri const [assetFilesArray, setAssetFilesArray] = useState([]) const [metadataFilesArray, setMetadataFilesArray] = useState([]) const [updatedMetadataFilesArray, setUpdatedMetadataFilesArray] = useState([]) const [uploadMethod, setUploadMethod] = useState('new') const [uploadService, setUploadService] = useState('nft-storage') const [metadataFileArrayIndex, setMetadataFileArrayIndex] = useState(0) const [refreshMetadata, setRefreshMetadata] = useState(false) const [nftStorageApiKey, setNftStorageApiKey] = useState( 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6ZXRocjoweDJBODk5OGI4ZkE2YTM1NzMyYmMxQTRDQzNhOUU2M0Y2NUM3ZjA1RWIiLCJpc3MiOiJuZnQtc3RvcmFnZSIsImlhdCI6MTY1NTE5MTcwNDQ2MiwibmFtZSI6IlRlc3QifQ.IbdV_26bkPHSdd81sxox5AoG-5a4CCEY4aCrdbCXwAE', ) const [pinataApiKey, setPinataApiKey] = useState('c8c2ea440c09ee8fa639') const [pinataSecretKey, setPinataSecretKey] = useState( '9d6f42dc01eaab15f52eac8f36cc4f0ee4184944cb3cdbcda229d06ecf877ee7', ) const handleChangeBaseTokenUri = (event: { target: { value: React.SetStateAction } }) => { setBaseTokenUri(event.target.value.toString()) } const handleChangeImage = (event: { target: { value: React.SetStateAction } }) => { setImage(event.target.value.toString()) } const selectAssets = (event: ChangeEvent) => { setAssetFilesArray([]) console.log(event.target.files) let reader: FileReader if (event.target.files === null) return for (let i = 0; i < event.target.files.length; i++) { reader = new FileReader() reader.onload = function (e) { if (!e.target?.result) return toast.error('Error parsing file.') if (!event.target.files) return toast.error('No files selected.') const assetFile = new File([e.target.result], event.target.files[i].name, { type: 'image/jpg' }) setAssetFilesArray((prev) => [...prev, assetFile]) } if (!event.target.files) return toast.error('No file selected.') reader.readAsArrayBuffer(event.target.files[i]) reader.onloadend = function (e) { setAssetFilesArray((prev) => prev.sort((a, b) => naturalCompare(a.name, b.name))) } } } const selectMetadata = (event: ChangeEvent) => { setMetadataFilesArray([]) setUpdatedMetadataFilesArray([]) console.log(assetFilesArray) console.log(event.target.files) let reader: FileReader if (event.target.files === null) return toast.error('No files selected.') for (let i = 0; i < event.target.files.length; i++) { reader = new FileReader() reader.onload = async function (e) { if (!e.target?.result) return toast.error('Error parsing file.') if (!event.target.files) return toast.error('No files selected.') if (!JSON.parse(await event.target.files[i].text()).attributes) return toast.error(`The file with name '${event.target.files[i].name}' doesn't have an attributes list!`) const metadataFile = new File([e.target.result], event.target.files[i].name, { type: 'application/json' }) setMetadataFilesArray((prev) => [...prev, metadataFile]) } if (!event.target.files) return toast.error('No file selected.') reader.readAsText(event.target.files[i], 'utf8') reader.onloadend = function (e) { setMetadataFilesArray((prev) => prev.sort((a, b) => naturalCompare(a.name, b.name))) console.log(metadataFilesArray) } } } const updateMetadata = async () => { const metadataFileNames = metadataFilesArray.map((file) => file.name) console.log(metadataFileNames) const assetFileNames = assetFilesArray.map((file) => file.name.substring(0, file.name.lastIndexOf('.'))) console.log(assetFileNames) //compare the two arrays to make sure they are the same const areArraysEqual = metadataFileNames.every((val, index) => val === assetFileNames[index]) if (!areArraysEqual) { return toast.error('Asset and metadata file names do not match.') } console.log(assetFilesArray) const assetURI = await upload( assetFilesArray, uploadService, 'assets', nftStorageApiKey, pinataApiKey, pinataSecretKey, ) console.log(assetURI) setUpdatedMetadataFilesArray([]) let reader: FileReader for (let i = 0; i < metadataFilesArray.length; i++) { reader = new FileReader() reader.onload = function (e) { const metadataJSON = JSON.parse(e.target?.result as string) metadataJSON.image = `ipfs://${assetURI}/${assetFilesArray[i].name}` const metadataFileBlob = new Blob([JSON.stringify(metadataJSON)], { type: 'application/json', }) const updatedMetadataFile = new File([metadataFileBlob], metadataFilesArray[i].name, { type: 'application/json', }) updatedMetadataFilesArray.push(updatedMetadataFile) console.log(`${updatedMetadataFile.name} => ${metadataJSON.image}`) if (i === metadataFilesArray.length - 1) { void uploadUpdatedMetadata() } } reader.readAsText(metadataFilesArray[i], 'utf8') } } const uploadUpdatedMetadata = async () => { setUpdatedMetadataFilesArray(updatedMetadataFilesArray) const result = await upload( updatedMetadataFilesArray, uploadService, 'metadata', nftStorageApiKey, pinataApiKey, pinataSecretKey, ) setBaseTokenUri(`ipfs://${result}`) console.log(`ipfs://${result}`) } const updateMetadataFileIndex = (index: number) => { setMetadataFileArrayIndex(index) setRefreshMetadata((prev) => !prev) } const updateMetadataFileArray = async (updatedMetadataFile: File) => { metadataFilesArray[metadataFileArrayIndex] = updatedMetadataFile console.log('Updated Metadata File:') console.log(JSON.parse(await metadataFilesArray[metadataFileArrayIndex]?.text())) } return (

Upload Assets & Metadata

Make sure you check our{' '} documentation {' '} on how to create your collection


{ setUploadMethod('existing') }} type="radio" value="Existing" />
{ setUploadMethod('new') }} type="radio" value="New" />
{baseTokenURI && ( Base Token URI: {baseTokenURI} )}

{uploadMethod === 'existing' && (

Though Stargaze's sg721 contract allows for off-chain metadata storage, it is recommended to use a decentralized storage solution, such as IPFS.
You may head over to{' '} NFT Storage {' '} or{' '} Pinata {' '} and upload your assets & metadata manually to get a base URI for your collection.

)} {uploadMethod === 'new' && (
{ setUploadService('nft-storage') }} type="radio" value="nft-storage" />
{ setUploadService('pinata') }} type="radio" value="pinata" />
setNftStorageApiKey(e.target.value)} value={nftStorageApiKey} />
setPinataApiKey(e.target.value)} value={pinataApiKey} /> setPinataSecretKey(e.target.value)} value={pinataSecretKey} />
{assetFilesArray.length > 0 && (
)} 0 && metadataFilesArray.length > 0 && assetFilesArray.length !== metadataFilesArray.length } > The number of assets and metadata files should match.
{assetFilesArray.length > 0 && (
{assetFilesArray.map((assetSource, index) => (
4 * index}> 4 * index + 1}> 4 * index + 2}> 4 * index + 3}>
))}
)}
)}
) } export default withMetadata(UploadPage, { center: false })