From d5b1acc16e2e3b2ebb9904e67ccef8e1db7e0707 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Sun, 23 Jul 2023 21:54:14 +0300 Subject: [PATCH 01/51] Create OpenEditionMinterDetailsDataProps --- .../openEdition/OpenEditionMinterCreator.tsx | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/components/openEdition/OpenEditionMinterCreator.tsx b/components/openEdition/OpenEditionMinterCreator.tsx index 3f0fce3..6fe8763 100644 --- a/components/openEdition/OpenEditionMinterCreator.tsx +++ b/components/openEdition/OpenEditionMinterCreator.tsx @@ -47,13 +47,24 @@ import { type RoyaltyDetailsDataProps, RoyaltyDetails } from './RoyaltyDetails' export type MetadataStorageMethod = 'off-chain' | 'on-chain' +export interface OpenEditionMinterDetailsDataProps { + imageUploadDetails?: ImageUploadDetailsDataProps + collectionDetails?: CollectionDetailsDataProps + royaltyDetails?: RoyaltyDetailsDataProps + onChainMetadataInputDetails?: OnChainMetadataInputDetailsDataProps + offChainMetadataUploadDetails?: OffChainMetadataUploadDetailsDataProps + mintingDetails?: MintingDetailsDataProps +} + interface OpenEditionMinterCreatorProps { onChange: (data: OpenEditionMinterCreatorDataProps) => void + onDetailsChange: (data: OpenEditionMinterDetailsDataProps) => void openEditionMinterUpdatableCreationFee?: string openEditionMinterCreationFee?: string minimumMintPrice?: string minimumUpdatableMintPrice?: string minterType?: MinterType + importedOpenEditionMinterDetails?: OpenEditionMinterCreatorDataProps } export interface OpenEditionMinterCreatorDataProps { @@ -65,6 +76,7 @@ export interface OpenEditionMinterCreatorDataProps { export const OpenEditionMinterCreator = ({ onChange, + onDetailsChange, openEditionMinterCreationFee, openEditionMinterUpdatableCreationFee, minimumMintPrice, @@ -585,6 +597,26 @@ export const OpenEditionMinterCreator = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [metadataStorageMethod, openEditionMinterContractAddress, sg721ContractAddress, transactionHash]) + useEffect(() => { + const data: OpenEditionMinterDetailsDataProps = { + imageUploadDetails: imageUploadDetails ? imageUploadDetails : undefined, + collectionDetails: collectionDetails ? collectionDetails : undefined, + royaltyDetails: royaltyDetails ? royaltyDetails : undefined, + onChainMetadataInputDetails: onChainMetadataInputDetails ? onChainMetadataInputDetails : undefined, + offChainMetadataUploadDetails: offChainMetadataUploadDetails ? offChainMetadataUploadDetails : undefined, + mintingDetails: mintingDetails ? mintingDetails : undefined, + } + onDetailsChange(data) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + imageUploadDetails, + collectionDetails, + royaltyDetails, + onChainMetadataInputDetails, + offChainMetadataUploadDetails, + mintingDetails, + ]) + return ( <div> {/* TODO: Cancel once we're able to index on-chain metadata */} From e074413a9ebacfb260a40ff540838c927bd96c6c Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 24 Jul 2023 21:57:53 +0300 Subject: [PATCH 02/51] Update AddressList --- components/forms/AddressList.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/forms/AddressList.tsx b/components/forms/AddressList.tsx index 59dad9d..b95db84 100644 --- a/components/forms/AddressList.tsx +++ b/components/forms/AddressList.tsx @@ -31,6 +31,7 @@ export function AddressList(props: AddressListProps) { {entries.map(([id], i) => ( <Address key={`ib-${id}`} + defaultValue={entries[i][1]} id={id} isLast={i === entries.length - 1} onAdd={onAdd} @@ -48,9 +49,10 @@ export interface AddressProps { onAdd: AddressListProps['onAdd'] onChange: AddressListProps['onChange'] onRemove: AddressListProps['onRemove'] + defaultValue?: Address } -export function Address({ id, isLast, onAdd, onChange, onRemove }: AddressProps) { +export function Address({ id, isLast, onAdd, onChange, onRemove, defaultValue }: AddressProps) { const wallet = useWallet() const Icon = useMemo(() => (isLast ? FaPlus : FaMinus), [isLast]) @@ -60,6 +62,7 @@ export function Address({ id, isLast, onAdd, onChange, onRemove }: AddressProps) id: `ib-address-${htmlId}`, name: `ib-address-${htmlId}`, title: ``, + defaultValue: defaultValue?.address, }) const resolveAddress = async (name: string) => { From 8921938c6c3f73b6a6fd9f44a6b323031b5d274a Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Tue, 25 Jul 2023 22:24:40 +0300 Subject: [PATCH 03/51] Initial export/import logic --- pages/collections/create.tsx | 103 ++++++++++++++++++++++++++++------- 1 file changed, 83 insertions(+), 20 deletions(-) diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index 6a99323..2399ce2 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -42,6 +42,7 @@ import type { DispatchExecuteArgs as VendingFactoryDispatchExecuteArgs } from 'c import { dispatchExecute as vendingFactoryDispatchExecute } from 'contracts/vendingFactory/messages/execute' import type { NextPage } from 'next' import { NextSeo } from 'next-seo' +import type { ChangeEvent } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { toast } from 'react-hot-toast' import { upload } from 'services/upload' @@ -71,6 +72,7 @@ import { uid } from 'utils/random' import type { MinterType } from '../../components/collections/actions/Combobox' import type { UploadMethod } from '../../components/collections/creation/UploadDetails' import { ConfirmationModal } from '../../components/ConfirmationModal' +import type { OpenEditionMinterDetailsDataProps } from '../../components/openEdition/OpenEditionMinterCreator' import { getAssetType } from '../../utils/getAssetType' import { isValidAddress } from '../../utils/isValidAddress' @@ -96,10 +98,23 @@ const CollectionCreationPage: NextPage = () => { [baseFactoryContract, wallet.address], ) + const [importedDetails, setImportedDetails] = useState<{ + minterType: MinterType + collectionDetails: CollectionDetailsDataProps + uploadDetails: UploadDetailsDataProps + mintingDetails: MintingDetailsDataProps + whitelistDetails: WhitelistDetailsDataProps + royaltyDetails: RoyaltyDetailsDataProps + baseMinterDetails: BaseMinterDetailsDataProps + openEditionMinterDetails: OpenEditionMinterDetailsDataProps + }>() + const [uploadDetails, setUploadDetails] = useState<UploadDetailsDataProps | null>(null) const [collectionDetails, setCollectionDetails] = useState<CollectionDetailsDataProps | null>(null) const [baseMinterDetails, setBaseMinterDetails] = useState<BaseMinterDetailsDataProps | null>(null) - const [openEditionMinterDetails, setOpenEditionMinterDetails] = useState<OpenEditionMinterCreatorDataProps | null>( + const [openEditionMinterCreatorData, setOpenEditionMinterCreatorData] = + useState<OpenEditionMinterCreatorDataProps | null>(null) + const [openEditionMinterDetails, setOpenEditionMinterDetails] = useState<OpenEditionMinterDetailsDataProps | null>( null, ) const [mintingDetails, setMintingDetails] = useState<MintingDetailsDataProps | null>(null) @@ -1159,27 +1174,60 @@ const CollectionCreationPage: NextPage = () => { }) } + // function to export all details as a .json file + const exportDetails = () => { + const details = { + minterType, + collectionDetails, + uploadDetails, + mintingDetails, + whitelistDetails, + royaltyDetails, + baseMinterDetails, + openEditionMinterDetails, + } + const element = document.createElement('a') + const file = new Blob([JSON.stringify(details)], { type: 'text/plain' }) + element.href = URL.createObjectURL(file) + element.download = 'details.json' + document.body.appendChild(element) // Required for this to work in FireFox + element.click() + } + // function to import all details from a .json file + const importDetails = (event: ChangeEvent<HTMLInputElement>) => { + if (event.target.files === null) return toast.error('No files selected.') + const file = event.target.files[0] + const reader = new FileReader() + reader.onload = (e) => { + const contents = e.target?.result + const details = JSON.parse(contents as string) + setMinterType(details.minterType) + setImportedDetails(details) + } + reader.readAsText(file) + } + const syncCollections = useCallback(async () => { const collectionAddress = - minterType === 'openEdition' ? openEditionMinterDetails?.sg721ContractAddress : sg721ContractAddress + minterType === 'openEdition' ? openEditionMinterCreatorData?.sg721ContractAddress : sg721ContractAddress if (collectionAddress && SYNC_COLLECTIONS_API_URL) { await axios.get(`${SYNC_COLLECTIONS_API_URL}/${collectionAddress}`).catch((error) => { console.error('Sync collections: ', error) }) } - }, [minterType, openEditionMinterDetails?.sg721ContractAddress, sg721ContractAddress]) + }, [minterType, openEditionMinterCreatorData?.sg721ContractAddress, sg721ContractAddress]) useEffect(() => { if ( vendingMinterContractAddress !== null || - openEditionMinterDetails?.openEditionMinterContractAddress || + openEditionMinterCreatorData?.openEditionMinterContractAddress || isMintingComplete ) { scrollRef.current?.scrollIntoView({ behavior: 'smooth' }) } if ( (minterType === 'vending' && vendingMinterContractAddress !== null) || - (minterType === 'openEdition' && openEditionMinterDetails?.openEditionMinterContractAddress) || + (minterType === 'openEdition' && openEditionMinterCreatorData?.openEditionMinterContractAddress) || (minterType === 'base' && vendingMinterContractAddress !== null && isMintingComplete) ) { void syncCollections() @@ -1192,7 +1240,7 @@ const CollectionCreationPage: NextPage = () => { } }, [ vendingMinterContractAddress, - openEditionMinterDetails?.openEditionMinterContractAddress, + openEditionMinterCreatorData?.openEditionMinterContractAddress, isMintingComplete, minterType, syncCollections, @@ -1222,7 +1270,15 @@ const CollectionCreationPage: NextPage = () => { : 'Create Collection' } /> - + <Button className="absolute top-5 right-5" onClick={() => exportDetails()}> + Export Details + </Button> + <input + accept="application/json" + className="absolute top-5 right-20" + onChange={(e) => importDetails(e)} + type="file" + /> <div className="mt-5 space-y-5 text-center"> <h1 className="font-heading text-4xl font-bold"> {minterType === 'base' && baseMinterDetails?.baseMinterAcquisitionMethod === 'existing' @@ -1244,7 +1300,7 @@ const CollectionCreationPage: NextPage = () => { </div> <div className="mx-10" ref={scrollRef}> <Conditional - test={minterType === 'openEdition' && openEditionMinterDetails?.openEditionMinterContractAddress !== null} + test={minterType === 'openEdition' && openEditionMinterCreatorData?.openEditionMinterContractAddress !== null} > <Alert className="mt-5" type="info"> <div> @@ -1253,10 +1309,10 @@ const CollectionCreationPage: NextPage = () => { className="text-stargaze hover:underline" external href={`/contracts/openEditionMinter/query/?contractAddress=${ - openEditionMinterDetails?.openEditionMinterContractAddress as string + openEditionMinterCreatorData?.openEditionMinterContractAddress as string }`} > - {openEditionMinterDetails?.openEditionMinterContractAddress as string} + {openEditionMinterCreatorData?.openEditionMinterContractAddress as string} </Anchor> <br /> SG721 Contract Address:{' '} @@ -1264,10 +1320,10 @@ const CollectionCreationPage: NextPage = () => { className="text-stargaze hover:underline" external href={`/contracts/sg721/query/?contractAddress=${ - openEditionMinterDetails?.sg721ContractAddress as string + openEditionMinterCreatorData?.sg721ContractAddress as string }`} > - {openEditionMinterDetails?.sg721ContractAddress as string} + {openEditionMinterCreatorData?.sg721ContractAddress as string} </Anchor> <br /> Transaction Hash: {' '} @@ -1275,18 +1331,18 @@ const CollectionCreationPage: NextPage = () => { <Anchor className="text-stargaze hover:underline" external - href={`${BLOCK_EXPLORER_URL}/tx/${openEditionMinterDetails?.transactionHash as string}`} + href={`${BLOCK_EXPLORER_URL}/tx/${openEditionMinterCreatorData?.transactionHash as string}`} > - {openEditionMinterDetails?.transactionHash} + {openEditionMinterCreatorData?.transactionHash} </Anchor> </Conditional> <Conditional test={NETWORK === 'mainnet'}> <Anchor className="text-stargaze hover:underline" external - href={`${BLOCK_EXPLORER_URL}/txs/${openEditionMinterDetails?.transactionHash as string}`} + href={`${BLOCK_EXPLORER_URL}/txs/${openEditionMinterCreatorData?.transactionHash as string}`} > - {openEditionMinterDetails?.transactionHash} + {openEditionMinterCreatorData?.transactionHash} </Anchor> </Conditional> <br /> @@ -1295,7 +1351,7 @@ const CollectionCreationPage: NextPage = () => { className="text-white" external href={`${STARGAZE_URL}/launchpad/${ - openEditionMinterDetails?.openEditionMinterContractAddress as string + openEditionMinterCreatorData?.openEditionMinterContractAddress as string }`} > View on Launchpad @@ -1564,7 +1620,8 @@ const CollectionCreationPage: NextPage = () => { minimumMintPrice={minimumOpenEditionMintPrice as string} minimumUpdatableMintPrice={minimumOpenEditionUpdatableMintPrice as string} minterType={minterType} - onChange={setOpenEditionMinterDetails} + onChange={setOpenEditionMinterCreatorData} + onDetailsChange={setOpenEditionMinterDetails} openEditionMinterCreationFee={openEditionMinterCreationFee as string} openEditionMinterUpdatableCreationFee={openEditionMinterUpdatableCreationFee as string} /> @@ -1573,6 +1630,7 @@ const CollectionCreationPage: NextPage = () => { <Conditional test={minterType === 'vending' || minterType === 'base'}> <UploadDetails baseMinterAcquisitionMethod={baseMinterDetails?.baseMinterAcquisitionMethod} + importedUploadDetails={importedDetails?.uploadDetails} minterType={minterType} onChange={setUploadDetails} /> @@ -1593,6 +1651,7 @@ const CollectionCreationPage: NextPage = () => { > <CollectionDetails coverImageUrl={coverImageUrl as string} + importedCollectionDetails={importedDetails?.collectionDetails} minterType={minterType} onChange={setCollectionDetails} uploadMethod={uploadDetails?.uploadMethod as UploadMethod} @@ -1600,6 +1659,7 @@ const CollectionCreationPage: NextPage = () => { </Conditional> <Conditional test={minterType === 'vending'}> <MintingDetails + importedMintingDetails={importedDetails?.mintingDetails} minimumMintPrice={ collectionDetails?.updatable ? Number(minimumUpdatableMintPrice) / 1000000 @@ -1633,10 +1693,13 @@ const CollectionCreationPage: NextPage = () => { > <div className="my-6"> <Conditional test={minterType === 'vending'}> - <WhitelistDetails onChange={setWhitelistDetails} /> + <WhitelistDetails + importedWhitelistDetails={importedDetails?.whitelistDetails} + onChange={setWhitelistDetails} + /> <div className="my-6" /> </Conditional> - <RoyaltyDetails onChange={setRoyaltyDetails} /> + <RoyaltyDetails importedRoyaltyDetails={importedDetails?.royaltyDetails} onChange={setRoyaltyDetails} /> </div> </Conditional> <Conditional test={readyToCreateVm && minterType === 'vending'}> From 2a38e791913fcb628d7f00d92800a2f6641e8436 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Tue, 25 Jul 2023 22:26:29 +0300 Subject: [PATCH 04/51] Surface open edition collection configuration --- components/openEdition/CollectionDetails.tsx | 19 ++++++++++++ components/openEdition/ImageUploadDetails.tsx | 15 +++++++++- components/openEdition/MintingDetails.tsx | 19 +++++++++++- .../OffChainMetadataUploadDetails.tsx | 15 ++++++++++ .../OnChainMetadataInputDetails.tsx | 27 ++++++++++++++++- .../openEdition/OpenEditionMinterCreator.tsx | 29 ++++++++++++++++--- components/openEdition/RoyaltyDetails.tsx | 12 +++++++- 7 files changed, 128 insertions(+), 8 deletions(-) diff --git a/components/openEdition/CollectionDetails.tsx b/components/openEdition/CollectionDetails.tsx index c919b73..4c46fff 100644 --- a/components/openEdition/CollectionDetails.tsx +++ b/components/openEdition/CollectionDetails.tsx @@ -28,6 +28,7 @@ interface CollectionDetailsProps { uploadMethod: UploadMethod coverImageUrl: string metadataStorageMethod: MetadataStorageMethod + importedCollectionDetails?: CollectionDetailsDataProps } export interface CollectionDetailsDataProps { @@ -46,6 +47,7 @@ export const CollectionDetails = ({ uploadMethod, metadataStorageMethod, coverImageUrl, + importedCollectionDetails, }: CollectionDetailsProps) => { const [coverImage, setCoverImage] = useState<File | null>(null) const [timestamp, setTimestamp] = useState<Date | undefined>() @@ -152,6 +154,23 @@ export const CollectionDetails = ({ } }, [updatable]) + useEffect(() => { + if (importedCollectionDetails) { + nameState.onChange(importedCollectionDetails.name) + descriptionState.onChange(importedCollectionDetails.description) + symbolState.onChange(importedCollectionDetails.symbol) + setCoverImage(importedCollectionDetails.imageFile[0] || null) + externalLinkState.onChange(importedCollectionDetails.externalLink || '') + setTimestamp( + importedCollectionDetails.startTradingTime + ? new Date(parseInt(importedCollectionDetails.startTradingTime) / 1_000_000) + : undefined, + ) + setExplicit(importedCollectionDetails.explicit) + setUpdatable(importedCollectionDetails.updatable) + } + }, [importedCollectionDetails]) + const videoPreview = useMemo(() => { if (uploadMethod === 'new' && coverImage) { return ( diff --git a/components/openEdition/ImageUploadDetails.tsx b/components/openEdition/ImageUploadDetails.tsx index ad84b0e..f8748f2 100644 --- a/components/openEdition/ImageUploadDetails.tsx +++ b/components/openEdition/ImageUploadDetails.tsx @@ -20,6 +20,7 @@ export type UploadMethod = 'new' | 'existing' interface ImageUploadDetailsProps { onChange: (value: ImageUploadDetailsDataProps) => void + importedImageUploadDetails?: ImageUploadDetailsDataProps } export interface ImageUploadDetailsDataProps { @@ -33,7 +34,7 @@ export interface ImageUploadDetailsDataProps { coverImageUrl?: string } -export const ImageUploadDetails = ({ onChange }: ImageUploadDetailsProps) => { +export const ImageUploadDetails = ({ onChange, importedImageUploadDetails }: ImageUploadDetailsProps) => { const [assetFile, setAssetFile] = useState<File>() const [uploadMethod, setUploadMethod] = useState<UploadMethod>('new') const [uploadService, setUploadService] = useState<UploadServiceType>('nft-storage') @@ -140,6 +141,18 @@ export const ImageUploadDetails = ({ onChange }: ImageUploadDetailsProps) => { imageUrlState.onChange('') }, [uploadMethod]) + useEffect(() => { + if (importedImageUploadDetails) { + setUploadMethod(importedImageUploadDetails.uploadMethod) + setUploadService(importedImageUploadDetails.uploadService) + nftStorageApiKeyState.onChange(importedImageUploadDetails.nftStorageApiKey || '') + pinataApiKeyState.onChange(importedImageUploadDetails.pinataApiKey || '') + pinataSecretKeyState.onChange(importedImageUploadDetails.pinataSecretKey || '') + imageUrlState.onChange(importedImageUploadDetails.imageUrl || '') + coverImageUrlState.onChange(importedImageUploadDetails.coverImageUrl || '') + } + }, [importedImageUploadDetails]) + const previewUrl = imageUrlState.value.toLowerCase().trim().startsWith('ipfs://') ? `https://ipfs-gw.stargaze-apis.com/ipfs/${imageUrlState.value.substring(7)}` : imageUrlState.value diff --git a/components/openEdition/MintingDetails.tsx b/components/openEdition/MintingDetails.tsx index 8b6acd5..0938a7d 100644 --- a/components/openEdition/MintingDetails.tsx +++ b/components/openEdition/MintingDetails.tsx @@ -15,6 +15,7 @@ interface MintingDetailsProps { onChange: (data: MintingDetailsDataProps) => void uploadMethod: UploadMethod minimumMintPrice: number + importedMintingDetails?: MintingDetailsDataProps } export interface MintingDetailsDataProps { @@ -25,7 +26,12 @@ export interface MintingDetailsDataProps { paymentAddress?: string } -export const MintingDetails = ({ onChange, uploadMethod, minimumMintPrice }: MintingDetailsProps) => { +export const MintingDetails = ({ + onChange, + uploadMethod, + minimumMintPrice, + importedMintingDetails, +}: MintingDetailsProps) => { const wallet = useWallet() const [timestamp, setTimestamp] = useState<Date | undefined>() @@ -81,6 +87,17 @@ export const MintingDetails = ({ onChange, uploadMethod, minimumMintPrice }: Min // eslint-disable-next-line react-hooks/exhaustive-deps }, [unitPriceState.value, perAddressLimitState.value, timestamp, endTimestamp, paymentAddressState.value]) + useEffect(() => { + if (importedMintingDetails) { + unitPriceState.onChange(Number(importedMintingDetails.unitPrice)) + perAddressLimitState.onChange(importedMintingDetails.perAddressLimit) + setTimestamp(new Date(Number(importedMintingDetails.startTime) / 1_000_000)) + setEndTimestamp(new Date(Number(importedMintingDetails.endTime) / 1_000_000)) + paymentAddressState.onChange(importedMintingDetails.paymentAddress ? importedMintingDetails.paymentAddress : '') + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [importedMintingDetails]) + return ( <div className="border-l-[1px] border-gray-500 border-opacity-20"> <FormGroup subtitle="Information about your minting settings" title="Minting Details"> diff --git a/components/openEdition/OffChainMetadataUploadDetails.tsx b/components/openEdition/OffChainMetadataUploadDetails.tsx index 02c4ce3..02e8703 100644 --- a/components/openEdition/OffChainMetadataUploadDetails.tsx +++ b/components/openEdition/OffChainMetadataUploadDetails.tsx @@ -28,6 +28,7 @@ export type UploadMethod = 'new' | 'existing' interface OffChainMetadataUploadDetailsProps { onChange: (value: OffChainMetadataUploadDetailsDataProps) => void metadataStorageMethod?: MetadataStorageMethod + importedOffChainMetadataUploadDetails?: OffChainMetadataUploadDetailsDataProps } export interface OffChainMetadataUploadDetailsDataProps { @@ -46,6 +47,7 @@ export interface OffChainMetadataUploadDetailsDataProps { export const OffChainMetadataUploadDetails = ({ onChange, metadataStorageMethod, + importedOffChainMetadataUploadDetails, }: OffChainMetadataUploadDetailsProps) => { const [assetFilesArray, setAssetFilesArray] = useState<File[]>([]) const [metadataFilesArray, setMetadataFilesArray] = useState<File[]>([]) @@ -233,6 +235,19 @@ export const OffChainMetadataUploadDetails = ({ coverImageUrlState.onChange('') }, [uploadMethod, metadataStorageMethod]) + useEffect(() => { + if (importedOffChainMetadataUploadDetails) { + setUploadService(importedOffChainMetadataUploadDetails.uploadService) + nftStorageApiKeyState.onChange(importedOffChainMetadataUploadDetails.nftStorageApiKey || '') + pinataApiKeyState.onChange(importedOffChainMetadataUploadDetails.pinataApiKey || '') + pinataSecretKeyState.onChange(importedOffChainMetadataUploadDetails.pinataSecretKey || '') + setUploadMethod('existing') + tokenUriState.onChange(importedOffChainMetadataUploadDetails.tokenURI || '') + coverImageUrlState.onChange(importedOffChainMetadataUploadDetails.imageUrl || '') + setOpenEditionMinterMetadataFile(importedOffChainMetadataUploadDetails.openEditionMinterMetadataFile) + } + }, [importedOffChainMetadataUploadDetails]) + return ( <div className="justify-items-start mb-3 rounded border-2 border-white/20 flex-column"> <div className="flex justify-center"> diff --git a/components/openEdition/OnChainMetadataInputDetails.tsx b/components/openEdition/OnChainMetadataInputDetails.tsx index 2fb0eed..f71fa23 100644 --- a/components/openEdition/OnChainMetadataInputDetails.tsx +++ b/components/openEdition/OnChainMetadataInputDetails.tsx @@ -21,6 +21,7 @@ import type { UploadMethod } from './ImageUploadDetails' interface OnChainMetadataInputDetailsProps { onChange: (data: OnChainMetadataInputDetailsDataProps) => void uploadMethod: UploadMethod | undefined + importedOnChainMetadataInputDetails?: OnChainMetadataInputDetailsDataProps } export interface OnChainMetadataInputDetailsDataProps { @@ -34,7 +35,11 @@ export interface OnChainMetadataInputDetailsDataProps { youtube_url?: string } -export const OnChainMetadataInputDetails = ({ onChange, uploadMethod }: OnChainMetadataInputDetailsProps) => { +export const OnChainMetadataInputDetails = ({ + onChange, + uploadMethod, + importedOnChainMetadataInputDetails, +}: OnChainMetadataInputDetailsProps) => { const wallet = useWallet() const [timestamp, setTimestamp] = useState<Date | undefined>(undefined) const [metadataFile, setMetadataFile] = useState<File>() @@ -196,6 +201,26 @@ export const OnChainMetadataInputDetails = ({ onChange, uploadMethod }: OnChainM youtubeUrlState.value, ]) + useEffect(() => { + if (importedOnChainMetadataInputDetails) { + nameState.onChange(importedOnChainMetadataInputDetails.name || '') + descriptionState.onChange(importedOnChainMetadataInputDetails.description || '') + externalUrlState.onChange(importedOnChainMetadataInputDetails.external_url || '') + youtubeUrlState.onChange(importedOnChainMetadataInputDetails.youtube_url || '') + animationUrlState.onChange(importedOnChainMetadataInputDetails.animation_url || '') + imageDataState.onChange(importedOnChainMetadataInputDetails.image_data || '') + if (importedOnChainMetadataInputDetails.attributes) { + attributesState.reset() + importedOnChainMetadataInputDetails.attributes.forEach((attr) => { + attributesState.add({ + trait_type: attr.trait_type, + value: attr.value, + }) + }) + } + } + }, [importedOnChainMetadataInputDetails]) + return ( <div className="py-3 px-8 rounded border-2 border-white/20"> <span className="ml-4 text-xl font-bold underline underline-offset-4">NFT Metadata</span> diff --git a/components/openEdition/OpenEditionMinterCreator.tsx b/components/openEdition/OpenEditionMinterCreator.tsx index 6fe8763..bfd893a 100644 --- a/components/openEdition/OpenEditionMinterCreator.tsx +++ b/components/openEdition/OpenEditionMinterCreator.tsx @@ -54,6 +54,7 @@ export interface OpenEditionMinterDetailsDataProps { onChainMetadataInputDetails?: OnChainMetadataInputDetailsDataProps offChainMetadataUploadDetails?: OffChainMetadataUploadDetailsDataProps mintingDetails?: MintingDetailsDataProps + metadataStorageMethod?: MetadataStorageMethod } interface OpenEditionMinterCreatorProps { @@ -64,7 +65,7 @@ interface OpenEditionMinterCreatorProps { minimumMintPrice?: string minimumUpdatableMintPrice?: string minterType?: MinterType - importedOpenEditionMinterDetails?: OpenEditionMinterCreatorDataProps + importedOpenEditionMinterDetails?: OpenEditionMinterDetailsDataProps } export interface OpenEditionMinterCreatorDataProps { @@ -82,6 +83,7 @@ export const OpenEditionMinterCreator = ({ minimumMintPrice, minimumUpdatableMintPrice, minterType, + importedOpenEditionMinterDetails, }: OpenEditionMinterCreatorProps) => { const wallet = useWallet() const { openEditionMinter: openEditionMinterContract, openEditionFactory: openEditionFactoryContract } = @@ -605,6 +607,7 @@ export const OpenEditionMinterCreator = ({ onChainMetadataInputDetails: onChainMetadataInputDetails ? onChainMetadataInputDetails : undefined, offChainMetadataUploadDetails: offChainMetadataUploadDetails ? offChainMetadataUploadDetails : undefined, mintingDetails: mintingDetails ? mintingDetails : undefined, + metadataStorageMethod, } onDetailsChange(data) // eslint-disable-next-line react-hooks/exhaustive-deps @@ -617,6 +620,12 @@ export const OpenEditionMinterCreator = ({ mintingDetails, ]) + useEffect(() => { + if (importedOpenEditionMinterDetails) { + setMetadataStorageMethod(importedOpenEditionMinterDetails.metadataStorageMethod as MetadataStorageMethod) + } + }, [importedOpenEditionMinterDetails]) + return ( <div> {/* TODO: Cancel once we're able to index on-chain metadata */} @@ -667,13 +676,20 @@ export const OpenEditionMinterCreator = ({ <div className={clsx('my-4 mx-10')}> <Conditional test={metadataStorageMethod === 'off-chain'}> <div> - <OffChainMetadataUploadDetails onChange={setOffChainMetadataUploadDetails} /> + <OffChainMetadataUploadDetails + importedOffChainMetadataUploadDetails={importedOpenEditionMinterDetails?.offChainMetadataUploadDetails} + onChange={setOffChainMetadataUploadDetails} + /> </div> </Conditional> <Conditional test={metadataStorageMethod === 'on-chain'}> <div> - <ImageUploadDetails onChange={setImageUploadDetails} /> + <ImageUploadDetails + importedImageUploadDetails={importedOpenEditionMinterDetails?.imageUploadDetails} + onChange={setImageUploadDetails} + /> <OnChainMetadataInputDetails + importedOnChainMetadataInputDetails={importedOpenEditionMinterDetails?.onChainMetadataInputDetails} onChange={setOnChainMetadataInputDetails} uploadMethod={imageUploadDetails?.uploadMethod} /> @@ -687,6 +703,7 @@ export const OpenEditionMinterCreator = ({ ? (offChainMetadataUploadDetails?.imageUrl as string) : (imageUploadDetails?.coverImageUrl as string) } + importedCollectionDetails={importedOpenEditionMinterDetails?.collectionDetails} metadataStorageMethod={metadataStorageMethod} onChange={setCollectionDetails} uploadMethod={ @@ -696,6 +713,7 @@ export const OpenEditionMinterCreator = ({ } /> <MintingDetails + importedMintingDetails={importedOpenEditionMinterDetails?.mintingDetails} minimumMintPrice={ collectionDetails?.updatable ? Number(minimumUpdatableMintPrice) / 1000000 @@ -706,7 +724,10 @@ export const OpenEditionMinterCreator = ({ /> </div> <div className="my-6"> - <RoyaltyDetails onChange={setRoyaltyDetails} /> + <RoyaltyDetails + importedRoyaltyDetails={importedOpenEditionMinterDetails?.royaltyDetails} + onChange={setRoyaltyDetails} + /> </div> <div className="flex justify-end w-full"> <Button diff --git a/components/openEdition/RoyaltyDetails.tsx b/components/openEdition/RoyaltyDetails.tsx index 518458a..17f32d3 100644 --- a/components/openEdition/RoyaltyDetails.tsx +++ b/components/openEdition/RoyaltyDetails.tsx @@ -9,6 +9,7 @@ import { NumberInput, TextInput } from '../forms/FormInput' interface RoyaltyDetailsProps { onChange: (data: RoyaltyDetailsDataProps) => void + importedRoyaltyDetails?: RoyaltyDetailsDataProps } export interface RoyaltyDetailsDataProps { @@ -19,7 +20,7 @@ export interface RoyaltyDetailsDataProps { type RoyaltyState = 'none' | 'new' -export const RoyaltyDetails = ({ onChange }: RoyaltyDetailsProps) => { +export const RoyaltyDetails = ({ onChange, importedRoyaltyDetails }: RoyaltyDetailsProps) => { const wallet = useWallet() const [royaltyState, setRoyaltyState] = useState<RoyaltyState>('none') @@ -60,6 +61,15 @@ export const RoyaltyDetails = ({ onChange }: RoyaltyDetailsProps) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [royaltyState, royaltyPaymentAddressState.value, royaltyShareState.value]) + useEffect(() => { + if (importedRoyaltyDetails) { + setRoyaltyState(importedRoyaltyDetails.royaltyType) + royaltyPaymentAddressState.onChange(importedRoyaltyDetails.paymentAddress) + royaltyShareState.onChange(importedRoyaltyDetails.share.toString()) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [importedRoyaltyDetails]) + return ( <div className="py-3 px-8 mx-10 rounded border-2 border-white/20"> <div className="flex justify-center"> From 0481032a1f53ecda1bc951802b773c21be69b068 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Tue, 25 Jul 2023 22:27:42 +0300 Subject: [PATCH 05/51] Surface standard & 1/1 collection configuration --- .../creation/BaseMinterDetails.tsx | 12 +++- .../creation/CollectionDetails.tsx | 26 ++++++++- .../collections/creation/MintingDetails.tsx | 20 ++++++- .../collections/creation/RoyaltyDetails.tsx | 12 +++- .../collections/creation/UploadDetails.tsx | 33 ++++++++++- .../collections/creation/WhitelistDetails.tsx | 56 ++++++++++++++++++- 6 files changed, 149 insertions(+), 10 deletions(-) diff --git a/components/collections/creation/BaseMinterDetails.tsx b/components/collections/creation/BaseMinterDetails.tsx index e40d842..f87ec07 100644 --- a/components/collections/creation/BaseMinterDetails.tsx +++ b/components/collections/creation/BaseMinterDetails.tsx @@ -28,6 +28,7 @@ export interface MinterInfo { interface BaseMinterDetailsProps { onChange: (data: BaseMinterDetailsDataProps) => void minterType: MinterType + importedBaseMinterDetails?: BaseMinterDetailsDataProps } export interface BaseMinterDetailsDataProps { @@ -37,7 +38,7 @@ export interface BaseMinterDetailsDataProps { collectionTokenCount: number | undefined } -export const BaseMinterDetails = ({ onChange, minterType }: BaseMinterDetailsProps) => { +export const BaseMinterDetails = ({ onChange, minterType, importedBaseMinterDetails }: BaseMinterDetailsProps) => { const wallet = useWallet() const [myBaseMinterContracts, setMyBaseMinterContracts] = useState<MinterInfo[]>([]) @@ -198,6 +199,15 @@ export const BaseMinterDetails = ({ onChange, minterType }: BaseMinterDetailsPro collectionTokenCount, ]) + useEffect(() => { + if (importedBaseMinterDetails) { + setBaseMinterAcquisitionMethod(importedBaseMinterDetails.baseMinterAcquisitionMethod) + existingBaseMinterState.onChange( + importedBaseMinterDetails.existingBaseMinter ? importedBaseMinterDetails.existingBaseMinter : '', + ) + } + }, [importedBaseMinterDetails]) + return ( <div className="mx-10 mb-4 rounded border-2 border-white/20"> <div className="flex justify-center mb-2"> diff --git a/components/collections/creation/CollectionDetails.tsx b/components/collections/creation/CollectionDetails.tsx index 629cbff..b2c2256 100644 --- a/components/collections/creation/CollectionDetails.tsx +++ b/components/collections/creation/CollectionDetails.tsx @@ -26,6 +26,7 @@ interface CollectionDetailsProps { uploadMethod: UploadMethod coverImageUrl: string minterType: MinterType + importedCollectionDetails?: CollectionDetailsDataProps } export interface CollectionDetailsDataProps { @@ -39,7 +40,13 @@ export interface CollectionDetailsDataProps { updatable: boolean } -export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl, minterType }: CollectionDetailsProps) => { +export const CollectionDetails = ({ + onChange, + uploadMethod, + coverImageUrl, + minterType, + importedCollectionDetails, +}: CollectionDetailsProps) => { const [coverImage, setCoverImage] = useState<File | null>(null) const [timestamp, setTimestamp] = useState<Date | undefined>() const [explicit, setExplicit] = useState<boolean>(false) @@ -105,6 +112,23 @@ export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl, minte updatable, ]) + useEffect(() => { + if (importedCollectionDetails) { + nameState.onChange(importedCollectionDetails.name) + descriptionState.onChange(importedCollectionDetails.description) + symbolState.onChange(importedCollectionDetails.symbol) + externalLinkState.onChange(importedCollectionDetails.externalLink || '') + setTimestamp( + importedCollectionDetails.startTradingTime + ? new Date(parseInt(importedCollectionDetails.startTradingTime) / 1_000_000) + : undefined, + ) + setExplicit(importedCollectionDetails.explicit) + setUpdatable(importedCollectionDetails.updatable) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [importedCollectionDetails]) + const selectCoverImage = (event: ChangeEvent<HTMLInputElement>) => { if (event.target.files === null) return toast.error('Error selecting cover image') if (event.target.files.length === 0) { diff --git a/components/collections/creation/MintingDetails.tsx b/components/collections/creation/MintingDetails.tsx index 6ca136e..7185ceb 100644 --- a/components/collections/creation/MintingDetails.tsx +++ b/components/collections/creation/MintingDetails.tsx @@ -16,6 +16,7 @@ interface MintingDetailsProps { numberOfTokens: number | undefined uploadMethod: UploadMethod minimumMintPrice: number + importedMintingDetails?: MintingDetailsDataProps } export interface MintingDetailsDataProps { @@ -26,7 +27,13 @@ export interface MintingDetailsDataProps { paymentAddress?: string } -export const MintingDetails = ({ onChange, numberOfTokens, uploadMethod, minimumMintPrice }: MintingDetailsProps) => { +export const MintingDetails = ({ + onChange, + numberOfTokens, + uploadMethod, + minimumMintPrice, + importedMintingDetails, +}: MintingDetailsProps) => { const wallet = useWallet() const [timestamp, setTimestamp] = useState<Date | undefined>() @@ -97,6 +104,17 @@ export const MintingDetails = ({ onChange, numberOfTokens, uploadMethod, minimum paymentAddressState.value, ]) + useEffect(() => { + if (importedMintingDetails) { + numberOfTokensState.onChange(importedMintingDetails.numTokens) + unitPriceState.onChange(Number(importedMintingDetails.unitPrice) / 1_000_000) + perAddressLimitState.onChange(importedMintingDetails.perAddressLimit) + setTimestamp(new Date(Number(importedMintingDetails.startTime) / 1_000_000)) + paymentAddressState.onChange(importedMintingDetails.paymentAddress ? importedMintingDetails.paymentAddress : '') + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [importedMintingDetails]) + return ( <div> <FormGroup subtitle="Information about your minting settings" title="Minting Details"> diff --git a/components/collections/creation/RoyaltyDetails.tsx b/components/collections/creation/RoyaltyDetails.tsx index 0d873e2..f8ccd35 100644 --- a/components/collections/creation/RoyaltyDetails.tsx +++ b/components/collections/creation/RoyaltyDetails.tsx @@ -9,6 +9,7 @@ import { NumberInput, TextInput } from '../../forms/FormInput' interface RoyaltyDetailsProps { onChange: (data: RoyaltyDetailsDataProps) => void + importedRoyaltyDetails?: RoyaltyDetailsDataProps } export interface RoyaltyDetailsDataProps { @@ -19,7 +20,7 @@ export interface RoyaltyDetailsDataProps { type RoyaltyState = 'none' | 'new' -export const RoyaltyDetails = ({ onChange }: RoyaltyDetailsProps) => { +export const RoyaltyDetails = ({ onChange, importedRoyaltyDetails }: RoyaltyDetailsProps) => { const wallet = useWallet() const [royaltyState, setRoyaltyState] = useState<RoyaltyState>('none') @@ -60,6 +61,15 @@ export const RoyaltyDetails = ({ onChange }: RoyaltyDetailsProps) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [royaltyState, royaltyPaymentAddressState.value, royaltyShareState.value]) + useEffect(() => { + if (importedRoyaltyDetails) { + setRoyaltyState(importedRoyaltyDetails.royaltyType) + royaltyPaymentAddressState.onChange(importedRoyaltyDetails.paymentAddress) + royaltyShareState.onChange(importedRoyaltyDetails.share.toString()) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [importedRoyaltyDetails]) + return ( <div className="py-3 px-8 rounded border-2 border-white/20"> <div className="flex justify-center"> diff --git a/components/collections/creation/UploadDetails.tsx b/components/collections/creation/UploadDetails.tsx index 02567a9..0b8aa61 100644 --- a/components/collections/creation/UploadDetails.tsx +++ b/components/collections/creation/UploadDetails.tsx @@ -31,6 +31,7 @@ interface UploadDetailsProps { onChange: (value: UploadDetailsDataProps) => void minterType: MinterType baseMinterAcquisitionMethod?: BaseMinterAcquisitionMethod + importedUploadDetails?: UploadDetailsDataProps } export interface UploadDetailsDataProps { @@ -46,7 +47,12 @@ export interface UploadDetailsDataProps { baseMinterMetadataFile?: File } -export const UploadDetails = ({ onChange, minterType, baseMinterAcquisitionMethod }: UploadDetailsProps) => { +export const UploadDetails = ({ + onChange, + minterType, + baseMinterAcquisitionMethod, + importedUploadDetails, +}: UploadDetailsProps) => { const [assetFilesArray, setAssetFilesArray] = useState<File[]>([]) const [metadataFilesArray, setMetadataFilesArray] = useState<File[]>([]) const [uploadMethod, setUploadMethod] = useState<UploadMethod>('new') @@ -274,10 +280,31 @@ export const UploadDetails = ({ onChange, minterType, baseMinterAcquisitionMetho setMetadataFilesArray([]) if (assetFilesRef.current) assetFilesRef.current.value = '' setAssetFilesArray([]) - baseTokenUriState.onChange('') - coverImageUrlState.onChange('') + if (!importedUploadDetails) { + baseTokenUriState.onChange('') + coverImageUrlState.onChange('') + } }, [uploadMethod, minterType, baseMinterAcquisitionMethod]) + useEffect(() => { + if (importedUploadDetails) { + if (importedUploadDetails.uploadMethod === 'new') { + setUploadMethod('existing') + setUploadService(importedUploadDetails.uploadService) + nftStorageApiKeyState.onChange(importedUploadDetails.nftStorageApiKey || '') + pinataApiKeyState.onChange(importedUploadDetails.pinataApiKey || '') + pinataSecretKeyState.onChange(importedUploadDetails.pinataSecretKey || '') + baseTokenUriState.onChange(importedUploadDetails.baseTokenURI || '') + coverImageUrlState.onChange(importedUploadDetails.imageUrl || '') + } else if (importedUploadDetails.uploadMethod === 'existing') { + setUploadMethod('existing') + baseTokenUriState.onChange(importedUploadDetails.baseTokenURI || '') + coverImageUrlState.onChange(importedUploadDetails.imageUrl || '') + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [importedUploadDetails]) + return ( <div className="justify-items-start mb-3 rounded border-2 border-white/20 flex-column"> <div className="flex justify-center"> diff --git a/components/collections/creation/WhitelistDetails.tsx b/components/collections/creation/WhitelistDetails.tsx index 3f9daa7..fd3beb3 100644 --- a/components/collections/creation/WhitelistDetails.tsx +++ b/components/collections/creation/WhitelistDetails.tsx @@ -18,6 +18,7 @@ import { WhitelistUpload } from '../../WhitelistUpload' interface WhitelistDetailsProps { onChange: (data: WhitelistDetailsDataProps) => void + importedWhitelistDetails?: WhitelistDetailsDataProps } export interface WhitelistDetailsDataProps { @@ -38,7 +39,7 @@ type WhitelistState = 'none' | 'existing' | 'new' type WhitelistType = 'standard' | 'flex' -export const WhitelistDetails = ({ onChange }: WhitelistDetailsProps) => { +export const WhitelistDetails = ({ onChange, importedWhitelistDetails }: WhitelistDetailsProps) => { const [whitelistState, setWhitelistState] = useState<WhitelistState>('none') const [whitelistType, setWhitelistType] = useState<WhitelistType>('standard') const [startDate, setStartDate] = useState<Date | undefined>(undefined) @@ -89,8 +90,10 @@ export const WhitelistDetails = ({ onChange }: WhitelistDetailsProps) => { } useEffect(() => { - setWhitelistStandardArray([]) - setWhitelistFlexArray([]) + if (!importedWhitelistDetails) { + setWhitelistStandardArray([]) + setWhitelistFlexArray([]) + } }, [whitelistType]) useEffect(() => { @@ -138,6 +141,53 @@ export const WhitelistDetails = ({ onChange }: WhitelistDetailsProps) => { adminsMutable, ]) + // make the necessary changes with respect to imported whitelist details + useEffect(() => { + if (importedWhitelistDetails) { + setWhitelistState(importedWhitelistDetails.whitelistState) + setWhitelistType(importedWhitelistDetails.whitelistType) + whitelistAddressState.onChange( + importedWhitelistDetails.contractAddress ? importedWhitelistDetails.contractAddress : '', + ) + unitPriceState.onChange(importedWhitelistDetails.unitPrice ? Number(importedWhitelistDetails.unitPrice) : 0) + memberLimitState.onChange(importedWhitelistDetails.memberLimit ? importedWhitelistDetails.memberLimit : 0) + perAddressLimitState.onChange( + importedWhitelistDetails.perAddressLimit ? importedWhitelistDetails.perAddressLimit : 0, + ) + setStartDate( + importedWhitelistDetails.startTime + ? new Date(Number(importedWhitelistDetails.startTime) / 1_000_000) + : undefined, + ) + setEndDate( + importedWhitelistDetails.endTime ? new Date(Number(importedWhitelistDetails.endTime) / 1_000_000) : undefined, + ) + setAdminsMutable(importedWhitelistDetails.adminsMutable ? importedWhitelistDetails.adminsMutable : true) + importedWhitelistDetails.admins?.forEach((admin) => { + addressListState.reset() + addressListState.add({ address: admin }) + }) + if (importedWhitelistDetails.whitelistType === 'standard') { + setWhitelistStandardArray([]) + importedWhitelistDetails.members?.forEach((member) => { + setWhitelistStandardArray((standardArray) => [...standardArray, member as string]) + }) + } else { + setWhitelistFlexArray([]) + importedWhitelistDetails.members?.forEach((member) => { + setWhitelistFlexArray((flexArray) => [ + ...flexArray, + { + address: (member as WhitelistFlexMember).address, + mint_count: (member as WhitelistFlexMember).mint_count, + }, + ]) + }) + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [importedWhitelistDetails]) + return ( <div className="py-3 px-8 rounded border-2 border-white/20"> <div className="flex justify-center"> From 75fe1d33876d96a50622bee6e4cc09d17b8fa613 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Wed, 26 Jul 2023 22:26:27 +0300 Subject: [PATCH 06/51] Init TokenInfo --- config/token.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 config/token.ts diff --git a/config/token.ts b/config/token.ts new file mode 100644 index 0000000..10d815f --- /dev/null +++ b/config/token.ts @@ -0,0 +1,22 @@ +export interface TokenInfo { + id: string + denom: string + displayName: string + decimalPlaces: number + imageURL?: string + symbol?: string +} + +export const ibcAtom: TokenInfo = { + id: 'ibc-atom', + denom: 'ibc/', + displayName: 'ATOM', + decimalPlaces: 6, +} + +export const stars: TokenInfo = { + id: 'stars', + denom: 'ustars', + displayName: 'STARS', + decimalPlaces: 6, +} From 3ff3d094b405a908ee676f71f4333f9bc9e4ddb5 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Thu, 27 Jul 2023 17:26:51 +0300 Subject: [PATCH 07/51] Init minter list --- .env.example | 2 ++ config/minter.ts | 34 ++++++++++++++++++++++++++++++++++ config/token.ts | 23 ++++++++++++++++------- env.d.ts | 2 ++ utils/constants.ts | 2 ++ 5 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 config/minter.ts diff --git a/.env.example b/.env.example index 46be8b3..7ec8697 100644 --- a/.env.example +++ b/.env.example @@ -14,6 +14,8 @@ NEXT_PUBLIC_VENDING_FACTORY_FLEX_ADDRESS="stars1hvu2ghqkcnvhtj2fc6wuazxt4dqcftsl NEXT_PUBLIC_BASE_FACTORY_ADDRESS="stars1a45hcxty3spnmm2f0papl8v4dk5ew29s4syhn4efte8u5haex99qlkrtnx" NEXT_PUBLIC_BASE_FACTORY_UPDATABLE_ADDRESS="stars100xegx2syry4tclkmejjwxk4nfqahvcqhm9qxut5wxuzhj5d9qfsh5nmym" NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" +NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" +NEXT_PUBLIC_OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS="stars1fk5dkzcylam8mcpqrn8y9spauvc3d4navtaqurcc49dc3p9f8d3qdkvymx" NEXT_PUBLIC_OPEN_EDITION_MINTER_CODE_ID=2579 diff --git a/config/minter.ts b/config/minter.ts new file mode 100644 index 0000000..4fa2a14 --- /dev/null +++ b/config/minter.ts @@ -0,0 +1,34 @@ +import { + OPEN_EDITION_FACTORY_ADDRESS, + OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS, + OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS, +} from 'utils/constants' + +import type { TokenInfo } from './token' +import { ibcAtom, ibcFrenz, stars } from './token' + +export interface MinterInfo { + id: string + factoryAddress: string + supportedToken: TokenInfo +} + +export const openEditionStarsMinter: MinterInfo = { + id: 'open-edition-stars-minter', + factoryAddress: OPEN_EDITION_FACTORY_ADDRESS, + supportedToken: stars, +} + +export const openEditionIbcAtomMinter: MinterInfo = { + id: 'open-edition-ibc-atom-minter', + factoryAddress: OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS, + supportedToken: ibcAtom, +} + +export const openEditionIbcFrenzMinter: MinterInfo = { + id: 'open-edition-ibc-frenz-minter', + factoryAddress: OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS, + supportedToken: ibcFrenz, +} + +export const minterList = [openEditionStarsMinter, openEditionIbcAtomMinter, openEditionIbcFrenzMinter] diff --git a/config/token.ts b/config/token.ts index 10d815f..8f64d82 100644 --- a/config/token.ts +++ b/config/token.ts @@ -7,16 +7,25 @@ export interface TokenInfo { symbol?: string } -export const ibcAtom: TokenInfo = { - id: 'ibc-atom', - denom: 'ibc/', - displayName: 'ATOM', - decimalPlaces: 6, -} - export const stars: TokenInfo = { id: 'stars', denom: 'ustars', displayName: 'STARS', decimalPlaces: 6, } + +export const ibcAtom: TokenInfo = { + id: 'ibc-atom', + denom: 'ibc/atom', + displayName: 'ATOM', + decimalPlaces: 6, +} + +export const ibcFrenz: TokenInfo = { + id: 'ibc-frenz', + denom: 'ibc/frenz', + displayName: 'FRENZ', + decimalPlaces: 6, +} + +export const tokensList = [stars, ibcAtom, ibcFrenz] diff --git a/env.d.ts b/env.d.ts index 955e661..77abe04 100644 --- a/env.d.ts +++ b/env.d.ts @@ -24,6 +24,8 @@ declare namespace NodeJS { readonly NEXT_PUBLIC_VENDING_MINTER_FLEX_CODE_ID: string readonly NEXT_PUBLIC_VENDING_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_MINTER_CODE_ID: string readonly NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_ADDRESS: string diff --git a/utils/constants.ts b/utils/constants.ts index 097aefa..81955ff 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -15,6 +15,8 @@ export const VENDING_FACTORY_FLEX_ADDRESS = process.env.NEXT_PUBLIC_VENDING_FACT export const BASE_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_BASE_FACTORY_ADDRESS export const BASE_FACTORY_UPDATABLE_ADDRESS = process.env.NEXT_PUBLIC_BASE_FACTORY_UPDATABLE_ADDRESS export const OPEN_EDITION_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS +export const OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS +export const OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS export const OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS export const OPEN_EDITION_MINTER_CODE_ID = parseInt(process.env.NEXT_PUBLIC_OPEN_EDITION_MINTER_CODE_ID, 10) export const SG721_NAME_ADDRESS = process.env.NEXT_PUBLIC_SG721_NAME_ADDRESS From 701369f2462e53ff1bd60b3ee37c2454c4a4c4bf Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Fri, 28 Jul 2023 23:38:45 +0300 Subject: [PATCH 08/51] Surface Open Edition Minter details --- .../openEdition/OpenEditionMinterCreator.tsx | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/components/openEdition/OpenEditionMinterCreator.tsx b/components/openEdition/OpenEditionMinterCreator.tsx index 3f0fce3..eab0869 100644 --- a/components/openEdition/OpenEditionMinterCreator.tsx +++ b/components/openEdition/OpenEditionMinterCreator.tsx @@ -12,6 +12,7 @@ import type { MinterType } from 'components/collections/actions/Combobox' import { Conditional } from 'components/Conditional' import { ConfirmationModal } from 'components/ConfirmationModal' import { LoadingModal } from 'components/LoadingModal' +import type { TokenInfo } from 'config/token' import { useContracts } from 'contexts/contracts' import { addLogItem } from 'contexts/log' import { useWallet } from 'contexts/wallet' @@ -47,13 +48,25 @@ import { type RoyaltyDetailsDataProps, RoyaltyDetails } from './RoyaltyDetails' export type MetadataStorageMethod = 'off-chain' | 'on-chain' +export interface OpenEditionMinterDetailsDataProps { + imageUploadDetails?: ImageUploadDetailsDataProps + collectionDetails?: CollectionDetailsDataProps + royaltyDetails?: RoyaltyDetailsDataProps + onChainMetadataInputDetails?: OnChainMetadataInputDetailsDataProps + offChainMetadataUploadDetails?: OffChainMetadataUploadDetailsDataProps + mintingDetails?: MintingDetailsDataProps + metadataStorageMethod?: MetadataStorageMethod +} + interface OpenEditionMinterCreatorProps { onChange: (data: OpenEditionMinterCreatorDataProps) => void + onDetailsChange: (data: OpenEditionMinterDetailsDataProps) => void openEditionMinterUpdatableCreationFee?: string openEditionMinterCreationFee?: string minimumMintPrice?: string minimumUpdatableMintPrice?: string minterType?: MinterType + mintTokenFromFactory?: TokenInfo | undefined } export interface OpenEditionMinterCreatorDataProps { @@ -65,11 +78,13 @@ export interface OpenEditionMinterCreatorDataProps { export const OpenEditionMinterCreator = ({ onChange, + onDetailsChange, openEditionMinterCreationFee, openEditionMinterUpdatableCreationFee, minimumMintPrice, minimumUpdatableMintPrice, minterType, + mintTokenFromFactory, }: OpenEditionMinterCreatorProps) => { const wallet = useWallet() const { openEditionMinter: openEditionMinterContract, openEditionFactory: openEditionFactoryContract } = @@ -585,6 +600,27 @@ export const OpenEditionMinterCreator = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [metadataStorageMethod, openEditionMinterContractAddress, sg721ContractAddress, transactionHash]) + useEffect(() => { + const data: OpenEditionMinterDetailsDataProps = { + imageUploadDetails: imageUploadDetails ? imageUploadDetails : undefined, + collectionDetails: collectionDetails ? collectionDetails : undefined, + royaltyDetails: royaltyDetails ? royaltyDetails : undefined, + onChainMetadataInputDetails: onChainMetadataInputDetails ? onChainMetadataInputDetails : undefined, + offChainMetadataUploadDetails: offChainMetadataUploadDetails ? offChainMetadataUploadDetails : undefined, + mintingDetails: mintingDetails ? mintingDetails : undefined, + metadataStorageMethod, + } + onDetailsChange(data) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + imageUploadDetails, + collectionDetails, + royaltyDetails, + onChainMetadataInputDetails, + offChainMetadataUploadDetails, + mintingDetails, + ]) + return ( <div> {/* TODO: Cancel once we're able to index on-chain metadata */} @@ -669,6 +705,7 @@ export const OpenEditionMinterCreator = ({ ? Number(minimumUpdatableMintPrice) / 1000000 : Number(minimumMintPrice) / 1000000 } + mintTokenFromFactory={mintTokenFromFactory} onChange={setMintingDetails} uploadMethod={offChainMetadataUploadDetails?.uploadMethod as UploadMethod} /> From 000a67a2f6c517f351e584d6e3e2cce759079917 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Sun, 30 Jul 2023 21:11:10 +0300 Subject: [PATCH 09/51] Update env variables --- .env.example | 8 ++++++-- env.d.ts | 8 ++++++-- utils/constants.ts | 11 +++++++++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index 7ec8697..29d3ca6 100644 --- a/.env.example +++ b/.env.example @@ -14,9 +14,13 @@ NEXT_PUBLIC_VENDING_FACTORY_FLEX_ADDRESS="stars1hvu2ghqkcnvhtj2fc6wuazxt4dqcftsl NEXT_PUBLIC_BASE_FACTORY_ADDRESS="stars1a45hcxty3spnmm2f0papl8v4dk5ew29s4syhn4efte8u5haex99qlkrtnx" NEXT_PUBLIC_BASE_FACTORY_UPDATABLE_ADDRESS="stars100xegx2syry4tclkmejjwxk4nfqahvcqhm9qxut5wxuzhj5d9qfsh5nmym" NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" -NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" -NEXT_PUBLIC_OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS="stars1fk5dkzcylam8mcpqrn8y9spauvc3d4navtaqurcc49dc3p9f8d3qdkvymx" +NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" +NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" +NEXT_PUBLIC_OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" +NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" +NEXT_PUBLIC_OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" +NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" NEXT_PUBLIC_OPEN_EDITION_MINTER_CODE_ID=2579 NEXT_PUBLIC_SG721_NAME_ADDRESS="stars1fx74nkqkw2748av8j7ew7r3xt9cgjqduwn8m0ur5lhe49uhlsasszc5fhr" diff --git a/env.d.ts b/env.d.ts index 77abe04..72e2935 100644 --- a/env.d.ts +++ b/env.d.ts @@ -24,9 +24,13 @@ declare namespace NodeJS { readonly NEXT_PUBLIC_VENDING_MINTER_FLEX_CODE_ID: string readonly NEXT_PUBLIC_VENDING_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS: string - readonly NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS: string - readonly NEXT_PUBLIC_OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_MINTER_CODE_ID: string readonly NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_ADDRESS: string readonly NEXT_PUBLIC_VENDING_FACTORY_FLEX_ADDRESS: string diff --git a/utils/constants.ts b/utils/constants.ts index 81955ff..2f0e607 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -15,9 +15,16 @@ export const VENDING_FACTORY_FLEX_ADDRESS = process.env.NEXT_PUBLIC_VENDING_FACT export const BASE_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_BASE_FACTORY_ADDRESS export const BASE_FACTORY_UPDATABLE_ADDRESS = process.env.NEXT_PUBLIC_BASE_FACTORY_UPDATABLE_ADDRESS export const OPEN_EDITION_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS -export const OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS -export const OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS export const OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS +export const OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS +export const OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS = + process.env.NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS +export const OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS +export const OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS = + process.env.NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS +export const OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS +export const OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS = + process.env.NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS export const OPEN_EDITION_MINTER_CODE_ID = parseInt(process.env.NEXT_PUBLIC_OPEN_EDITION_MINTER_CODE_ID, 10) export const SG721_NAME_ADDRESS = process.env.NEXT_PUBLIC_SG721_NAME_ADDRESS export const BASE_MINTER_CODE_ID = parseInt(process.env.NEXT_PUBLIC_VENDING_MINTER_CODE_ID, 10) From 717fd88e74506d56dba9493cb175d05a20293149 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Sun, 30 Jul 2023 21:11:37 +0300 Subject: [PATCH 10/51] Update minter and token list --- config/minter.ts | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- config/token.ts | 9 ++++++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/config/minter.ts b/config/minter.ts index 4fa2a14..6260da1 100644 --- a/config/minter.ts +++ b/config/minter.ts @@ -2,33 +2,77 @@ import { OPEN_EDITION_FACTORY_ADDRESS, OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS, OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS, + OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS, + OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS, + OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS, + OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS, + OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS, } from 'utils/constants' import type { TokenInfo } from './token' -import { ibcAtom, ibcFrenz, stars } from './token' +import { ibcAtom, ibcFrenz, ibcUsdc, stars } from './token' export interface MinterInfo { id: string factoryAddress: string supportedToken: TokenInfo + updatable?: boolean } export const openEditionStarsMinter: MinterInfo = { id: 'open-edition-stars-minter', factoryAddress: OPEN_EDITION_FACTORY_ADDRESS, supportedToken: stars, + updatable: false, +} + +export const openEditionUpdatableStarsMinter: MinterInfo = { + id: 'open-edition-stars-minter', + factoryAddress: OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS, + supportedToken: stars, + updatable: true, } export const openEditionIbcAtomMinter: MinterInfo = { id: 'open-edition-ibc-atom-minter', factoryAddress: OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS, supportedToken: ibcAtom, + updatable: false, +} + +export const openEditionUpdatableIbcAtomMinter: MinterInfo = { + id: 'open-edition-ibc-atom-minter', + factoryAddress: OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS, + supportedToken: ibcAtom, + updatable: true, +} + +export const openEditionIbcUsdcMinter: MinterInfo = { + id: 'open-edition-ibc-usdc-minter', + factoryAddress: OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS, + supportedToken: ibcUsdc, + updatable: false, +} + +export const openEditionUpdatableIbcUsdcMinter: MinterInfo = { + id: 'open-edition-ibc-usdc-minter', + factoryAddress: OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS, + supportedToken: ibcUsdc, + updatable: false, } export const openEditionIbcFrenzMinter: MinterInfo = { id: 'open-edition-ibc-frenz-minter', factoryAddress: OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS, supportedToken: ibcFrenz, + updatable: false, } -export const minterList = [openEditionStarsMinter, openEditionIbcAtomMinter, openEditionIbcFrenzMinter] +export const openEditionUpdatableIbcFrenzMinter: MinterInfo = { + id: 'open-edition-ibc-frenz-minter', + factoryAddress: OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS, + supportedToken: ibcFrenz, + updatable: true, +} + +export const openEditionMinterList = [openEditionStarsMinter, openEditionIbcAtomMinter, openEditionIbcFrenzMinter] diff --git a/config/token.ts b/config/token.ts index 8f64d82..3119c6d 100644 --- a/config/token.ts +++ b/config/token.ts @@ -21,6 +21,13 @@ export const ibcAtom: TokenInfo = { decimalPlaces: 6, } +export const ibcUsdc: TokenInfo = { + id: 'ibc-usdc', + denom: 'ibc/usdc', + displayName: 'USDC', + decimalPlaces: 6, +} + export const ibcFrenz: TokenInfo = { id: 'ibc-frenz', denom: 'ibc/frenz', @@ -28,4 +35,4 @@ export const ibcFrenz: TokenInfo = { decimalPlaces: 6, } -export const tokensList = [stars, ibcAtom, ibcFrenz] +export const tokensList = [stars, ibcAtom, ibcUsdc, ibcFrenz] From d2d06dffae0899eb6a050123e841132e87d47e76 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 31 Jul 2023 12:25:57 +0300 Subject: [PATCH 11/51] Match selected denom and fetch open edition factory parameters --- components/openEdition/MintingDetails.tsx | 46 ++++++++- config/minter.ts | 19 +++- pages/collections/create.tsx | 108 ++++++++++++++++++---- 3 files changed, 144 insertions(+), 29 deletions(-) diff --git a/components/openEdition/MintingDetails.tsx b/components/openEdition/MintingDetails.tsx index 8b6acd5..5562c43 100644 --- a/components/openEdition/MintingDetails.tsx +++ b/components/openEdition/MintingDetails.tsx @@ -4,6 +4,9 @@ import { FormControl } from 'components/FormControl' import { FormGroup } from 'components/FormGroup' import { useInputState, useNumberInputState } from 'components/forms/FormInput.hooks' import { InputDateTime } from 'components/InputDateTime' +import { openEditionMinterList } from 'config/minter' +import type { TokenInfo } from 'config/token' +import { stars, tokensList } from 'config/token' import React, { useEffect, useState } from 'react' import { resolveAddress } from 'utils/resolveAddress' @@ -15,6 +18,7 @@ interface MintingDetailsProps { onChange: (data: MintingDetailsDataProps) => void uploadMethod: UploadMethod minimumMintPrice: number + mintTokenFromFactory?: TokenInfo | undefined } export interface MintingDetailsDataProps { @@ -23,19 +27,28 @@ export interface MintingDetailsDataProps { startTime: string endTime: string paymentAddress?: string + selectedMintToken?: TokenInfo } -export const MintingDetails = ({ onChange, uploadMethod, minimumMintPrice }: MintingDetailsProps) => { +export const MintingDetails = ({ + onChange, + uploadMethod, + minimumMintPrice, + mintTokenFromFactory, +}: MintingDetailsProps) => { const wallet = useWallet() const [timestamp, setTimestamp] = useState<Date | undefined>() const [endTimestamp, setEndTimestamp] = useState<Date | undefined>() + const [selectedMintToken, setSelectedMintToken] = useState<TokenInfo | undefined>(stars) const unitPriceState = useNumberInputState({ id: 'unitPrice', name: 'unitPrice', - title: 'Unit Price', - subtitle: `Price of each token (min. ${minimumMintPrice} STARS)`, + title: 'Mint Price', + subtitle: `Price of each token (min. ${minimumMintPrice} ${ + mintTokenFromFactory ? mintTokenFromFactory.displayName : 'STARS' + })`, placeholder: '50', }) @@ -76,15 +89,38 @@ export const MintingDetails = ({ onChange, uploadMethod, minimumMintPrice }: Min startTime: timestamp ? (timestamp.getTime() * 1_000_000).toString() : '', endTime: endTimestamp ? (endTimestamp.getTime() * 1_000_000).toString() : '', paymentAddress: paymentAddressState.value.trim(), + selectedMintToken, } onChange(data) // eslint-disable-next-line react-hooks/exhaustive-deps - }, [unitPriceState.value, perAddressLimitState.value, timestamp, endTimestamp, paymentAddressState.value]) + }, [ + unitPriceState.value, + perAddressLimitState.value, + timestamp, + endTimestamp, + paymentAddressState.value, + selectedMintToken, + ]) return ( <div className="border-l-[1px] border-gray-500 border-opacity-20"> <FormGroup subtitle="Information about your minting settings" title="Minting Details"> - <NumberInput {...unitPriceState} isRequired /> + <div className="flex flex-row items-center"> + <NumberInput {...unitPriceState} isRequired /> + <select + className="py-[9px] px-4 mt-14 ml-4 placeholder:text-white/50 bg-white/10 rounded border-2 border-white/20 focus:ring focus:ring-plumbus-20" + onChange={(e) => setSelectedMintToken(tokensList.find((t) => t.displayName === e.target.value))} + > + {openEditionMinterList + .filter((minter) => minter.factoryAddress !== undefined && minter.updatable === false) + .map((minter) => ( + <option key={minter.id} className="bg-black" value={minter.supportedToken.displayName}> + {minter.supportedToken.displayName} + </option> + ))} + </select> + </div> + <NumberInput {...perAddressLimitState} isRequired /> <FormControl htmlId="timestamp" isRequired subtitle="Minting start time (local)" title="Start Time"> <InputDateTime minDate={new Date()} onChange={(date) => setTimestamp(date)} value={timestamp} /> diff --git a/config/minter.ts b/config/minter.ts index 6260da1..413614d 100644 --- a/config/minter.ts +++ b/config/minter.ts @@ -27,7 +27,7 @@ export const openEditionStarsMinter: MinterInfo = { } export const openEditionUpdatableStarsMinter: MinterInfo = { - id: 'open-edition-stars-minter', + id: 'open-edition-updatable-stars-minter', factoryAddress: OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS, supportedToken: stars, updatable: true, @@ -41,7 +41,7 @@ export const openEditionIbcAtomMinter: MinterInfo = { } export const openEditionUpdatableIbcAtomMinter: MinterInfo = { - id: 'open-edition-ibc-atom-minter', + id: 'open-edition-updatable-ibc-atom-minter', factoryAddress: OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS, supportedToken: ibcAtom, updatable: true, @@ -55,7 +55,7 @@ export const openEditionIbcUsdcMinter: MinterInfo = { } export const openEditionUpdatableIbcUsdcMinter: MinterInfo = { - id: 'open-edition-ibc-usdc-minter', + id: 'open-edition-updatable-ibc-usdc-minter', factoryAddress: OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS, supportedToken: ibcUsdc, updatable: false, @@ -69,10 +69,19 @@ export const openEditionIbcFrenzMinter: MinterInfo = { } export const openEditionUpdatableIbcFrenzMinter: MinterInfo = { - id: 'open-edition-ibc-frenz-minter', + id: 'open-edition-updatable-ibc-frenz-minter', factoryAddress: OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS, supportedToken: ibcFrenz, updatable: true, } -export const openEditionMinterList = [openEditionStarsMinter, openEditionIbcAtomMinter, openEditionIbcFrenzMinter] +export const openEditionMinterList = [ + openEditionStarsMinter, + openEditionUpdatableStarsMinter, + openEditionUpdatableIbcAtomMinter, + openEditionIbcAtomMinter, + openEditionIbcFrenzMinter, + openEditionUpdatableIbcFrenzMinter, + openEditionIbcUsdcMinter, + openEditionUpdatableIbcUsdcMinter, +] diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index 6a99323..9493fba 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -33,6 +33,8 @@ import { Conditional } from 'components/Conditional' import { LoadingModal } from 'components/LoadingModal' import type { OpenEditionMinterCreatorDataProps } from 'components/openEdition/OpenEditionMinterCreator' import { OpenEditionMinterCreator } from 'components/openEdition/OpenEditionMinterCreator' +import { openEditionMinterList } from 'config/minter' +import type { TokenInfo } from 'config/token' import { useContracts } from 'contexts/contracts' import { addLogItem } from 'contexts/log' import { useWallet } from 'contexts/wallet' @@ -71,6 +73,8 @@ import { uid } from 'utils/random' import type { MinterType } from '../../components/collections/actions/Combobox' import type { UploadMethod } from '../../components/collections/creation/UploadDetails' import { ConfirmationModal } from '../../components/ConfirmationModal' +import type { OpenEditionMinterDetailsDataProps } from '../../components/openEdition/OpenEditionMinterCreator' +import { tokensList } from '../../config/token' import { getAssetType } from '../../utils/getAssetType' import { isValidAddress } from '../../utils/isValidAddress' @@ -99,7 +103,9 @@ const CollectionCreationPage: NextPage = () => { const [uploadDetails, setUploadDetails] = useState<UploadDetailsDataProps | null>(null) const [collectionDetails, setCollectionDetails] = useState<CollectionDetailsDataProps | null>(null) const [baseMinterDetails, setBaseMinterDetails] = useState<BaseMinterDetailsDataProps | null>(null) - const [openEditionMinterDetails, setOpenEditionMinterDetails] = useState<OpenEditionMinterCreatorDataProps | null>( + const [openEditionMinterCreatorData, setOpenEditionMinterCreatorData] = + useState<OpenEditionMinterCreatorDataProps | null>(null) + const [openEditionMinterDetails, setOpenEditionMinterDetails] = useState<OpenEditionMinterDetailsDataProps | null>( null, ) const [mintingDetails, setMintingDetails] = useState<MintingDetailsDataProps | null>(null) @@ -122,6 +128,10 @@ const CollectionCreationPage: NextPage = () => { const [minimumOpenEditionUpdatableMintPrice, setMinimumOpenEditionUpdatableMintPrice] = useState<string | null>('0') const [minimumFlexMintPrice, setMinimumFlexMintPrice] = useState<string | null>('0') + const [mintTokenFromOpenEditionFactory, setMintTokenFromOpenEditionFactory] = useState<TokenInfo | undefined>( + undefined, + ) + const [uploading, setUploading] = useState(false) const [isMintingComplete, setIsMintingComplete] = useState(false) const [creatingCollection, setCreatingCollection] = useState(false) @@ -1043,7 +1053,7 @@ const CollectionCreationPage: NextPage = () => { } } - const fetchFactoryParameters = async () => { + const fetchInitialFactoryParameters = async () => { const client = wallet.client if (!client) return if (BASE_FACTORY_ADDRESS) { @@ -1100,6 +1110,7 @@ const CollectionCreationPage: NextPage = () => { setVendingMinterFlexCreationFee(vendingFactoryFlexParameters?.params?.creation_fee?.amount) setMinimumFlexMintPrice(vendingFactoryFlexParameters?.params?.min_mint_price?.amount) } + if (OPEN_EDITION_FACTORY_ADDRESS) { const openEditionFactoryParameters = await client .queryContractSmart(OPEN_EDITION_FACTORY_ADDRESS, { params: {} }) @@ -1122,6 +1133,59 @@ const CollectionCreationPage: NextPage = () => { } } + const fetchOpenEditionFactoryParameters = useCallback(async () => { + const client = wallet.client + if (!client) return + const factoryForSelectedDenom = openEditionMinterList.find( + (minter) => + minter.supportedToken === openEditionMinterDetails?.mintingDetails?.selectedMintToken && + minter.updatable === false, + ) + const updatableFactoryForSelectedDenom = openEditionMinterList.find( + (minter) => + minter.supportedToken === openEditionMinterDetails?.mintingDetails?.selectedMintToken && + minter.updatable === true, + ) + if (factoryForSelectedDenom?.factoryAddress) { + const openEditionFactoryParameters = await client + .queryContractSmart(factoryForSelectedDenom.factoryAddress, { params: {} }) + .catch((error) => { + toast.error(`${error.message}`, { style: { maxWidth: 'none' } }) + addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() }) + }) + setOpenEditionMinterCreationFee(openEditionFactoryParameters?.params?.creation_fee?.amount) + if (!openEditionMinterDetails?.collectionDetails?.updatable) { + setMinimumOpenEditionMintPrice(openEditionFactoryParameters?.params?.min_mint_price?.amount) + setMintTokenFromOpenEditionFactory( + tokensList.find((token) => token.denom === openEditionFactoryParameters?.params?.min_mint_price?.denom), + ) + } + console.log('Selected OE Factory: ', factoryForSelectedDenom) + console.log('Selected Updatable OE Factory: ', updatableFactoryForSelectedDenom) + } + if (updatableFactoryForSelectedDenom?.factoryAddress) { + const openEditionUpdatableFactoryParameters = await client + .queryContractSmart(updatableFactoryForSelectedDenom.factoryAddress, { params: {} }) + .catch((error) => { + toast.error(`${error.message}`, { style: { maxWidth: 'none' } }) + addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() }) + }) + setOpenEditionMinterUpdatableCreationFee(openEditionUpdatableFactoryParameters?.params?.creation_fee?.amount) + if (openEditionMinterDetails?.collectionDetails?.updatable) { + setMinimumOpenEditionMintPrice(openEditionUpdatableFactoryParameters?.params?.min_mint_price?.amount) + setMintTokenFromOpenEditionFactory( + tokensList.find( + (token) => token.denom === openEditionUpdatableFactoryParameters?.params?.min_mint_price?.denom, + ), + ) + } + } + }, [ + openEditionMinterDetails?.mintingDetails?.selectedMintToken, + openEditionMinterDetails?.collectionDetails?.updatable, + wallet.client, + ]) + const checkwalletBalance = async () => { const walletBalance = await wallet.client?.getBalance(wallet.address, 'ustars').then((balance) => { if (minterType === 'vending' && whitelistDetails?.whitelistState === 'new' && whitelistDetails.memberLimit) { @@ -1161,25 +1225,25 @@ const CollectionCreationPage: NextPage = () => { const syncCollections = useCallback(async () => { const collectionAddress = - minterType === 'openEdition' ? openEditionMinterDetails?.sg721ContractAddress : sg721ContractAddress + minterType === 'openEdition' ? openEditionMinterCreatorData?.sg721ContractAddress : sg721ContractAddress if (collectionAddress && SYNC_COLLECTIONS_API_URL) { await axios.get(`${SYNC_COLLECTIONS_API_URL}/${collectionAddress}`).catch((error) => { console.error('Sync collections: ', error) }) } - }, [minterType, openEditionMinterDetails?.sg721ContractAddress, sg721ContractAddress]) + }, [minterType, openEditionMinterCreatorData?.sg721ContractAddress, sg721ContractAddress]) useEffect(() => { if ( vendingMinterContractAddress !== null || - openEditionMinterDetails?.openEditionMinterContractAddress || + openEditionMinterCreatorData?.openEditionMinterContractAddress || isMintingComplete ) { scrollRef.current?.scrollIntoView({ behavior: 'smooth' }) } if ( (minterType === 'vending' && vendingMinterContractAddress !== null) || - (minterType === 'openEdition' && openEditionMinterDetails?.openEditionMinterContractAddress) || + (minterType === 'openEdition' && openEditionMinterCreatorData?.openEditionMinterContractAddress) || (minterType === 'base' && vendingMinterContractAddress !== null && isMintingComplete) ) { void syncCollections() @@ -1192,7 +1256,7 @@ const CollectionCreationPage: NextPage = () => { } }, [ vendingMinterContractAddress, - openEditionMinterDetails?.openEditionMinterContractAddress, + openEditionMinterCreatorData?.openEditionMinterContractAddress, isMintingComplete, minterType, syncCollections, @@ -1210,9 +1274,13 @@ const CollectionCreationPage: NextPage = () => { }, [minterType, baseMinterDetails?.baseMinterAcquisitionMethod, uploadDetails?.uploadMethod]) useEffect(() => { - void fetchFactoryParameters() + void fetchInitialFactoryParameters() }, [wallet.client]) + useEffect(() => { + void fetchOpenEditionFactoryParameters() + }, [fetchOpenEditionFactoryParameters]) + return ( <div> <NextSeo @@ -1244,7 +1312,7 @@ const CollectionCreationPage: NextPage = () => { </div> <div className="mx-10" ref={scrollRef}> <Conditional - test={minterType === 'openEdition' && openEditionMinterDetails?.openEditionMinterContractAddress !== null} + test={minterType === 'openEdition' && openEditionMinterCreatorData?.openEditionMinterContractAddress !== null} > <Alert className="mt-5" type="info"> <div> @@ -1253,10 +1321,10 @@ const CollectionCreationPage: NextPage = () => { className="text-stargaze hover:underline" external href={`/contracts/openEditionMinter/query/?contractAddress=${ - openEditionMinterDetails?.openEditionMinterContractAddress as string + openEditionMinterCreatorData?.openEditionMinterContractAddress as string }`} > - {openEditionMinterDetails?.openEditionMinterContractAddress as string} + {openEditionMinterCreatorData?.openEditionMinterContractAddress as string} </Anchor> <br /> SG721 Contract Address:{' '} @@ -1264,10 +1332,10 @@ const CollectionCreationPage: NextPage = () => { className="text-stargaze hover:underline" external href={`/contracts/sg721/query/?contractAddress=${ - openEditionMinterDetails?.sg721ContractAddress as string + openEditionMinterCreatorData?.sg721ContractAddress as string }`} > - {openEditionMinterDetails?.sg721ContractAddress as string} + {openEditionMinterCreatorData?.sg721ContractAddress as string} </Anchor> <br /> Transaction Hash: {' '} @@ -1275,18 +1343,18 @@ const CollectionCreationPage: NextPage = () => { <Anchor className="text-stargaze hover:underline" external - href={`${BLOCK_EXPLORER_URL}/tx/${openEditionMinterDetails?.transactionHash as string}`} + href={`${BLOCK_EXPLORER_URL}/tx/${openEditionMinterCreatorData?.transactionHash as string}`} > - {openEditionMinterDetails?.transactionHash} + {openEditionMinterCreatorData?.transactionHash} </Anchor> </Conditional> <Conditional test={NETWORK === 'mainnet'}> <Anchor className="text-stargaze hover:underline" external - href={`${BLOCK_EXPLORER_URL}/txs/${openEditionMinterDetails?.transactionHash as string}`} + href={`${BLOCK_EXPLORER_URL}/txs/${openEditionMinterCreatorData?.transactionHash as string}`} > - {openEditionMinterDetails?.transactionHash} + {openEditionMinterCreatorData?.transactionHash} </Anchor> </Conditional> <br /> @@ -1295,7 +1363,7 @@ const CollectionCreationPage: NextPage = () => { className="text-white" external href={`${STARGAZE_URL}/launchpad/${ - openEditionMinterDetails?.openEditionMinterContractAddress as string + openEditionMinterCreatorData?.openEditionMinterContractAddress as string }`} > View on Launchpad @@ -1563,8 +1631,10 @@ const CollectionCreationPage: NextPage = () => { <OpenEditionMinterCreator minimumMintPrice={minimumOpenEditionMintPrice as string} minimumUpdatableMintPrice={minimumOpenEditionUpdatableMintPrice as string} + mintTokenFromFactory={mintTokenFromOpenEditionFactory} minterType={minterType} - onChange={setOpenEditionMinterDetails} + onChange={setOpenEditionMinterCreatorData} + onDetailsChange={setOpenEditionMinterDetails} openEditionMinterCreationFee={openEditionMinterCreationFee as string} openEditionMinterUpdatableCreationFee={openEditionMinterUpdatableCreationFee as string} /> From 1bfd1113bbeadb584193c0913133a217f8d4573f Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 31 Jul 2023 16:49:35 +0300 Subject: [PATCH 12/51] Open edition IBC minter creation success --- .../openEdition/OpenEditionMinterCreator.tsx | 26 ++++++++++++++----- config/minter.ts | 2 +- contracts/openEditionFactory/contract.ts | 13 +++------- pages/collections/create.tsx | 7 +++-- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/components/openEdition/OpenEditionMinterCreator.tsx b/components/openEdition/OpenEditionMinterCreator.tsx index eab0869..15af184 100644 --- a/components/openEdition/OpenEditionMinterCreator.tsx +++ b/components/openEdition/OpenEditionMinterCreator.tsx @@ -12,6 +12,7 @@ import type { MinterType } from 'components/collections/actions/Combobox' import { Conditional } from 'components/Conditional' import { ConfirmationModal } from 'components/ConfirmationModal' import { LoadingModal } from 'components/LoadingModal' +import { openEditionMinterList } from 'config/minter' import type { TokenInfo } from 'config/token' import { useContracts } from 'contexts/contracts' import { addLogItem } from 'contexts/log' @@ -90,10 +91,6 @@ export const OpenEditionMinterCreator = ({ const { openEditionMinter: openEditionMinterContract, openEditionFactory: openEditionFactoryContract } = useContracts() - const openEditionFactoryMessages = useMemo( - () => openEditionFactoryContract?.use(OPEN_EDITION_FACTORY_ADDRESS), - [openEditionFactoryContract, wallet.address], - ) const [metadataStorageMethod, setMetadataStorageMethod] = useState<MetadataStorageMethod>('off-chain') const [imageUploadDetails, setImageUploadDetails] = useState<ImageUploadDetailsDataProps | null>(null) const [collectionDetails, setCollectionDetails] = useState<CollectionDetailsDataProps | null>(null) @@ -114,6 +111,21 @@ export const OpenEditionMinterCreator = ({ const [sg721ContractAddress, setSg721ContractAddress] = useState<string | null>(null) const [transactionHash, setTransactionHash] = useState<string | null>(null) + const factoryAddressForSelectedDenom = + openEditionMinterList.find((minter) => minter.supportedToken === mintTokenFromFactory && minter.updatable === false) + ?.factoryAddress || OPEN_EDITION_FACTORY_ADDRESS + const updatableFactoryAddressForSelectedDenom = + openEditionMinterList.find((minter) => minter.supportedToken === mintTokenFromFactory && minter.updatable === true) + ?.factoryAddress || OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS + + const openEditionFactoryMessages = useMemo( + () => + openEditionFactoryContract?.use( + collectionDetails?.updatable ? updatableFactoryAddressForSelectedDenom : factoryAddressForSelectedDenom, + ), + [openEditionFactoryContract, wallet.address], + ) + const performOpenEditionMinterChecks = () => { try { setReadyToCreate(false) @@ -528,7 +540,7 @@ export const OpenEditionMinterCreator = ({ end_time: mintingDetails?.endTime, mint_price: { amount: Number(mintingDetails?.unitPrice).toString(), - denom: 'ustars', + denom: (mintTokenFromFactory?.denom as string) || 'ustars', }, per_address_limit: mintingDetails?.perAddressLimit, payment_address: mintingDetails?.paymentAddress || null, @@ -550,9 +562,9 @@ export const OpenEditionMinterCreator = ({ } console.log('msg: ', msg) - + console.log('Using factory address: ', factoryAddressForSelectedDenom) const payload: OpenEditionFactoryDispatchExecuteArgs = { - contract: collectionDetails?.updatable ? OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS : OPEN_EDITION_FACTORY_ADDRESS, + contract: collectionDetails?.updatable ? updatableFactoryAddressForSelectedDenom : factoryAddressForSelectedDenom, messages: openEditionFactoryMessages, txSigner: wallet.address, msg, diff --git a/config/minter.ts b/config/minter.ts index 413614d..d191f14 100644 --- a/config/minter.ts +++ b/config/minter.ts @@ -58,7 +58,7 @@ export const openEditionUpdatableIbcUsdcMinter: MinterInfo = { id: 'open-edition-updatable-ibc-usdc-minter', factoryAddress: OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS, supportedToken: ibcUsdc, - updatable: false, + updatable: true, } export const openEditionIbcFrenzMinter: MinterInfo = { diff --git a/contracts/openEditionFactory/contract.ts b/contracts/openEditionFactory/contract.ts index d512b8a..7f20216 100644 --- a/contracts/openEditionFactory/contract.ts +++ b/contracts/openEditionFactory/contract.ts @@ -3,7 +3,6 @@ import type { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate' import type { Coin } from '@cosmjs/proto-signing' import type { logs } from '@cosmjs/stargate' -import { OPEN_EDITION_FACTORY_ADDRESS, OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS } from 'utils/constants' export interface CreateOpenEditionMinterResponse { readonly openEditionMinterAddress: string @@ -23,6 +22,7 @@ export interface OpenEditionFactoryInstance { msg: Record<string, unknown>, funds: Coin[], updatable?: boolean, + selectedFactoryAddress?: string, ) => Promise<CreateOpenEditionMinterResponse> } @@ -56,16 +56,9 @@ export const openEditionFactory = (client: SigningCosmWasmClient, txSigner: stri senderAddress: string, msg: Record<string, unknown>, funds: Coin[], - updatable?: boolean, ): Promise<CreateOpenEditionMinterResponse> => { - const result = await client.execute( - senderAddress, - updatable ? OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS : OPEN_EDITION_FACTORY_ADDRESS, - msg, - 'auto', - '', - funds, - ) + console.log('Contract Address: ', contractAddress) + const result = await client.execute(senderAddress, contractAddress, msg, 'auto', '', funds) return { openEditionMinterAddress: result.logs[0].events[5].attributes[0].value, diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index 9493fba..42573f7 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -74,7 +74,7 @@ import type { MinterType } from '../../components/collections/actions/Combobox' import type { UploadMethod } from '../../components/collections/creation/UploadDetails' import { ConfirmationModal } from '../../components/ConfirmationModal' import type { OpenEditionMinterDetailsDataProps } from '../../components/openEdition/OpenEditionMinterCreator' -import { tokensList } from '../../config/token' +import { stars, tokensList } from '../../config/token' import { getAssetType } from '../../utils/getAssetType' import { isValidAddress } from '../../utils/isValidAddress' @@ -128,9 +128,7 @@ const CollectionCreationPage: NextPage = () => { const [minimumOpenEditionUpdatableMintPrice, setMinimumOpenEditionUpdatableMintPrice] = useState<string | null>('0') const [minimumFlexMintPrice, setMinimumFlexMintPrice] = useState<string | null>('0') - const [mintTokenFromOpenEditionFactory, setMintTokenFromOpenEditionFactory] = useState<TokenInfo | undefined>( - undefined, - ) + const [mintTokenFromOpenEditionFactory, setMintTokenFromOpenEditionFactory] = useState<TokenInfo | undefined>(stars) const [uploading, setUploading] = useState(false) const [isMintingComplete, setIsMintingComplete] = useState(false) @@ -1153,6 +1151,7 @@ const CollectionCreationPage: NextPage = () => { toast.error(`${error.message}`, { style: { maxWidth: 'none' } }) addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() }) }) + console.log('Open Edition Factory Parameters: ', openEditionFactoryParameters) setOpenEditionMinterCreationFee(openEditionFactoryParameters?.params?.creation_fee?.amount) if (!openEditionMinterDetails?.collectionDetails?.updatable) { setMinimumOpenEditionMintPrice(openEditionFactoryParameters?.params?.min_mint_price?.amount) From 4f473b6bf818809d9a4986b2240010c10c9927c7 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 31 Jul 2023 17:24:04 +0300 Subject: [PATCH 13/51] Display the right denom in minimum mint price not met error --- components/openEdition/OpenEditionMinterCreator.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/openEdition/OpenEditionMinterCreator.tsx b/components/openEdition/OpenEditionMinterCreator.tsx index 15af184..0db516e 100644 --- a/components/openEdition/OpenEditionMinterCreator.tsx +++ b/components/openEdition/OpenEditionMinterCreator.tsx @@ -284,10 +284,16 @@ export const OpenEditionMinterCreator = ({ if (collectionDetails?.updatable) { if (Number(mintingDetails.unitPrice) < Number(minimumUpdatableMintPrice)) throw new Error( - `Invalid mint price: The minimum mint price is ${Number(minimumUpdatableMintPrice) / 1000000} STARS`, + `Invalid mint price: The minimum mint price is ${Number(minimumUpdatableMintPrice) / 1000000} ${ + mintTokenFromFactory?.displayName + }`, ) } else if (Number(mintingDetails.unitPrice) < Number(minimumMintPrice)) - throw new Error(`Invalid mint price: The minimum mint price is ${Number(minimumMintPrice) / 1000000} STARS`) + throw new Error( + `Invalid mint price: The minimum mint price is ${Number(minimumMintPrice) / 1000000} ${ + mintTokenFromFactory?.displayName + }`, + ) if (!mintingDetails.perAddressLimit || mintingDetails.perAddressLimit < 1 || mintingDetails.perAddressLimit > 50) throw new Error('Invalid limit for tokens per address') if (mintingDetails.startTime === '') throw new Error('Start time is required') From ce477c8b768702dc21b56dc1551e695331ededb6 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Tue, 1 Aug 2023 11:14:22 +0300 Subject: [PATCH 14/51] Include vending minters in the minter list --- .env.example | 4 ++++ config/minter.ts | 57 ++++++++++++++++++++++++++++++++++++++++++++++ env.d.ts | 4 ++++ utils/constants.ts | 6 +++++ 4 files changed, 71 insertions(+) diff --git a/.env.example b/.env.example index 29d3ca6..538335d 100644 --- a/.env.example +++ b/.env.example @@ -11,6 +11,10 @@ NEXT_PUBLIC_BASE_MINTER_CODE_ID=2598 NEXT_PUBLIC_VENDING_FACTORY_ADDRESS="stars18h7ugh8eaug7wr0w4yjw0ls5s937z35pnkg935ucsek2y9xl3gaqqk4jtx" NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_ADDRESS="stars1h65nms9gwg4vdktyqj84tu50gwlm34e0eczl5w2ezllxuzfxy9esa9qlt0" NEXT_PUBLIC_VENDING_FACTORY_FLEX_ADDRESS="stars1hvu2ghqkcnvhtj2fc6wuazxt4dqcftslp2rwkkkcxy269a35a9pq60ug2q" +NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" +NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" +NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" +NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" NEXT_PUBLIC_BASE_FACTORY_ADDRESS="stars1a45hcxty3spnmm2f0papl8v4dk5ew29s4syhn4efte8u5haex99qlkrtnx" NEXT_PUBLIC_BASE_FACTORY_UPDATABLE_ADDRESS="stars100xegx2syry4tclkmejjwxk4nfqahvcqhm9qxut5wxuzhj5d9qfsh5nmym" NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" diff --git a/config/minter.ts b/config/minter.ts index d191f14..b7d359f 100644 --- a/config/minter.ts +++ b/config/minter.ts @@ -7,6 +7,12 @@ import { OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS, OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS, OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS, + VENDING_FACTORY_ADDRESS, + VENDING_FACTORY_UPDATABLE_ADDRESS, + VENDING_IBC_ATOM_FACTORY_ADDRESS, + VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS, + VENDING_IBC_USDC_FACTORY_ADDRESS, + VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS, } from 'utils/constants' import type { TokenInfo } from './token' @@ -85,3 +91,54 @@ export const openEditionMinterList = [ openEditionIbcUsdcMinter, openEditionUpdatableIbcUsdcMinter, ] + +export const vendingStarsMinter: MinterInfo = { + id: 'vending-stars-minter', + factoryAddress: VENDING_FACTORY_ADDRESS, + supportedToken: stars, + updatable: false, +} + +export const vendingUpdatableStarsMinter: MinterInfo = { + id: 'vending-updatable-stars-minter', + factoryAddress: VENDING_FACTORY_UPDATABLE_ADDRESS, + supportedToken: stars, + updatable: true, +} + +export const vendingIbcAtomMinter: MinterInfo = { + id: 'vending-ibc-atom-minter', + factoryAddress: VENDING_IBC_ATOM_FACTORY_ADDRESS, + supportedToken: ibcAtom, + updatable: false, +} + +export const vendingUpdatableIbcAtomMinter: MinterInfo = { + id: 'vending-updatable-ibc-atom-minter', + factoryAddress: VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS, + supportedToken: ibcAtom, + updatable: true, +} + +export const vendingIbcUsdcMinter: MinterInfo = { + id: 'vending-ibc-usdc-minter', + factoryAddress: VENDING_IBC_USDC_FACTORY_ADDRESS, + supportedToken: ibcUsdc, + updatable: false, +} + +export const vendingUpdatableIbcUsdcMinter: MinterInfo = { + id: 'vending-updatable-ibc-usdc-minter', + factoryAddress: VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS, + supportedToken: ibcUsdc, + updatable: true, +} + +export const vendingMinterList = [ + vendingStarsMinter, + vendingUpdatableStarsMinter, + vendingIbcAtomMinter, + vendingUpdatableIbcAtomMinter, + vendingIbcUsdcMinter, + vendingUpdatableIbcUsdcMinter, +] diff --git a/env.d.ts b/env.d.ts index 72e2935..1325002 100644 --- a/env.d.ts +++ b/env.d.ts @@ -23,6 +23,10 @@ declare namespace NodeJS { readonly NEXT_PUBLIC_VENDING_MINTER_CODE_ID: string readonly NEXT_PUBLIC_VENDING_MINTER_FLEX_CODE_ID: string readonly NEXT_PUBLIC_VENDING_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS: string diff --git a/utils/constants.ts b/utils/constants.ts index 2f0e607..01b8a8a 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -12,6 +12,12 @@ export const VENDING_MINTER_FLEX_CODE_ID = parseInt(process.env.NEXT_PUBLIC_VEND export const VENDING_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_VENDING_FACTORY_ADDRESS export const VENDING_FACTORY_UPDATABLE_ADDRESS = process.env.NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_ADDRESS export const VENDING_FACTORY_FLEX_ADDRESS = process.env.NEXT_PUBLIC_VENDING_FACTORY_FLEX_ADDRESS +export const VENDING_IBC_ATOM_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_ADDRESS +export const VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS = + process.env.NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS +export const VENDING_IBC_USDC_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_ADDRESS +export const VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS = + process.env.NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS export const BASE_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_BASE_FACTORY_ADDRESS export const BASE_FACTORY_UPDATABLE_ADDRESS = process.env.NEXT_PUBLIC_BASE_FACTORY_UPDATABLE_ADDRESS export const OPEN_EDITION_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS From 3621a7363e3f2ea4bb95d1c26856a8b01077f3cd Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Tue, 1 Aug 2023 11:26:24 +0300 Subject: [PATCH 15/51] Add mint denom selection for standard collections --- .../collections/creation/MintingDetails.tsx | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/components/collections/creation/MintingDetails.tsx b/components/collections/creation/MintingDetails.tsx index 6ca136e..e6555f1 100644 --- a/components/collections/creation/MintingDetails.tsx +++ b/components/collections/creation/MintingDetails.tsx @@ -4,6 +4,9 @@ import { FormControl } from 'components/FormControl' import { FormGroup } from 'components/FormGroup' import { useInputState, useNumberInputState } from 'components/forms/FormInput.hooks' import { InputDateTime } from 'components/InputDateTime' +import { vendingMinterList } from 'config/minter' +import type { TokenInfo } from 'config/token' +import { stars, tokensList } from 'config/token' import React, { useEffect, useState } from 'react' import { resolveAddress } from 'utils/resolveAddress' @@ -24,12 +27,14 @@ export interface MintingDetailsDataProps { perAddressLimit: number startTime: string paymentAddress?: string + selectedMintToken?: TokenInfo } export const MintingDetails = ({ onChange, numberOfTokens, uploadMethod, minimumMintPrice }: MintingDetailsProps) => { const wallet = useWallet() const [timestamp, setTimestamp] = useState<Date | undefined>() + const [selectedMintToken, setSelectedMintToken] = useState<TokenInfo | undefined>(stars) const numberOfTokensState = useNumberInputState({ id: 'numberoftokens', @@ -85,6 +90,7 @@ export const MintingDetails = ({ onChange, numberOfTokens, uploadMethod, minimum perAddressLimit: perAddressLimitState.value, startTime: timestamp ? (timestamp.getTime() * 1_000_000).toString() : '', paymentAddress: paymentAddressState.value.trim(), + selectedMintToken, } onChange(data) // eslint-disable-next-line react-hooks/exhaustive-deps @@ -95,6 +101,7 @@ export const MintingDetails = ({ onChange, numberOfTokens, uploadMethod, minimum perAddressLimitState.value, timestamp, paymentAddressState.value, + selectedMintToken, ]) return ( @@ -106,7 +113,22 @@ export const MintingDetails = ({ onChange, numberOfTokens, uploadMethod, minimum isRequired value={uploadMethod === 'new' ? numberOfTokens : numberOfTokensState.value} /> - <NumberInput {...unitPriceState} isRequired /> + <div className="flex flex-row items-center"> + <NumberInput {...unitPriceState} isRequired /> + <select + className="py-[9px] px-4 mt-14 ml-4 placeholder:text-white/50 bg-white/10 rounded border-2 border-white/20 focus:ring focus:ring-plumbus-20" + onChange={(e) => setSelectedMintToken(tokensList.find((t) => t.displayName === e.target.value))} + > + {vendingMinterList + .filter((minter) => minter.factoryAddress !== undefined && minter.updatable === false) + .map((minter) => ( + <option key={minter.id} className="bg-black" value={minter.supportedToken.displayName}> + {minter.supportedToken.displayName} + </option> + ))} + </select> + </div> + <NumberInput {...perAddressLimitState} isRequired /> <FormControl htmlId="timestamp" isRequired subtitle="Minting start time (local)" title="Start Time"> <InputDateTime minDate={new Date()} onChange={(date) => setTimestamp(date)} value={timestamp} /> From 180eb914b3025a904d521bd9ef73a9cae3109b5b Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Wed, 2 Aug 2023 22:56:41 +0300 Subject: [PATCH 16/51] Fetch vending factory parameters wrt selected mint denom --- pages/collections/create.tsx | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index 42573f7..d03115e 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -33,7 +33,7 @@ import { Conditional } from 'components/Conditional' import { LoadingModal } from 'components/LoadingModal' import type { OpenEditionMinterCreatorDataProps } from 'components/openEdition/OpenEditionMinterCreator' import { OpenEditionMinterCreator } from 'components/openEdition/OpenEditionMinterCreator' -import { openEditionMinterList } from 'config/minter' +import { flexibleVendingMinterList, openEditionMinterList, vendingMinterList } from 'config/minter' import type { TokenInfo } from 'config/token' import { useContracts } from 'contexts/contracts' import { addLogItem } from 'contexts/log' @@ -129,6 +129,7 @@ const CollectionCreationPage: NextPage = () => { const [minimumFlexMintPrice, setMinimumFlexMintPrice] = useState<string | null>('0') const [mintTokenFromOpenEditionFactory, setMintTokenFromOpenEditionFactory] = useState<TokenInfo | undefined>(stars) + const [mintTokenFromVendingFactory, setMintTokenFromVendingFactory] = useState<TokenInfo | undefined>(stars) const [uploading, setUploading] = useState(false) const [isMintingComplete, setIsMintingComplete] = useState(false) @@ -540,7 +541,7 @@ const CollectionCreationPage: NextPage = () => { payment_address: mintingDetails?.paymentAddress ? mintingDetails.paymentAddress : undefined, mint_price: { amount: mintingDetails?.unitPrice, - denom: 'ustars', + denom: (mintTokenFromVendingFactory?.denom as string) || 'ustars', }, per_address_limit: mintingDetails?.perAddressLimit, whitelist, @@ -1185,6 +1186,34 @@ const CollectionCreationPage: NextPage = () => { wallet.client, ]) + const fetchVendingFactoryParameters = useCallback(async () => { + const client = wallet.client + if (!client) return + const vendingFactoryForSelectedDenom = vendingMinterList + .concat(flexibleVendingMinterList) + .find( + (minter) => + minter.supportedToken === mintingDetails?.selectedMintToken && + minter.updatable === collectionDetails?.updatable && + minter.flexible === (whitelistDetails?.whitelistType === 'flex'), + )?.factoryAddress + if (vendingFactoryForSelectedDenom) { + const vendingFactoryParameters = await client + .queryContractSmart(vendingFactoryForSelectedDenom, { params: {} }) + .catch((error) => { + toast.error(`${error.message}`, { style: { maxWidth: 'none' } }) + addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() }) + }) + setVendingMinterCreationFee(vendingFactoryParameters?.params?.creation_fee?.amount) + if (!collectionDetails?.updatable) { + setMinimumMintPrice(vendingFactoryParameters?.params?.min_mint_price?.amount) + setMintTokenFromVendingFactory( + tokensList.find((token) => token.denom === vendingFactoryParameters?.params?.min_mint_price?.denom), + ) + } + } + }, [collectionDetails?.updatable, mintingDetails?.selectedMintToken, tokensList, whitelistDetails?.whitelistType]) + const checkwalletBalance = async () => { const walletBalance = await wallet.client?.getBalance(wallet.address, 'ustars').then((balance) => { if (minterType === 'vending' && whitelistDetails?.whitelistState === 'new' && whitelistDetails.memberLimit) { From 15427072e4acfa7e83716aa06c431637540b08bf Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Sun, 6 Aug 2023 16:53:02 +0300 Subject: [PATCH 17/51] Add flexible factories to the minter list --- .env.example | 4 +++ config/minter.ts | 70 ++++++++++++++++++++++++++++++++++++++++++++++ env.d.ts | 9 ++++-- utils/constants.ts | 7 +++++ 4 files changed, 88 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 538335d..45ed4b3 100644 --- a/.env.example +++ b/.env.example @@ -15,6 +15,10 @@ NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" +NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_FLEX_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" +NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_FLEX_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" +NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" +NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_FLEX_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" NEXT_PUBLIC_BASE_FACTORY_ADDRESS="stars1a45hcxty3spnmm2f0papl8v4dk5ew29s4syhn4efte8u5haex99qlkrtnx" NEXT_PUBLIC_BASE_FACTORY_UPDATABLE_ADDRESS="stars100xegx2syry4tclkmejjwxk4nfqahvcqhm9qxut5wxuzhj5d9qfsh5nmym" NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" diff --git a/config/minter.ts b/config/minter.ts index b7d359f..02ebc5b 100644 --- a/config/minter.ts +++ b/config/minter.ts @@ -8,11 +8,17 @@ import { OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS, OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS, VENDING_FACTORY_ADDRESS, + VENDING_FACTORY_FLEX_ADDRESS, VENDING_FACTORY_UPDATABLE_ADDRESS, + VENDING_FACTORY_UPDATABLE_FLEX_ADDRESS, VENDING_IBC_ATOM_FACTORY_ADDRESS, + VENDING_IBC_ATOM_FACTORY_FLEX_ADDRESS, VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS, + VENDING_IBC_ATOM_UPDATABLE_FACTORY_FLEX_ADDRESS, VENDING_IBC_USDC_FACTORY_ADDRESS, + VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS, VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS, + VENDING_IBC_USDC_UPDATABLE_FACTORY_FLEX_ADDRESS, } from 'utils/constants' import type { TokenInfo } from './token' @@ -23,6 +29,7 @@ export interface MinterInfo { factoryAddress: string supportedToken: TokenInfo updatable?: boolean + flexible?: boolean } export const openEditionStarsMinter: MinterInfo = { @@ -97,6 +104,7 @@ export const vendingStarsMinter: MinterInfo = { factoryAddress: VENDING_FACTORY_ADDRESS, supportedToken: stars, updatable: false, + flexible: false, } export const vendingUpdatableStarsMinter: MinterInfo = { @@ -104,6 +112,7 @@ export const vendingUpdatableStarsMinter: MinterInfo = { factoryAddress: VENDING_FACTORY_UPDATABLE_ADDRESS, supportedToken: stars, updatable: true, + flexible: false, } export const vendingIbcAtomMinter: MinterInfo = { @@ -111,6 +120,7 @@ export const vendingIbcAtomMinter: MinterInfo = { factoryAddress: VENDING_IBC_ATOM_FACTORY_ADDRESS, supportedToken: ibcAtom, updatable: false, + flexible: false, } export const vendingUpdatableIbcAtomMinter: MinterInfo = { @@ -118,6 +128,7 @@ export const vendingUpdatableIbcAtomMinter: MinterInfo = { factoryAddress: VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS, supportedToken: ibcAtom, updatable: true, + flexible: false, } export const vendingIbcUsdcMinter: MinterInfo = { @@ -125,6 +136,7 @@ export const vendingIbcUsdcMinter: MinterInfo = { factoryAddress: VENDING_IBC_USDC_FACTORY_ADDRESS, supportedToken: ibcUsdc, updatable: false, + flexible: false, } export const vendingUpdatableIbcUsdcMinter: MinterInfo = { @@ -132,6 +144,7 @@ export const vendingUpdatableIbcUsdcMinter: MinterInfo = { factoryAddress: VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS, supportedToken: ibcUsdc, updatable: true, + flexible: false, } export const vendingMinterList = [ @@ -142,3 +155,60 @@ export const vendingMinterList = [ vendingIbcUsdcMinter, vendingUpdatableIbcUsdcMinter, ] + +export const flexibleVendingStarsMinter: MinterInfo = { + id: 'flexible-vending-stars-minter', + factoryAddress: VENDING_FACTORY_FLEX_ADDRESS, + supportedToken: stars, + updatable: false, + flexible: true, +} + +export const flexibleVendingUpdatableStarsMinter: MinterInfo = { + id: 'flexible-vending-updatable-stars-minter', + factoryAddress: VENDING_FACTORY_UPDATABLE_FLEX_ADDRESS, + supportedToken: stars, + updatable: true, + flexible: true, +} + +export const flexibleVendingIbcAtomMinter: MinterInfo = { + id: 'flexible-vending-ibc-atom-minter', + factoryAddress: VENDING_IBC_ATOM_FACTORY_FLEX_ADDRESS, + supportedToken: ibcAtom, + updatable: false, + flexible: true, +} + +export const flexibleVendingUpdatableIbcAtomMinter: MinterInfo = { + id: 'flexible-vending-updatable-ibc-atom-minter', + factoryAddress: VENDING_IBC_ATOM_UPDATABLE_FACTORY_FLEX_ADDRESS, + supportedToken: ibcAtom, + updatable: true, + flexible: true, +} + +export const flexibleVendingIbcUsdcMinter: MinterInfo = { + id: 'flexible-vending-ibc-usdc-minter', + factoryAddress: VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS, + supportedToken: ibcUsdc, + updatable: false, + flexible: true, +} + +export const flexibleVendingUpdatableIbcUsdcMinter: MinterInfo = { + id: 'flexible-vending-updatable-ibc-usdc-minter', + factoryAddress: VENDING_IBC_USDC_UPDATABLE_FACTORY_FLEX_ADDRESS, + supportedToken: ibcUsdc, + updatable: true, + flexible: true, +} + +export const flexibleVendingMinterList = [ + flexibleVendingStarsMinter, + flexibleVendingUpdatableStarsMinter, + flexibleVendingIbcAtomMinter, + flexibleVendingUpdatableIbcAtomMinter, + flexibleVendingIbcUsdcMinter, + flexibleVendingUpdatableIbcUsdcMinter, +] diff --git a/env.d.ts b/env.d.ts index 1325002..d41c8e5 100644 --- a/env.d.ts +++ b/env.d.ts @@ -23,10 +23,17 @@ declare namespace NodeJS { readonly NEXT_PUBLIC_VENDING_MINTER_CODE_ID: string readonly NEXT_PUBLIC_VENDING_MINTER_FLEX_CODE_ID: string readonly NEXT_PUBLIC_VENDING_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_ADDRESS: string + readonly NEXT_PUBLIC_VENDING_FACTORY_FLEX_ADDRESS: string + readonly NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_FLEX_ADDRESS: string readonly NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_FLEX_ADDRESS: string + readonly NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_FLEX_ADDRESS: string + readonly NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS: string + readonly NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_FLEX_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS: string @@ -36,8 +43,6 @@ declare namespace NodeJS { readonly NEXT_PUBLIC_OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_MINTER_CODE_ID: string - readonly NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_ADDRESS: string - readonly NEXT_PUBLIC_VENDING_FACTORY_FLEX_ADDRESS: string readonly NEXT_PUBLIC_BASE_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_BASE_FACTORY_UPDATABLE_ADDRESS: string readonly NEXT_PUBLIC_SG721_NAME_ADDRESS: string diff --git a/utils/constants.ts b/utils/constants.ts index 01b8a8a..8b919ed 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -12,12 +12,19 @@ export const VENDING_MINTER_FLEX_CODE_ID = parseInt(process.env.NEXT_PUBLIC_VEND export const VENDING_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_VENDING_FACTORY_ADDRESS export const VENDING_FACTORY_UPDATABLE_ADDRESS = process.env.NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_ADDRESS export const VENDING_FACTORY_FLEX_ADDRESS = process.env.NEXT_PUBLIC_VENDING_FACTORY_FLEX_ADDRESS +export const VENDING_FACTORY_UPDATABLE_FLEX_ADDRESS = process.env.NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_FLEX_ADDRESS export const VENDING_IBC_ATOM_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_ADDRESS export const VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS export const VENDING_IBC_USDC_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_ADDRESS export const VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS +export const VENDING_IBC_ATOM_FACTORY_FLEX_ADDRESS = process.env.NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_FLEX_ADDRESS +export const VENDING_IBC_ATOM_UPDATABLE_FACTORY_FLEX_ADDRESS = + process.env.NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_FLEX_ADDRESS +export const VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS = process.env.NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS +export const VENDING_IBC_USDC_UPDATABLE_FACTORY_FLEX_ADDRESS = + process.env.NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_FLEX_ADDRESS export const BASE_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_BASE_FACTORY_ADDRESS export const BASE_FACTORY_UPDATABLE_ADDRESS = process.env.NEXT_PUBLIC_BASE_FACTORY_UPDATABLE_ADDRESS export const OPEN_EDITION_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS From ade9410a91764ec618c85d5ba934bdf1abdef3ab Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Sun, 6 Aug 2023 17:24:51 +0300 Subject: [PATCH 18/51] Fetch and display factory denom for minimum mint price --- components/collections/creation/MintingDetails.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/components/collections/creation/MintingDetails.tsx b/components/collections/creation/MintingDetails.tsx index e6555f1..22f802f 100644 --- a/components/collections/creation/MintingDetails.tsx +++ b/components/collections/creation/MintingDetails.tsx @@ -19,6 +19,7 @@ interface MintingDetailsProps { numberOfTokens: number | undefined uploadMethod: UploadMethod minimumMintPrice: number + mintingTokenFromFactory?: TokenInfo } export interface MintingDetailsDataProps { @@ -30,7 +31,13 @@ export interface MintingDetailsDataProps { selectedMintToken?: TokenInfo } -export const MintingDetails = ({ onChange, numberOfTokens, uploadMethod, minimumMintPrice }: MintingDetailsProps) => { +export const MintingDetails = ({ + onChange, + numberOfTokens, + uploadMethod, + minimumMintPrice, + mintingTokenFromFactory, +}: MintingDetailsProps) => { const wallet = useWallet() const [timestamp, setTimestamp] = useState<Date | undefined>() @@ -48,7 +55,9 @@ export const MintingDetails = ({ onChange, numberOfTokens, uploadMethod, minimum id: 'unitPrice', name: 'unitPrice', title: 'Unit Price', - subtitle: `Price of each token (min. ${minimumMintPrice} STARS)`, + subtitle: `Price of each token (min. ${minimumMintPrice} ${ + mintingTokenFromFactory ? mintingTokenFromFactory.displayName : 'STARS' + })`, placeholder: '50', }) From 8aaff3823877825f18ffc1c99e021abf5597e4ca Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Sun, 6 Aug 2023 18:44:22 +0300 Subject: [PATCH 19/51] Instantiate vending minter wrt selected denom --- contracts/vendingFactory/contract.ts | 14 +---- pages/collections/create.tsx | 90 ++++++++++++++++++---------- 2 files changed, 60 insertions(+), 44 deletions(-) diff --git a/contracts/vendingFactory/contract.ts b/contracts/vendingFactory/contract.ts index 30f59d0..af41418 100644 --- a/contracts/vendingFactory/contract.ts +++ b/contracts/vendingFactory/contract.ts @@ -1,11 +1,8 @@ /* eslint-disable eslint-comments/disable-enable-pair */ -/* eslint-disable no-nested-ternary */ + import type { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate' import type { Coin } from '@cosmjs/proto-signing' import type { logs } from '@cosmjs/stargate' -import { VENDING_FACTORY_ADDRESS, VENDING_FACTORY_FLEX_ADDRESS } from 'utils/constants' - -import { VENDING_FACTORY_UPDATABLE_ADDRESS } from '../../utils/constants' export interface CreateVendingMinterResponse { readonly vendingMinterAddress: string @@ -63,14 +60,7 @@ export const vendingFactory = (client: SigningCosmWasmClient, txSigner: string): updatable?: boolean, flex?: boolean, ): Promise<CreateVendingMinterResponse> => { - const result = await client.execute( - senderAddress, - flex ? VENDING_FACTORY_FLEX_ADDRESS : updatable ? VENDING_FACTORY_UPDATABLE_ADDRESS : VENDING_FACTORY_ADDRESS, - msg, - 'auto', - '', - funds, - ) + const result = await client.execute(senderAddress, contractAddress, msg, 'auto', '', funds) return { vendingMinterAddress: result.logs[0].events[5].attributes[0].value, diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index d03115e..c29bd28 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -90,16 +90,6 @@ const CollectionCreationPage: NextPage = () => { const scrollRef = useRef<HTMLDivElement>(null) const sidetabRef = useRef<any>(null) - const vendingFactoryMessages = useMemo( - () => vendingFactoryContract?.use(VENDING_FACTORY_ADDRESS), - [vendingFactoryContract, wallet.address], - ) - - const baseFactoryMessages = useMemo( - () => baseFactoryContract?.use(BASE_FACTORY_ADDRESS), - [baseFactoryContract, wallet.address], - ) - const [uploadDetails, setUploadDetails] = useState<UploadDetailsDataProps | null>(null) const [collectionDetails, setCollectionDetails] = useState<CollectionDetailsDataProps | null>(null) const [baseMinterDetails, setBaseMinterDetails] = useState<BaseMinterDetailsDataProps | null>(null) @@ -130,6 +120,17 @@ const CollectionCreationPage: NextPage = () => { const [mintTokenFromOpenEditionFactory, setMintTokenFromOpenEditionFactory] = useState<TokenInfo | undefined>(stars) const [mintTokenFromVendingFactory, setMintTokenFromVendingFactory] = useState<TokenInfo | undefined>(stars) + const [vendingFactoryAddress, setVendingFactoryAddress] = useState<string | null>(VENDING_FACTORY_ADDRESS) + + const vendingFactoryMessages = useMemo( + () => vendingFactoryContract?.use(vendingFactoryAddress as string), + [vendingFactoryContract, wallet.address], + ) + + const baseFactoryMessages = useMemo( + () => baseFactoryContract?.use(BASE_FACTORY_ADDRESS), + [baseFactoryContract, wallet.address], + ) const [uploading, setUploading] = useState(false) const [isMintingComplete, setIsMintingComplete] = useState(false) @@ -547,11 +548,7 @@ const CollectionCreationPage: NextPage = () => { whitelist, }, collection_params: { - code_id: collectionDetails?.updatable - ? whitelistDetails?.whitelistType === 'flex' - ? SG721_CODE_ID - : SG721_UPDATABLE_CODE_ID - : SG721_CODE_ID, + code_id: collectionDetails?.updatable ? SG721_CODE_ID : SG721_UPDATABLE_CODE_ID, name: collectionDetails?.name, symbol: collectionDetails?.symbol, info: { @@ -572,12 +569,7 @@ const CollectionCreationPage: NextPage = () => { } const payload: VendingFactoryDispatchExecuteArgs = { - contract: - whitelistDetails?.whitelistState !== 'none' && whitelistDetails?.whitelistType === 'flex' - ? VENDING_FACTORY_FLEX_ADDRESS - : collectionDetails?.updatable - ? VENDING_FACTORY_UPDATABLE_ADDRESS - : VENDING_FACTORY_ADDRESS, + contract: vendingFactoryAddress as string, messages: vendingFactoryMessages, txSigner: wallet.address, msg, @@ -912,14 +904,24 @@ const CollectionCreationPage: NextPage = () => { if (mintingDetails.unitPrice === '') throw new Error('Public mint price is required') if (whitelistDetails?.whitelistState !== 'none' && whitelistDetails?.whitelistType === 'flex') { if (Number(mintingDetails.unitPrice) < Number(minimumFlexMintPrice)) - throw new Error(`Invalid unit price: The minimum unit price is ${Number(minimumFlexMintPrice) / 1000000} STARS`) + throw new Error( + `Invalid unit price: The minimum unit price is ${Number(minimumFlexMintPrice) / 1000000} ${ + mintTokenFromVendingFactory ? mintTokenFromVendingFactory.displayName : 'STARS' + }`, + ) } else if (collectionDetails?.updatable) { if (Number(mintingDetails.unitPrice) < Number(minimumUpdatableMintPrice)) throw new Error( - `Invalid unit price: The minimum unit price is ${Number(minimumUpdatableMintPrice) / 1000000} STARS`, + `Invalid unit price: The minimum unit price is ${Number(minimumUpdatableMintPrice) / 1000000} ${ + mintTokenFromVendingFactory ? mintTokenFromVendingFactory.displayName : 'STARS' + }`, ) } else if (Number(mintingDetails.unitPrice) < Number(minimumMintPrice)) - throw new Error(`Invalid unit price: The minimum unit price is ${Number(minimumMintPrice) / 1000000} STARS`) + throw new Error( + `Invalid unit price: The minimum unit price is ${Number(minimumMintPrice) / 1000000} ${ + mintTokenFromVendingFactory ? mintTokenFromVendingFactory.displayName : 'STARS' + }`, + ) if ( !mintingDetails.perAddressLimit || mintingDetails.perAddressLimit < 1 || @@ -1128,7 +1130,7 @@ const CollectionCreationPage: NextPage = () => { addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() }) }) setOpenEditionMinterUpdatableCreationFee(openEditionUpdatableFactoryParameters?.params?.creation_fee?.amount) - setMinimumOpenEditionMintPrice(openEditionUpdatableFactoryParameters?.params?.min_mint_price?.amount) + setMinimumOpenEditionUpdatableMintPrice(openEditionUpdatableFactoryParameters?.params?.min_mint_price?.amount) } } @@ -1187,8 +1189,10 @@ const CollectionCreationPage: NextPage = () => { ]) const fetchVendingFactoryParameters = useCallback(async () => { + console.log('Here') const client = wallet.client if (!client) return + console.log('Selected Token: ', mintingDetails?.selectedMintToken) const vendingFactoryForSelectedDenom = vendingMinterList .concat(flexibleVendingMinterList) .find( @@ -1197,22 +1201,38 @@ const CollectionCreationPage: NextPage = () => { minter.updatable === collectionDetails?.updatable && minter.flexible === (whitelistDetails?.whitelistType === 'flex'), )?.factoryAddress + console.log(vendingFactoryForSelectedDenom) if (vendingFactoryForSelectedDenom) { + setVendingFactoryAddress(vendingFactoryForSelectedDenom) const vendingFactoryParameters = await client .queryContractSmart(vendingFactoryForSelectedDenom, { params: {} }) .catch((error) => { toast.error(`${error.message}`, { style: { maxWidth: 'none' } }) addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() }) }) - setVendingMinterCreationFee(vendingFactoryParameters?.params?.creation_fee?.amount) - if (!collectionDetails?.updatable) { + + if (whitelistDetails?.whitelistState !== 'none' && whitelistDetails?.whitelistType === 'flex') { + setVendingMinterFlexCreationFee(vendingFactoryParameters?.params?.creation_fee?.amount) + setMinimumFlexMintPrice(vendingFactoryParameters?.params?.min_mint_price?.amount) + } else if (collectionDetails?.updatable) { + setVendingMinterUpdatableCreationFee(vendingFactoryParameters?.params?.creation_fee?.amount) + setMinimumUpdatableMintPrice(vendingFactoryParameters?.params?.min_mint_price?.amount) + } else { + setVendingMinterCreationFee(vendingFactoryParameters?.params?.creation_fee?.amount) setMinimumMintPrice(vendingFactoryParameters?.params?.min_mint_price?.amount) - setMintTokenFromVendingFactory( - tokensList.find((token) => token.denom === vendingFactoryParameters?.params?.min_mint_price?.denom), - ) } + + setMintTokenFromVendingFactory( + tokensList.find((token) => token.denom === vendingFactoryParameters?.params?.min_mint_price?.denom), + ) } - }, [collectionDetails?.updatable, mintingDetails?.selectedMintToken, tokensList, whitelistDetails?.whitelistType]) + }, [ + collectionDetails?.updatable, + mintingDetails?.selectedMintToken, + wallet.client, + whitelistDetails?.whitelistState, + whitelistDetails?.whitelistType, + ]) const checkwalletBalance = async () => { const walletBalance = await wallet.client?.getBalance(wallet.address, 'ustars').then((balance) => { @@ -1309,6 +1329,10 @@ const CollectionCreationPage: NextPage = () => { void fetchOpenEditionFactoryParameters() }, [fetchOpenEditionFactoryParameters]) + useEffect(() => { + void fetchVendingFactoryParameters() + }, [fetchVendingFactoryParameters]) + return ( <div> <NextSeo @@ -1699,7 +1723,9 @@ const CollectionCreationPage: NextPage = () => { <Conditional test={minterType === 'vending'}> <MintingDetails minimumMintPrice={ - collectionDetails?.updatable + whitelistDetails?.whitelistState !== 'none' && whitelistDetails?.whitelistType === 'flex' + ? Number(minimumFlexMintPrice) / 1000000 + : collectionDetails?.updatable ? Number(minimumUpdatableMintPrice) / 1000000 : Number(minimumMintPrice) / 1000000 } From c77e583d53655328f1bfecc89485314bcda3f914 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Sun, 6 Aug 2023 21:09:43 +0300 Subject: [PATCH 20/51] Update whitelist mint price denom wrt selected denom --- .../collections/creation/MintingDetails.tsx | 2 +- .../collections/creation/WhitelistDetails.tsx | 8 ++++++-- pages/collections/create.tsx | 15 +++++++++++---- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/components/collections/creation/MintingDetails.tsx b/components/collections/creation/MintingDetails.tsx index 22f802f..8246327 100644 --- a/components/collections/creation/MintingDetails.tsx +++ b/components/collections/creation/MintingDetails.tsx @@ -125,7 +125,7 @@ export const MintingDetails = ({ <div className="flex flex-row items-center"> <NumberInput {...unitPriceState} isRequired /> <select - className="py-[9px] px-4 mt-14 ml-4 placeholder:text-white/50 bg-white/10 rounded border-2 border-white/20 focus:ring focus:ring-plumbus-20" + className="py-[9px] px-4 mt-14 ml-2 placeholder:text-white/50 bg-white/10 rounded border-2 border-white/20 focus:ring focus:ring-plumbus-20" onChange={(e) => setSelectedMintToken(tokensList.find((t) => t.displayName === e.target.value))} > {vendingMinterList diff --git a/components/collections/creation/WhitelistDetails.tsx b/components/collections/creation/WhitelistDetails.tsx index 3f9daa7..e047e0d 100644 --- a/components/collections/creation/WhitelistDetails.tsx +++ b/components/collections/creation/WhitelistDetails.tsx @@ -8,6 +8,7 @@ import { useInputState, useNumberInputState } from 'components/forms/FormInput.h import { InputDateTime } from 'components/InputDateTime' import type { WhitelistFlexMember } from 'components/WhitelistFlexUpload' import { WhitelistFlexUpload } from 'components/WhitelistFlexUpload' +import type { TokenInfo } from 'config/token' import React, { useEffect, useState } from 'react' import { isValidAddress } from 'utils/isValidAddress' @@ -18,6 +19,7 @@ import { WhitelistUpload } from '../../WhitelistUpload' interface WhitelistDetailsProps { onChange: (data: WhitelistDetailsDataProps) => void + mintingTokenFromFactory?: TokenInfo } export interface WhitelistDetailsDataProps { @@ -38,7 +40,7 @@ type WhitelistState = 'none' | 'existing' | 'new' type WhitelistType = 'standard' | 'flex' -export const WhitelistDetails = ({ onChange }: WhitelistDetailsProps) => { +export const WhitelistDetails = ({ onChange, mintingTokenFromFactory }: WhitelistDetailsProps) => { const [whitelistState, setWhitelistState] = useState<WhitelistState>('none') const [whitelistType, setWhitelistType] = useState<WhitelistType>('standard') const [startDate, setStartDate] = useState<Date | undefined>(undefined) @@ -58,7 +60,9 @@ export const WhitelistDetails = ({ onChange }: WhitelistDetailsProps) => { id: 'unit-price', name: 'unitPrice', title: 'Unit Price', - subtitle: 'Token price for whitelisted addresses \n (min. 0 STARS)', + subtitle: `Token price for whitelisted addresses \n (min. 0 ${ + mintingTokenFromFactory ? mintingTokenFromFactory.displayName : 'STARS' + })`, placeholder: '25', }) diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index c29bd28..f23d75d 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -494,7 +494,10 @@ const CollectionCreationPage: NextPage = () => { members: whitelistDetails?.members, start_time: whitelistDetails?.startTime, end_time: whitelistDetails?.endTime, - mint_price: coin(String(Number(whitelistDetails?.unitPrice)), 'ustars'), + mint_price: coin( + String(Number(whitelistDetails?.unitPrice)), + mintTokenFromVendingFactory ? mintTokenFromVendingFactory.denom : 'ustars', + ), per_address_limit: whitelistDetails?.perAddressLimit, member_limit: whitelistDetails?.memberLimit, admins: whitelistDetails?.admins || [wallet.address], @@ -505,7 +508,10 @@ const CollectionCreationPage: NextPage = () => { members: whitelistDetails?.members, start_time: whitelistDetails?.startTime, end_time: whitelistDetails?.endTime, - mint_price: coin(String(Number(whitelistDetails?.unitPrice)), 'ustars'), + mint_price: coin( + String(Number(whitelistDetails?.unitPrice)), + mintTokenFromVendingFactory ? mintTokenFromVendingFactory.denom : 'ustars', + ), member_limit: whitelistDetails?.memberLimit, admins: whitelistDetails?.admins || [wallet.address], admins_mutable: whitelistDetails?.adminsMutable, @@ -1221,7 +1227,7 @@ const CollectionCreationPage: NextPage = () => { setVendingMinterCreationFee(vendingFactoryParameters?.params?.creation_fee?.amount) setMinimumMintPrice(vendingFactoryParameters?.params?.min_mint_price?.amount) } - + console.log('Vending Factory Parameters: ', vendingFactoryParameters) setMintTokenFromVendingFactory( tokensList.find((token) => token.denom === vendingFactoryParameters?.params?.min_mint_price?.denom), ) @@ -1729,6 +1735,7 @@ const CollectionCreationPage: NextPage = () => { ? Number(minimumUpdatableMintPrice) / 1000000 : Number(minimumMintPrice) / 1000000 } + mintingTokenFromFactory={mintTokenFromVendingFactory} numberOfTokens={uploadDetails?.assetFiles.length} onChange={setMintingDetails} uploadMethod={uploadDetails?.uploadMethod as UploadMethod} @@ -1757,7 +1764,7 @@ const CollectionCreationPage: NextPage = () => { > <div className="my-6"> <Conditional test={minterType === 'vending'}> - <WhitelistDetails onChange={setWhitelistDetails} /> + <WhitelistDetails mintingTokenFromFactory={mintTokenFromVendingFactory} onChange={setWhitelistDetails} /> <div className="my-6" /> </Conditional> <RoyaltyDetails onChange={setRoyaltyDetails} /> From a985e97c2eb5e32d8c36a6aabe1c9c7f2946b5fe Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Sun, 6 Aug 2023 21:27:58 +0300 Subject: [PATCH 21/51] Use factoryParameters/mint_price/denom for open edition airdrops --- contracts/openEditionMinter/contract.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/contracts/openEditionMinter/contract.ts b/contracts/openEditionMinter/contract.ts index df99013..bb66310 100644 --- a/contracts/openEditionMinter/contract.ts +++ b/contracts/openEditionMinter/contract.ts @@ -356,9 +356,10 @@ export const openEditionMinter = (client: SigningCosmWasmClient, txSigner: strin console.log(factoryParameters?.params?.extension?.airdrop_mint_fee_bps) const price = factoryParameters?.params?.extension?.airdrop_mint_price.amount + const denom = factoryParameters?.params?.extension?.airdrop_mint_price.denom || 'ustars' if (!price) { throw new Error( - 'Unable to retrieve a valid airdrop mint price. It may be that the given contract address does not belong to a Open Edition Factory.', + 'Unable to retrieve a valid airdrop mint price. It may be that the given contract address does not belong to an Open Edition Factory.', ) } const airdropFee = Number(price) * Number(factoryParameters.params.extension?.airdrop_mint_fee_bps) @@ -370,7 +371,7 @@ export const openEditionMinter = (client: SigningCosmWasmClient, txSigner: strin }, 'auto', '', - airdropFee > 0 ? [coin(airdropFee / 100 / 100, 'ustars')] : [], + airdropFee > 0 ? [coin(airdropFee / 100 / 100, denom as string)] : [], ) return res.transactionHash }) @@ -386,6 +387,7 @@ export const openEditionMinter = (client: SigningCosmWasmClient, txSigner: strin }) const price = factoryParameters?.params?.extension?.airdrop_mint_price.amount + const denom = factoryParameters?.params?.extension?.airdrop_mint_price.denom || 'ustars' if (!price) { throw new Error( 'Unable to retrieve a valid airdrop mint price. It may be that the given contract address does not belong to a Open Edition Factory.', @@ -403,7 +405,7 @@ export const openEditionMinter = (client: SigningCosmWasmClient, txSigner: strin sender: senderAddress, contract: contractAddress, msg: toUtf8(JSON.stringify(msg)), - funds: airdropFee > 0 ? [coin(airdropFee / 100 / 100, 'ustars')] : [], + funds: airdropFee > 0 ? [coin(airdropFee / 100 / 100, denom as string)] : [], }), } @@ -426,6 +428,7 @@ export const openEditionMinter = (client: SigningCosmWasmClient, txSigner: strin }) const price = factoryParameters?.params?.extension?.airdrop_mint_price.amount + const denom = factoryParameters?.params?.extension?.airdrop_mint_price.denom || 'ustars' if (!price) { throw new Error( 'Unable to retrieve a valid airdrop mint price. It may be that the given contract address does not belong to a Open Edition Factory.', @@ -443,7 +446,7 @@ export const openEditionMinter = (client: SigningCosmWasmClient, txSigner: strin sender: senderAddress, contract: contractAddress, msg: toUtf8(JSON.stringify(msg)), - funds: airdropFee > 0 ? [coin(airdropFee / 100 / 100, 'ustars')] : [], + funds: airdropFee > 0 ? [coin(airdropFee / 100 / 100, denom as string)] : [], }), } From fe1cfe884c3ece1ab32b35c8e007c6f8c3363266 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Sun, 6 Aug 2023 21:57:03 +0300 Subject: [PATCH 22/51] Fix updatable code id mix up --- pages/collections/create.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index f23d75d..9adca27 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -554,7 +554,7 @@ const CollectionCreationPage: NextPage = () => { whitelist, }, collection_params: { - code_id: collectionDetails?.updatable ? SG721_CODE_ID : SG721_UPDATABLE_CODE_ID, + code_id: collectionDetails?.updatable ? SG721_UPDATABLE_CODE_ID : SG721_CODE_ID, name: collectionDetails?.name, symbol: collectionDetails?.symbol, info: { From 6d0b21ec594a805086dc54f632f29c78ca3deb47 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 7 Aug 2023 12:59:23 +0300 Subject: [PATCH 23/51] Update IBC denoms --- config/minter.ts | 26 +++++++++++++------------- config/token.ts | 14 +++++++------- env.d.ts | 4 ++-- utils/constants.ts | 6 +++--- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/config/minter.ts b/config/minter.ts index 02ebc5b..528f60b 100644 --- a/config/minter.ts +++ b/config/minter.ts @@ -1,11 +1,11 @@ import { OPEN_EDITION_FACTORY_ADDRESS, OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS, - OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS, + OPEN_EDITION_IBC_FRNZ_FACTORY_ADDRESS, OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS, OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS, OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS, - OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS, + OPEN_EDITION_UPDATABLE_IBC_FRNZ_FACTORY_ADDRESS, OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS, VENDING_FACTORY_ADDRESS, VENDING_FACTORY_FLEX_ADDRESS, @@ -22,7 +22,7 @@ import { } from 'utils/constants' import type { TokenInfo } from './token' -import { ibcAtom, ibcFrenz, ibcUsdc, stars } from './token' +import { ibcAtom, ibcFrnz, ibcUsdc, stars } from './token' export interface MinterInfo { id: string @@ -74,17 +74,17 @@ export const openEditionUpdatableIbcUsdcMinter: MinterInfo = { updatable: true, } -export const openEditionIbcFrenzMinter: MinterInfo = { - id: 'open-edition-ibc-frenz-minter', - factoryAddress: OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS, - supportedToken: ibcFrenz, +export const openEditionIbcFrnzMinter: MinterInfo = { + id: 'open-edition-ibc-frnz-minter', + factoryAddress: OPEN_EDITION_IBC_FRNZ_FACTORY_ADDRESS, + supportedToken: ibcFrnz, updatable: false, } -export const openEditionUpdatableIbcFrenzMinter: MinterInfo = { - id: 'open-edition-updatable-ibc-frenz-minter', - factoryAddress: OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS, - supportedToken: ibcFrenz, +export const openEditionUpdatableIbcFrnzMinter: MinterInfo = { + id: 'open-edition-updatable-ibc-frnz-minter', + factoryAddress: OPEN_EDITION_UPDATABLE_IBC_FRNZ_FACTORY_ADDRESS, + supportedToken: ibcFrnz, updatable: true, } @@ -93,8 +93,8 @@ export const openEditionMinterList = [ openEditionUpdatableStarsMinter, openEditionUpdatableIbcAtomMinter, openEditionIbcAtomMinter, - openEditionIbcFrenzMinter, - openEditionUpdatableIbcFrenzMinter, + openEditionIbcFrnzMinter, + openEditionUpdatableIbcFrnzMinter, openEditionIbcUsdcMinter, openEditionUpdatableIbcUsdcMinter, ] diff --git a/config/token.ts b/config/token.ts index 3119c6d..af49d99 100644 --- a/config/token.ts +++ b/config/token.ts @@ -16,23 +16,23 @@ export const stars: TokenInfo = { export const ibcAtom: TokenInfo = { id: 'ibc-atom', - denom: 'ibc/atom', + denom: 'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2', displayName: 'ATOM', decimalPlaces: 6, } export const ibcUsdc: TokenInfo = { id: 'ibc-usdc', - denom: 'ibc/usdc', + denom: 'ibc/D189335C6E4A68B513C10AB227BF1C1D38C746766278BA3EEB4FB14124F1D858', displayName: 'USDC', decimalPlaces: 6, } -export const ibcFrenz: TokenInfo = { - id: 'ibc-frenz', - denom: 'ibc/frenz', - displayName: 'FRENZ', +export const ibcFrnz: TokenInfo = { + id: 'ibc-frnz', + denom: 'ibc/7FA7EC64490E3BDE5A1A28CBE73CC0AD22522794957BC891C46321E3A6074DB9', + displayName: 'FRNZ', decimalPlaces: 6, } -export const tokensList = [stars, ibcAtom, ibcUsdc, ibcFrenz] +export const tokensList = [stars, ibcAtom, ibcUsdc, ibcFrnz] diff --git a/env.d.ts b/env.d.ts index d41c8e5..3d4984a 100644 --- a/env.d.ts +++ b/env.d.ts @@ -40,8 +40,8 @@ declare namespace NodeJS { readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS: string - readonly NEXT_PUBLIC_OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS: string - readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_OPEN_EDITION_IBC_FRNZ_FACTORY_ADDRESS: string + readonly NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_FRNZ_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_OPEN_EDITION_MINTER_CODE_ID: string readonly NEXT_PUBLIC_BASE_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_BASE_FACTORY_UPDATABLE_ADDRESS: string diff --git a/utils/constants.ts b/utils/constants.ts index 8b919ed..4911df1 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -35,9 +35,9 @@ export const OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS = export const OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS export const OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS -export const OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS -export const OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS = - process.env.NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS +export const OPEN_EDITION_IBC_FRNZ_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_IBC_FRNZ_FACTORY_ADDRESS +export const OPEN_EDITION_UPDATABLE_IBC_FRNZ_FACTORY_ADDRESS = + process.env.NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_FRNZ_FACTORY_ADDRESS export const OPEN_EDITION_MINTER_CODE_ID = parseInt(process.env.NEXT_PUBLIC_OPEN_EDITION_MINTER_CODE_ID, 10) export const SG721_NAME_ADDRESS = process.env.NEXT_PUBLIC_SG721_NAME_ADDRESS export const BASE_MINTER_CODE_ID = parseInt(process.env.NEXT_PUBLIC_VENDING_MINTER_CODE_ID, 10) From 7207e26520422f17473ba6c1cde22b18ac4699a8 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 7 Aug 2023 13:53:17 +0300 Subject: [PATCH 24/51] Update .env.example --- .env.example | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/.env.example b/.env.example index 45ed4b3..12863f6 100644 --- a/.env.example +++ b/.env.example @@ -8,28 +8,35 @@ NEXT_PUBLIC_OPEN_EDITION_SG721_UPDATABLE_CODE_ID=2596 NEXT_PUBLIC_VENDING_MINTER_CODE_ID=2600 NEXT_PUBLIC_VENDING_MINTER_FLEX_CODE_ID=2601 NEXT_PUBLIC_BASE_MINTER_CODE_ID=2598 +NEXT_PUBLIC_OPEN_EDITION_MINTER_CODE_ID=2579 + NEXT_PUBLIC_VENDING_FACTORY_ADDRESS="stars18h7ugh8eaug7wr0w4yjw0ls5s937z35pnkg935ucsek2y9xl3gaqqk4jtx" NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_ADDRESS="stars1h65nms9gwg4vdktyqj84tu50gwlm34e0eczl5w2ezllxuzfxy9esa9qlt0" NEXT_PUBLIC_VENDING_FACTORY_FLEX_ADDRESS="stars1hvu2ghqkcnvhtj2fc6wuazxt4dqcftslp2rwkkkcxy269a35a9pq60ug2q" -NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" -NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" -NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" -NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" -NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_FLEX_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" -NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_FLEX_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" -NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" -NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_FLEX_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" +NEXT_PUBLIC_VENDING_FACTORY_UPDATABLE_FLEX_ADDRESS= + +# NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_ADDRESS= +# NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_ADDRESS= +# NEXT_PUBLIC_VENDING_IBC_ATOM_FACTORY_FLEX_ADDRESS= +# NEXT_PUBLIC_VENDING_IBC_ATOM_UPDATABLE_FACTORY_FLEX_ADDRESS= + +# NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_ADDRESS= +# NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_ADDRESS= +# NEXT_PUBLIC_VENDING_IBC_USDC_FACTORY_FLEX_ADDRESS= +# NEXT_PUBLIC_VENDING_IBC_USDC_UPDATABLE_FACTORY_FLEX_ADDRESS= + NEXT_PUBLIC_BASE_FACTORY_ADDRESS="stars1a45hcxty3spnmm2f0papl8v4dk5ew29s4syhn4efte8u5haex99qlkrtnx" NEXT_PUBLIC_BASE_FACTORY_UPDATABLE_ADDRESS="stars100xegx2syry4tclkmejjwxk4nfqahvcqhm9qxut5wxuzhj5d9qfsh5nmym" + NEXT_PUBLIC_OPEN_EDITION_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS="stars1fk5dkzcylam8mcpqrn8y9spauvc3d4navtaqurcc49dc3p9f8d3qdkvymx" -NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" -NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" -NEXT_PUBLIC_OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" -NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" -NEXT_PUBLIC_OPEN_EDITION_IBC_FRENZ_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" -NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_FRENZ_FACTORY_ADDRESS="stars1sqweqcxlf2f7qhf27gn5naqusk5q52fkzewmy63c4sglvle3s7ls6k828e" -NEXT_PUBLIC_OPEN_EDITION_MINTER_CODE_ID=2579 + +# NEXT_PUBLIC_OPEN_EDITION_IBC_ATOM_FACTORY_ADDRESS= +# NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS= +# NEXT_PUBLIC_OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS= +# NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS= +# NEXT_PUBLIC_OPEN_EDITION_IBC_FRNZ_FACTORY_ADDRESS= +# NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_FRNZ_FACTORY_ADDRESS= NEXT_PUBLIC_SG721_NAME_ADDRESS="stars1fx74nkqkw2748av8j7ew7r3xt9cgjqduwn8m0ur5lhe49uhlsasszc5fhr" NEXT_PUBLIC_WHITELIST_CODE_ID=2602 @@ -47,4 +54,4 @@ NEXT_PUBLIC_NETWORK=testnet NEXT_PUBLIC_STARGAZE_WEBSITE_URL=https://testnet.publicawesome.dev NEXT_PUBLIC_BADGES_URL=https://badges.publicawesome.dev NEXT_PUBLIC_WEBSITE_URL=https:// -NEXT_PUBLIC_SYNC_COLLECTIONS_API_URL="https://..." \ No newline at end of file +NEXT_PUBLIC_SYNC_COLLECTIONS_API_URL="https://..." From 784446a676e4b317fbab3214068678a809c4d15e Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 7 Aug 2023 13:55:31 +0300 Subject: [PATCH 25/51] Bump Studio version --- .env.example | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 12863f6..e460d2a 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -APP_VERSION=0.7.2 +APP_VERSION=0.7.3 NEXT_PUBLIC_PINATA_ENDPOINT_URL=https://api.pinata.cloud/pinning/pinFileToIPFS NEXT_PUBLIC_SG721_CODE_ID=2595 diff --git a/package.json b/package.json index e053b14..ff54c94 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "stargaze-studio", - "version": "0.7.2", + "version": "0.7.3", "workspaces": [ "packages/*" ], From 85efecd40c3b61b7736934f9e4820a04ea29ca27 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 7 Aug 2023 14:01:46 +0300 Subject: [PATCH 26/51] Clean up --- pages/collections/create.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index 9adca27..7642a42 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -675,8 +675,6 @@ const CollectionCreationPage: NextPage = () => { setCreatingCollection(false) }) } else { - console.log('Here') - console.log(data.baseMinterAddress) await toast .promise( baseMinterContract @@ -1160,7 +1158,6 @@ const CollectionCreationPage: NextPage = () => { toast.error(`${error.message}`, { style: { maxWidth: 'none' } }) addLogItem({ id: uid(), message: error.message, type: 'Error', timestamp: new Date() }) }) - console.log('Open Edition Factory Parameters: ', openEditionFactoryParameters) setOpenEditionMinterCreationFee(openEditionFactoryParameters?.params?.creation_fee?.amount) if (!openEditionMinterDetails?.collectionDetails?.updatable) { setMinimumOpenEditionMintPrice(openEditionFactoryParameters?.params?.min_mint_price?.amount) @@ -1168,8 +1165,6 @@ const CollectionCreationPage: NextPage = () => { tokensList.find((token) => token.denom === openEditionFactoryParameters?.params?.min_mint_price?.denom), ) } - console.log('Selected OE Factory: ', factoryForSelectedDenom) - console.log('Selected Updatable OE Factory: ', updatableFactoryForSelectedDenom) } if (updatableFactoryForSelectedDenom?.factoryAddress) { const openEditionUpdatableFactoryParameters = await client @@ -1195,10 +1190,8 @@ const CollectionCreationPage: NextPage = () => { ]) const fetchVendingFactoryParameters = useCallback(async () => { - console.log('Here') const client = wallet.client if (!client) return - console.log('Selected Token: ', mintingDetails?.selectedMintToken) const vendingFactoryForSelectedDenom = vendingMinterList .concat(flexibleVendingMinterList) .find( @@ -1207,7 +1200,6 @@ const CollectionCreationPage: NextPage = () => { minter.updatable === collectionDetails?.updatable && minter.flexible === (whitelistDetails?.whitelistType === 'flex'), )?.factoryAddress - console.log(vendingFactoryForSelectedDenom) if (vendingFactoryForSelectedDenom) { setVendingFactoryAddress(vendingFactoryForSelectedDenom) const vendingFactoryParameters = await client @@ -1227,7 +1219,6 @@ const CollectionCreationPage: NextPage = () => { setVendingMinterCreationFee(vendingFactoryParameters?.params?.creation_fee?.amount) setMinimumMintPrice(vendingFactoryParameters?.params?.min_mint_price?.amount) } - console.log('Vending Factory Parameters: ', vendingFactoryParameters) setMintTokenFromVendingFactory( tokensList.find((token) => token.denom === vendingFactoryParameters?.params?.min_mint_price?.denom), ) From 51843cade00b33f09585c1c8609e84569c92bf08 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Thu, 10 Aug 2023 22:38:03 +0300 Subject: [PATCH 27/51] Surface open edition collection summary details --- components/openEdition/OpenEditionMinterCreator.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/components/openEdition/OpenEditionMinterCreator.tsx b/components/openEdition/OpenEditionMinterCreator.tsx index 6b845b9..90c7c21 100644 --- a/components/openEdition/OpenEditionMinterCreator.tsx +++ b/components/openEdition/OpenEditionMinterCreator.tsx @@ -57,6 +57,9 @@ export interface OpenEditionMinterDetailsDataProps { offChainMetadataUploadDetails?: OffChainMetadataUploadDetailsDataProps mintingDetails?: MintingDetailsDataProps metadataStorageMethod?: MetadataStorageMethod + openEditionMinterContractAddress?: string | null + coverImageUrl?: string | null + tokenUri?: string | null } interface OpenEditionMinterCreatorProps { @@ -627,6 +630,9 @@ export const OpenEditionMinterCreator = ({ offChainMetadataUploadDetails: offChainMetadataUploadDetails ? offChainMetadataUploadDetails : undefined, mintingDetails: mintingDetails ? mintingDetails : undefined, metadataStorageMethod, + openEditionMinterContractAddress, + coverImageUrl, + tokenUri, } onDetailsChange(data) // eslint-disable-next-line react-hooks/exhaustive-deps @@ -637,6 +643,10 @@ export const OpenEditionMinterCreator = ({ onChainMetadataInputDetails, offChainMetadataUploadDetails, mintingDetails, + metadataStorageMethod, + openEditionMinterContractAddress, + coverImageUrl, + tokenUri, ]) useEffect(() => { From f324cb6f50626ef8e6615783fda2b3eda16207aa Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Sat, 12 Aug 2023 22:47:53 +0300 Subject: [PATCH 28/51] Add token image URI to open edition minter details --- components/openEdition/OpenEditionMinterCreator.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/openEdition/OpenEditionMinterCreator.tsx b/components/openEdition/OpenEditionMinterCreator.tsx index 90c7c21..d21cf60 100644 --- a/components/openEdition/OpenEditionMinterCreator.tsx +++ b/components/openEdition/OpenEditionMinterCreator.tsx @@ -60,6 +60,7 @@ export interface OpenEditionMinterDetailsDataProps { openEditionMinterContractAddress?: string | null coverImageUrl?: string | null tokenUri?: string | null + tokenImageUri?: string | null } interface OpenEditionMinterCreatorProps { @@ -633,6 +634,7 @@ export const OpenEditionMinterCreator = ({ openEditionMinterContractAddress, coverImageUrl, tokenUri, + tokenImageUri, } onDetailsChange(data) // eslint-disable-next-line react-hooks/exhaustive-deps @@ -647,6 +649,7 @@ export const OpenEditionMinterCreator = ({ openEditionMinterContractAddress, coverImageUrl, tokenUri, + tokenImageUri, ]) useEffect(() => { From 6fc4022c8d3be90155045663a2ad3343664d65e3 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Tue, 15 Aug 2023 18:24:27 +0300 Subject: [PATCH 29/51] Update export logic for standard collection summary --- pages/collections/create.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index 9ad3c76..4bf62e5 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -1280,7 +1280,6 @@ const CollectionCreationPage: NextPage = () => { }) } - // function to export all details as a .json file const exportDetails = () => { const details = { minterType, @@ -1291,6 +1290,12 @@ const CollectionCreationPage: NextPage = () => { royaltyDetails, baseMinterDetails, openEditionMinterDetails, + vendingMinterContractAddress, + baseTokenUri: `ipfs://${baseTokenUri}`, + coverImageUrl: + uploadDetails?.uploadMethod === 'new' + ? `ipfs://${coverImageUrl}/${collectionDetails?.imageFile[0].name as string}` + : `${coverImageUrl}`, } const element = document.createElement('a') const file = new Blob([JSON.stringify(details)], { type: 'text/plain' }) @@ -1299,7 +1304,6 @@ const CollectionCreationPage: NextPage = () => { document.body.appendChild(element) // Required for this to work in FireFox element.click() } - // function to import all details from a .json file const importDetails = (event: ChangeEvent<HTMLInputElement>) => { if (event.target.files === null) return toast.error('No files selected.') const file = event.target.files[0] @@ -1308,6 +1312,11 @@ const CollectionCreationPage: NextPage = () => { const contents = e.target?.result const details = JSON.parse(contents as string) setMinterType(details.minterType) + if (details.vendingMinterContractAddress) { + details.uploadDetails.uploadMethod = 'existing' + details.uploadDetails.baseTokenURI = details.baseTokenUri + details.uploadDetails.imageUrl = details.coverImageUrl + } setImportedDetails(details) } reader.readAsText(file) From 702e47e9e6500d61d3ec8415db05642bcf199aff Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Tue, 15 Aug 2023 20:06:53 +0300 Subject: [PATCH 30/51] Update FRNZ denom for testnet --- config/token.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/config/token.ts b/config/token.ts index af49d99..7cb99b6 100644 --- a/config/token.ts +++ b/config/token.ts @@ -1,3 +1,5 @@ +import { NETWORK } from 'utils/constants' + export interface TokenInfo { id: string denom: string @@ -30,7 +32,10 @@ export const ibcUsdc: TokenInfo = { export const ibcFrnz: TokenInfo = { id: 'ibc-frnz', - denom: 'ibc/7FA7EC64490E3BDE5A1A28CBE73CC0AD22522794957BC891C46321E3A6074DB9', + denom: + NETWORK === 'mainnet' + ? 'ibc/7FA7EC64490E3BDE5A1A28CBE73CC0AD22522794957BC891C46321E3A6074DB9' + : 'factory/stars10w5eulj60qp3cfqa0hkmke78qdy2feq6x9xdmd/ufrnz', displayName: 'FRNZ', decimalPlaces: 6, } From 3c392381b246974733ca6a07eed1ad3d098cf70b Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Tue, 15 Aug 2023 20:07:51 +0300 Subject: [PATCH 31/51] Address invalid creation fee problem following minting denom change --- pages/collections/create.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index 7642a42..70868ff 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -134,6 +134,7 @@ const CollectionCreationPage: NextPage = () => { const [uploading, setUploading] = useState(false) const [isMintingComplete, setIsMintingComplete] = useState(false) + const [initialParametersFetched, setInitialParametersFetched] = useState(false) const [creatingCollection, setCreatingCollection] = useState(false) const [readyToCreateVm, setReadyToCreateVm] = useState(false) const [readyToCreateBm, setReadyToCreateBm] = useState(false) @@ -1115,7 +1116,6 @@ const CollectionCreationPage: NextPage = () => { setVendingMinterFlexCreationFee(vendingFactoryFlexParameters?.params?.creation_fee?.amount) setMinimumFlexMintPrice(vendingFactoryFlexParameters?.params?.min_mint_price?.amount) } - if (OPEN_EDITION_FACTORY_ADDRESS) { const openEditionFactoryParameters = await client .queryContractSmart(OPEN_EDITION_FACTORY_ADDRESS, { params: {} }) @@ -1136,6 +1136,7 @@ const CollectionCreationPage: NextPage = () => { setOpenEditionMinterUpdatableCreationFee(openEditionUpdatableFactoryParameters?.params?.creation_fee?.amount) setMinimumOpenEditionUpdatableMintPrice(openEditionUpdatableFactoryParameters?.params?.min_mint_price?.amount) } + setInitialParametersFetched(true) } const fetchOpenEditionFactoryParameters = useCallback(async () => { @@ -1175,7 +1176,7 @@ const CollectionCreationPage: NextPage = () => { }) setOpenEditionMinterUpdatableCreationFee(openEditionUpdatableFactoryParameters?.params?.creation_fee?.amount) if (openEditionMinterDetails?.collectionDetails?.updatable) { - setMinimumOpenEditionMintPrice(openEditionUpdatableFactoryParameters?.params?.min_mint_price?.amount) + setMinimumOpenEditionUpdatableMintPrice(openEditionUpdatableFactoryParameters?.params?.min_mint_price?.amount) setMintTokenFromOpenEditionFactory( tokensList.find( (token) => token.denom === openEditionUpdatableFactoryParameters?.params?.min_mint_price?.denom, @@ -1319,7 +1320,9 @@ const CollectionCreationPage: NextPage = () => { }, [minterType, baseMinterDetails?.baseMinterAcquisitionMethod, uploadDetails?.uploadMethod]) useEffect(() => { - void fetchInitialFactoryParameters() + if (!initialParametersFetched) { + void fetchInitialFactoryParameters() + } }, [wallet.client]) useEffect(() => { From ea5caff1aa2ef599a8d0bdbcde9aae81efc06e83 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Tue, 15 Aug 2023 20:09:35 +0300 Subject: [PATCH 32/51] Update .env.example --- .env.example | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index e460d2a..ffe2835 100644 --- a/.env.example +++ b/.env.example @@ -35,8 +35,8 @@ NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_FACTORY_ADDRESS="stars1fk5dkzcylam8mcpqrn8y9s # NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_ATOM_FACTORY_ADDRESS= # NEXT_PUBLIC_OPEN_EDITION_IBC_USDC_FACTORY_ADDRESS= # NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_USDC_FACTORY_ADDRESS= -# NEXT_PUBLIC_OPEN_EDITION_IBC_FRNZ_FACTORY_ADDRESS= -# NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_FRNZ_FACTORY_ADDRESS= +NEXT_PUBLIC_OPEN_EDITION_IBC_FRNZ_FACTORY_ADDRESS="stars1vzffawsjhvspstu5lvtzz2x5n7zh07hnw09c9dfxcj78un05rcms5n3q3e" +NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_FRNZ_FACTORY_ADDRESS="stars1tc09vlgdg8rqyapcxwm9qdq8naj4gym9px4ntue9cs0kse5rvess0nee3a" NEXT_PUBLIC_SG721_NAME_ADDRESS="stars1fx74nkqkw2748av8j7ew7r3xt9cgjqduwn8m0ur5lhe49uhlsasszc5fhr" NEXT_PUBLIC_WHITELIST_CODE_ID=2602 From 27e1727fa84b65a987881fc1ca2acd236a46f99e Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Wed, 16 Aug 2023 17:48:33 +0300 Subject: [PATCH 33/51] Update export logic for open edition collection summary --- components/openEdition/CollectionDetails.tsx | 2 +- .../openEdition/OffChainMetadataUploadDetails.tsx | 8 +++++--- pages/collections/create.tsx | 11 ++++++++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/components/openEdition/CollectionDetails.tsx b/components/openEdition/CollectionDetails.tsx index 4c46fff..0118cea 100644 --- a/components/openEdition/CollectionDetails.tsx +++ b/components/openEdition/CollectionDetails.tsx @@ -159,7 +159,7 @@ export const CollectionDetails = ({ nameState.onChange(importedCollectionDetails.name) descriptionState.onChange(importedCollectionDetails.description) symbolState.onChange(importedCollectionDetails.symbol) - setCoverImage(importedCollectionDetails.imageFile[0] || null) + //setCoverImage(importedCollectionDetails.imageFile[0] || null) externalLinkState.onChange(importedCollectionDetails.externalLink || '') setTimestamp( importedCollectionDetails.startTradingTime diff --git a/components/openEdition/OffChainMetadataUploadDetails.tsx b/components/openEdition/OffChainMetadataUploadDetails.tsx index 02e8703..4328495 100644 --- a/components/openEdition/OffChainMetadataUploadDetails.tsx +++ b/components/openEdition/OffChainMetadataUploadDetails.tsx @@ -231,8 +231,10 @@ export const OffChainMetadataUploadDetails = ({ setMetadataFilesArray([]) if (assetFilesRef.current) assetFilesRef.current.value = '' setAssetFilesArray([]) - tokenUriState.onChange('') - coverImageUrlState.onChange('') + if (!importedOffChainMetadataUploadDetails) { + tokenUriState.onChange('') + coverImageUrlState.onChange('') + } }, [uploadMethod, metadataStorageMethod]) useEffect(() => { @@ -241,7 +243,7 @@ export const OffChainMetadataUploadDetails = ({ nftStorageApiKeyState.onChange(importedOffChainMetadataUploadDetails.nftStorageApiKey || '') pinataApiKeyState.onChange(importedOffChainMetadataUploadDetails.pinataApiKey || '') pinataSecretKeyState.onChange(importedOffChainMetadataUploadDetails.pinataSecretKey || '') - setUploadMethod('existing') + setUploadMethod(importedOffChainMetadataUploadDetails.uploadMethod) tokenUriState.onChange(importedOffChainMetadataUploadDetails.tokenURI || '') coverImageUrlState.onChange(importedOffChainMetadataUploadDetails.imageUrl || '') setOpenEditionMinterMetadataFile(importedOffChainMetadataUploadDetails.openEditionMinterMetadataFile) diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index 4bf62e5..423cd19 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -1294,7 +1294,7 @@ const CollectionCreationPage: NextPage = () => { baseTokenUri: `ipfs://${baseTokenUri}`, coverImageUrl: uploadDetails?.uploadMethod === 'new' - ? `ipfs://${coverImageUrl}/${collectionDetails?.imageFile[0].name as string}` + ? `ipfs://${coverImageUrl}/${collectionDetails?.imageFile[0]?.name as string}` : `${coverImageUrl}`, } const element = document.createElement('a') @@ -1317,6 +1317,14 @@ const CollectionCreationPage: NextPage = () => { details.uploadDetails.baseTokenURI = details.baseTokenUri details.uploadDetails.imageUrl = details.coverImageUrl } + if (details.openEditionMinterDetails.openEditionMinterContractAddress) { + details.openEditionMinterDetails.offChainMetadataUploadDetails.uploadMethod = 'existing' + details.openEditionMinterDetails.offChainMetadataUploadDetails.tokenURI = + details.openEditionMinterDetails.tokenUri + details.openEditionMinterDetails.offChainMetadataUploadDetails.imageUrl = + details.openEditionMinterDetails.coverImageUrl + } + setImportedDetails(details) } reader.readAsText(file) @@ -1740,6 +1748,7 @@ const CollectionCreationPage: NextPage = () => { )} <Conditional test={minterType === 'openEdition'}> <OpenEditionMinterCreator + importedOpenEditionMinterDetails={importedDetails?.openEditionMinterDetails} minimumMintPrice={minimumOpenEditionMintPrice as string} minimumUpdatableMintPrice={minimumOpenEditionUpdatableMintPrice as string} mintTokenFromFactory={mintTokenFromOpenEditionFactory} From 3fbebbe03d264ca825132956d0ace32c3b7fd247 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Wed, 16 Aug 2023 17:57:07 +0300 Subject: [PATCH 34/51] Auto-add wallet address as whitelist admin --- components/collections/creation/WhitelistDetails.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/components/collections/creation/WhitelistDetails.tsx b/components/collections/creation/WhitelistDetails.tsx index a0de6c9..aa0d908 100644 --- a/components/collections/creation/WhitelistDetails.tsx +++ b/components/collections/creation/WhitelistDetails.tsx @@ -9,6 +9,7 @@ import { InputDateTime } from 'components/InputDateTime' import type { WhitelistFlexMember } from 'components/WhitelistFlexUpload' import { WhitelistFlexUpload } from 'components/WhitelistFlexUpload' import type { TokenInfo } from 'config/token' +import { useWallet } from 'contexts/wallet' import React, { useEffect, useState } from 'react' import { isValidAddress } from 'utils/isValidAddress' @@ -46,6 +47,8 @@ export const WhitelistDetails = ({ mintingTokenFromFactory, importedWhitelistDetails, }: WhitelistDetailsProps) => { + const wallet = useWallet() + const [whitelistState, setWhitelistState] = useState<WhitelistState>('none') const [whitelistType, setWhitelistType] = useState<WhitelistType>('standard') const [startDate, setStartDate] = useState<Date | undefined>(undefined) @@ -196,6 +199,14 @@ export const WhitelistDetails = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [importedWhitelistDetails]) + useEffect(() => { + if (whitelistState === 'new' && wallet.address) { + addressListState.reset() + addressListState.add({ address: wallet.address }) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [whitelistState, wallet.address]) + return ( <div className="py-3 px-8 rounded border-2 border-white/20"> <div className="flex justify-center"> @@ -344,7 +355,6 @@ export const WhitelistDetails = ({ <div className="my-4 ml-4"> <AddressList entries={addressListState.entries} - isRequired onAdd={addressListState.add} onChange={addressListState.update} onRemove={addressListState.remove} From 71e539a0b4ac5ff057aa9e8ee81369faaeec6a18 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Thu, 17 Aug 2023 14:20:37 +0300 Subject: [PATCH 35/51] Export/Import open edition token metadata --- components/MetadataInput.tsx | 39 +++++++++++++++++-- .../OffChainMetadataUploadDetails.tsx | 8 +++- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/components/MetadataInput.tsx b/components/MetadataInput.tsx index 652c480..2340d3f 100644 --- a/components/MetadataInput.tsx +++ b/components/MetadataInput.tsx @@ -13,6 +13,8 @@ export interface MetadataInputProps { selectedAssetFile: File selectedMetadataFile: File updateMetadataToUpload: (metadataFile: File) => void + onChange?: (metadata: any) => void + importedMetadata?: any } export const MetadataInput = (props: MetadataInputProps) => { @@ -151,8 +153,11 @@ export const MetadataInput = (props: MetadataInputProps) => { useEffect(() => { console.log(props.selectedMetadataFile?.name) - if (props.selectedMetadataFile) void parseMetadata(props.selectedMetadataFile) - else void parseMetadata(emptyMetadataFile) + if (props.selectedMetadataFile) { + void parseMetadata(props.selectedMetadataFile) + } else if (!props.importedMetadata) { + void parseMetadata(emptyMetadataFile) + } }, [props.selectedMetadataFile?.name]) const nameStateMemo = useMemo(() => nameState, [nameState.value]) @@ -163,7 +168,10 @@ export const MetadataInput = (props: MetadataInputProps) => { useEffect(() => { console.log('Update metadata') - if (metadata) generateUpdatedMetadata() + if (metadata) { + generateUpdatedMetadata() + if (props.onChange) props.onChange(metadata) + } console.log(metadata) }, [ nameStateMemo.value, @@ -173,6 +181,31 @@ export const MetadataInput = (props: MetadataInputProps) => { attributesStateMemo.entries, ]) + useEffect(() => { + if (props.importedMetadata) { + console.log('Imported metadata: ', props.importedMetadata) + nameState.onChange(props.importedMetadata.name || '') + descriptionState.onChange(props.importedMetadata.description || '') + externalUrlState.onChange(props.importedMetadata.external_url || '') + youtubeUrlState.onChange(props.importedMetadata.youtube_url || '') + if (props.importedMetadata?.attributes && props.importedMetadata?.attributes?.length > 0) { + attributesState.reset() + props.importedMetadata?.attributes?.forEach((attribute: { trait_type: string; value: string }) => { + attributesState.add({ + trait_type: attribute.trait_type, + value: attribute.value, + }) + }) + } else { + attributesState.reset() + attributesState.add({ + trait_type: '', + value: '', + }) + } + } + }, [props.importedMetadata]) + return ( <div> <div className="grid grid-cols-2 mt-4 mr-4 ml-8 w-full max-w-6xl max-h-full no-scrollbar"> diff --git a/components/openEdition/OffChainMetadataUploadDetails.tsx b/components/openEdition/OffChainMetadataUploadDetails.tsx index 4328495..abe06f6 100644 --- a/components/openEdition/OffChainMetadataUploadDetails.tsx +++ b/components/openEdition/OffChainMetadataUploadDetails.tsx @@ -42,6 +42,7 @@ export interface OffChainMetadataUploadDetailsDataProps { tokenURI?: string imageUrl?: string openEditionMinterMetadataFile?: File + exportedMetadata?: any } export const OffChainMetadataUploadDetails = ({ @@ -55,6 +56,7 @@ export const OffChainMetadataUploadDetails = ({ const [uploadService, setUploadService] = useState<UploadServiceType>('nft-storage') const [metadataFileArrayIndex, setMetadataFileArrayIndex] = useState(0) const [refreshMetadata, setRefreshMetadata] = useState(false) + const [exportedMetadata, setExportedMetadata] = useState(undefined) const [openEditionMinterMetadataFile, setOpenEditionMinterMetadataFile] = useState<File | undefined>() @@ -206,6 +208,7 @@ export const OffChainMetadataUploadDetails = ({ .replace(regex, '') .trim(), openEditionMinterMetadataFile, + exportedMetadata, } onChange(data) } catch (error: any) { @@ -224,6 +227,7 @@ export const OffChainMetadataUploadDetails = ({ coverImageUrlState.value, refreshMetadata, openEditionMinterMetadataFile, + exportedMetadata, ]) useEffect(() => { @@ -246,7 +250,7 @@ export const OffChainMetadataUploadDetails = ({ setUploadMethod(importedOffChainMetadataUploadDetails.uploadMethod) tokenUriState.onChange(importedOffChainMetadataUploadDetails.tokenURI || '') coverImageUrlState.onChange(importedOffChainMetadataUploadDetails.imageUrl || '') - setOpenEditionMinterMetadataFile(importedOffChainMetadataUploadDetails.openEditionMinterMetadataFile) + //setOpenEditionMinterMetadataFile(importedOffChainMetadataUploadDetails.openEditionMinterMetadataFile) } }, [importedOffChainMetadataUploadDetails]) @@ -464,6 +468,8 @@ export const OffChainMetadataUploadDetails = ({ /> </div> <MetadataInput + importedMetadata={importedOffChainMetadataUploadDetails?.exportedMetadata} + onChange={setExportedMetadata} selectedAssetFile={assetFilesArray[0]} selectedMetadataFile={metadataFilesArray[0]} updateMetadataToUpload={updateOpenEditionMinterMetadataFile} From 58d2a4abd772a4e1a83c80ab972eb4d28de9f836 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Thu, 17 Aug 2023 14:21:30 +0300 Subject: [PATCH 36/51] Export/Import selected mint token --- components/collections/creation/MintingDetails.tsx | 2 ++ components/openEdition/MintingDetails.tsx | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/components/collections/creation/MintingDetails.tsx b/components/collections/creation/MintingDetails.tsx index ca6187b..1e17ce9 100644 --- a/components/collections/creation/MintingDetails.tsx +++ b/components/collections/creation/MintingDetails.tsx @@ -122,6 +122,7 @@ export const MintingDetails = ({ perAddressLimitState.onChange(importedMintingDetails.perAddressLimit) setTimestamp(new Date(Number(importedMintingDetails.startTime) / 1_000_000)) paymentAddressState.onChange(importedMintingDetails.paymentAddress ? importedMintingDetails.paymentAddress : '') + setSelectedMintToken(tokensList.find((token) => token.id === importedMintingDetails.selectedMintToken?.id)) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [importedMintingDetails]) @@ -140,6 +141,7 @@ export const MintingDetails = ({ <select className="py-[9px] px-4 mt-14 ml-2 placeholder:text-white/50 bg-white/10 rounded border-2 border-white/20 focus:ring focus:ring-plumbus-20" onChange={(e) => setSelectedMintToken(tokensList.find((t) => t.displayName === e.target.value))} + value={selectedMintToken?.displayName} > {vendingMinterList .filter((minter) => minter.factoryAddress !== undefined && minter.updatable === false) diff --git a/components/openEdition/MintingDetails.tsx b/components/openEdition/MintingDetails.tsx index 6bb9338..3da1036 100644 --- a/components/openEdition/MintingDetails.tsx +++ b/components/openEdition/MintingDetails.tsx @@ -43,6 +43,7 @@ export const MintingDetails = ({ const [timestamp, setTimestamp] = useState<Date | undefined>() const [endTimestamp, setEndTimestamp] = useState<Date | undefined>() const [selectedMintToken, setSelectedMintToken] = useState<TokenInfo | undefined>(stars) + const [mintingDetailsImported, setMintingDetailsImported] = useState(false) const unitPriceState = useNumberInputState({ id: 'unitPrice', @@ -77,7 +78,9 @@ export const MintingDetails = ({ } useEffect(() => { - void resolvePaymentAddress() + if (!importedMintingDetails || (importedMintingDetails && mintingDetailsImported)) { + void resolvePaymentAddress() + } }, [paymentAddressState.value]) useEffect(() => { @@ -106,11 +109,14 @@ export const MintingDetails = ({ useEffect(() => { if (importedMintingDetails) { - unitPriceState.onChange(Number(importedMintingDetails.unitPrice)) + console.log('Selected Token ID: ', importedMintingDetails.selectedMintToken?.id) + unitPriceState.onChange(Number(importedMintingDetails.unitPrice) / 1000000) perAddressLimitState.onChange(importedMintingDetails.perAddressLimit) setTimestamp(new Date(Number(importedMintingDetails.startTime) / 1_000_000)) setEndTimestamp(new Date(Number(importedMintingDetails.endTime) / 1_000_000)) paymentAddressState.onChange(importedMintingDetails.paymentAddress ? importedMintingDetails.paymentAddress : '') + setSelectedMintToken(tokensList.find((token) => token.id === importedMintingDetails.selectedMintToken?.id)) + setMintingDetailsImported(true) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [importedMintingDetails]) @@ -123,6 +129,7 @@ export const MintingDetails = ({ <select className="py-[9px] px-4 mt-14 ml-4 placeholder:text-white/50 bg-white/10 rounded border-2 border-white/20 focus:ring focus:ring-plumbus-20" onChange={(e) => setSelectedMintToken(tokensList.find((t) => t.displayName === e.target.value))} + value={selectedMintToken?.displayName} > {openEditionMinterList .filter((minter) => minter.factoryAddress !== undefined && minter.updatable === false) From 8b902a1078e72ed6b2bebceef8116250cdd33840 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Thu, 17 Aug 2023 14:22:05 +0300 Subject: [PATCH 37/51] Address royalty address import issue for open edition --- components/openEdition/RoyaltyDetails.tsx | 40 +++++++++++++---------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/components/openEdition/RoyaltyDetails.tsx b/components/openEdition/RoyaltyDetails.tsx index 17f32d3..039e9b6 100644 --- a/components/openEdition/RoyaltyDetails.tsx +++ b/components/openEdition/RoyaltyDetails.tsx @@ -23,6 +23,7 @@ type RoyaltyState = 'none' | 'new' export const RoyaltyDetails = ({ onChange, importedRoyaltyDetails }: RoyaltyDetailsProps) => { const wallet = useWallet() const [royaltyState, setRoyaltyState] = useState<RoyaltyState>('none') + const [royaltyDetailsImported, setRoyaltyDetailsImported] = useState(false) const royaltyPaymentAddressState = useInputState({ id: 'royalty-payment-address', @@ -41,31 +42,34 @@ export const RoyaltyDetails = ({ onChange, importedRoyaltyDetails }: RoyaltyDeta }) useEffect(() => { - void resolveAddress( - royaltyPaymentAddressState.value - .toLowerCase() - .replace(/,/g, '') - .replace(/"/g, '') - .replace(/'/g, '') - .replace(/ /g, ''), - wallet, - ).then((royaltyPaymentAddress) => { - royaltyPaymentAddressState.onChange(royaltyPaymentAddress) - const data: RoyaltyDetailsDataProps = { - royaltyType: royaltyState, - paymentAddress: royaltyPaymentAddressState.value, - share: Number(royaltyShareState.value), - } - onChange(data) - }) + if (!importedRoyaltyDetails || (importedRoyaltyDetails && royaltyDetailsImported)) { + void resolveAddress( + royaltyPaymentAddressState.value + .toLowerCase() + .replace(/,/g, '') + .replace(/"/g, '') + .replace(/'/g, '') + .replace(/ /g, ''), + wallet, + ).then((royaltyPaymentAddress) => { + royaltyPaymentAddressState.onChange(royaltyPaymentAddress) + const data: RoyaltyDetailsDataProps = { + royaltyType: royaltyState, + paymentAddress: royaltyPaymentAddressState.value, + share: Number(royaltyShareState.value), + } + onChange(data) + }) + } // eslint-disable-next-line react-hooks/exhaustive-deps }, [royaltyState, royaltyPaymentAddressState.value, royaltyShareState.value]) useEffect(() => { if (importedRoyaltyDetails) { setRoyaltyState(importedRoyaltyDetails.royaltyType) - royaltyPaymentAddressState.onChange(importedRoyaltyDetails.paymentAddress) + royaltyPaymentAddressState.onChange(importedRoyaltyDetails.paymentAddress.toString()) royaltyShareState.onChange(importedRoyaltyDetails.share.toString()) + setRoyaltyDetailsImported(true) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [importedRoyaltyDetails]) From e26253fec5027757b457afc9d440ad0a06c37b5b Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Fri, 18 Aug 2023 13:10:22 +0300 Subject: [PATCH 38/51] Badge creation video asset support --- components/badges/creation/BadgeDetails.tsx | 7 +++- .../badges/creation/ImageUploadDetails.tsx | 40 ++++++++++++++----- pages/badges/create.tsx | 8 +++- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/components/badges/creation/BadgeDetails.tsx b/components/badges/creation/BadgeDetails.tsx index 652143b..58fef4c 100644 --- a/components/badges/creation/BadgeDetails.tsx +++ b/components/badges/creation/BadgeDetails.tsx @@ -44,7 +44,7 @@ export interface BadgeDetailsDataProps { youtube_url?: string } -export const BadgeDetails = ({ metadataSize, onChange }: BadgeDetailsProps) => { +export const BadgeDetails = ({ metadataSize, onChange, uploadMethod }: BadgeDetailsProps) => { const wallet = useWallet() const [timestamp, setTimestamp] = useState<Date | undefined>(undefined) const [transferrable, setTransferrable] = useState<boolean>(false) @@ -192,6 +192,10 @@ export const BadgeDetails = ({ metadataSize, onChange }: BadgeDetailsProps) => { }) }, [metadataFile]) + useEffect(() => { + animationUrlState.onChange('') + }, [uploadMethod]) + useEffect(() => { try { const data: BadgeDetailsDataProps = { @@ -266,6 +270,7 @@ export const BadgeDetails = ({ metadataSize, onChange }: BadgeDetailsProps) => { <TextInput className="mt-2" {...nameState} /> <TextInput className="mt-2" {...descriptionState} /> <NumberInput className="mt-2" {...maxSupplyState} /> + {uploadMethod === 'existing' ? <TextInput className="mt-2" {...animationUrlState} /> : null} <TextInput className="mt-2" {...externalUrlState} /> <FormControl className="mt-2" htmlId="expiry-date" subtitle="Badge minting expiry date" title="Expiry Date"> diff --git a/components/badges/creation/ImageUploadDetails.tsx b/components/badges/creation/ImageUploadDetails.tsx index 180aa06..a6b1851 100644 --- a/components/badges/creation/ImageUploadDetails.tsx +++ b/components/badges/creation/ImageUploadDetails.tsx @@ -1,5 +1,5 @@ /* eslint-disable eslint-comments/disable-enable-pair */ - +/* eslint-disable jsx-a11y/media-has-caption */ /* eslint-disable no-misleading-character-class */ /* eslint-disable no-control-regex */ @@ -10,9 +10,10 @@ import { TextInput } from 'components/forms/FormInput' import { useInputState } from 'components/forms/FormInput.hooks' import { SingleAssetPreview } from 'components/SingleAssetPreview' import type { ChangeEvent } from 'react' -import { useEffect, useRef, useState } from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { toast } from 'react-hot-toast' import type { UploadServiceType } from 'services/upload' +import { getAssetType } from 'utils/getAssetType' export type UploadMethod = 'new' | 'existing' export type MintRule = 'by_key' | 'by_minter' | 'by_keys' | 'not_resolved' @@ -129,6 +130,22 @@ export const ImageUploadDetails = ({ onChange, mintRule }: ImageUploadDetailsPro imageUrlState.onChange('') }, [uploadMethod, mintRule]) + const videoPreview = useMemo( + () => ( + <video + className="ml-4" + controls + id="video" + onMouseEnter={(e) => e.currentTarget.play()} + onMouseLeave={(e) => e.currentTarget.pause()} + src={ + imageUrlState.value ? imageUrlState.value.replace('ipfs://', 'https://ipfs-gw.stargaze-apis.com/ipfs/') : '' + } + /> + ), + [imageUrlState.value], + ) + return ( <div className="justify-items-start mb-3 rounded border-2 border-white/20 flex-column"> <div className="flex justify-center"> @@ -190,13 +207,16 @@ export const ImageUploadDetails = ({ onChange, mintRule }: ImageUploadDetailsPro <div className="flex flex-row w-full"> <TextInput {...imageUrlState} className="mt-2 ml-6 w-full max-w-2xl" /> <Conditional test={imageUrlState.value !== ''}> - <div className="mt-2 ml-4 w-1/4 border-2 border-dashed"> - <img - alt="badge-preview" - className="w-full" - src={imageUrlState.value.replace('IPFS://', 'ipfs://').replace(/,/g, '').replace(/"/g, '').trim()} - /> - </div> + {getAssetType(imageUrlState.value) === 'image' && ( + <div className="mt-2 ml-4 w-1/4 border-2 border-dashed"> + <img + alt="badge-preview" + className="w-full" + src={imageUrlState.value.replace('IPFS://', 'ipfs://').replace(/,/g, '').replace(/"/g, '').trim()} + /> + </div> + )} + {getAssetType(imageUrlState.value) === 'video' && videoPreview} </Conditional> </div> </div> @@ -277,7 +297,7 @@ export const ImageUploadDetails = ({ onChange, mintRule }: ImageUploadDetailsPro )} > <input - accept="image/*" + accept="image/*, video/*" className={clsx( 'file:py-2 file:px-4 file:mr-4 file:bg-plumbus-light file:rounded file:border-0 cursor-pointer', 'before:absolute before:inset-0 before:hover:bg-white/5 before:transition', diff --git a/pages/badges/create.tsx b/pages/badges/create.tsx index 61f39ad..85aecd0 100644 --- a/pages/badges/create.tsx +++ b/pages/badges/create.tsx @@ -41,6 +41,7 @@ import * as secp256k1 from 'secp256k1' import { upload } from 'services/upload' import { copy } from 'utils/clipboard' import { BADGE_HUB_ADDRESS, BLOCK_EXPLORER_URL, NETWORK } from 'utils/constants' +import { getAssetType } from 'utils/getAssetType' import { withMetadata } from 'utils/layout' import { links } from 'utils/links' import { uid } from 'utils/random' @@ -184,7 +185,6 @@ const BadgeCreationPage: NextPage = () => { if (!badgeHubContract) throw new Error('Contract not found') setCreatingBadge(true) const coverUrl = await handleImageUrl() - const badge = { manager: badgeDetails?.manager as string, metadata: { @@ -195,7 +195,11 @@ const BadgeCreationPage: NextPage = () => { external_url: badgeDetails?.external_url || undefined, attributes: badgeDetails?.attributes || undefined, background_color: badgeDetails?.background_color || undefined, - animation_url: badgeDetails?.animation_url || undefined, + animation_url: badgeDetails?.animation_url + ? badgeDetails.animation_url + : imageUploadDetails?.assetFile && getAssetType(imageUploadDetails.assetFile.name) === 'video' + ? coverUrl + : undefined, youtube_url: badgeDetails?.youtube_url || undefined, }, transferrable: badgeDetails?.transferrable as boolean, From 0ed370aa67fae56bb696c129e0830625889fcc88 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 21 Aug 2023 10:59:10 +0300 Subject: [PATCH 39/51] Check end time during open edition creation --- components/openEdition/OpenEditionMinterCreator.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/components/openEdition/OpenEditionMinterCreator.tsx b/components/openEdition/OpenEditionMinterCreator.tsx index 4253c56..d7acb34 100644 --- a/components/openEdition/OpenEditionMinterCreator.tsx +++ b/components/openEdition/OpenEditionMinterCreator.tsx @@ -303,6 +303,7 @@ export const OpenEditionMinterCreator = ({ if (!mintingDetails.perAddressLimit || mintingDetails.perAddressLimit < 1 || mintingDetails.perAddressLimit > 50) throw new Error('Invalid limit for tokens per address') if (mintingDetails.startTime === '') throw new Error('Start time is required') + if (mintingDetails.endTime === '') throw new Error('End time is required') if (Number(mintingDetails.startTime) < new Date().getTime() * 1000000) throw new Error('Invalid start time') if ( mintingDetails.paymentAddress && From 8990175b03cc07c496611d39c54eec579b725500 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 21 Aug 2023 11:37:19 +0300 Subject: [PATCH 40/51] Check end time during open edition creation - 2 --- components/openEdition/OpenEditionMinterCreator.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/openEdition/OpenEditionMinterCreator.tsx b/components/openEdition/OpenEditionMinterCreator.tsx index d7acb34..46e4363 100644 --- a/components/openEdition/OpenEditionMinterCreator.tsx +++ b/components/openEdition/OpenEditionMinterCreator.tsx @@ -305,6 +305,11 @@ export const OpenEditionMinterCreator = ({ if (mintingDetails.startTime === '') throw new Error('Start time is required') if (mintingDetails.endTime === '') throw new Error('End time is required') if (Number(mintingDetails.startTime) < new Date().getTime() * 1000000) throw new Error('Invalid start time') + if (Number(mintingDetails.endTime) < Number(mintingDetails.startTime)) + throw new Error('End time cannot be earlier than start time') + if (Number(mintingDetails.endTime) === Number(mintingDetails.startTime)) + throw new Error('End time cannot be equal to the start time') + if ( mintingDetails.paymentAddress && (!isValidAddress(mintingDetails.paymentAddress) || !mintingDetails.paymentAddress.startsWith('stars1')) From 96dda936ae6d95fc3b154d5d24c6b6ba76192de2 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 21 Aug 2023 12:07:11 +0300 Subject: [PATCH 41/51] Address OE collection empty metadata file issue --- components/MetadataInput.tsx | 42 ++++++++++--------- .../OffChainMetadataUploadDetails.tsx | 2 +- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/components/MetadataInput.tsx b/components/MetadataInput.tsx index 2340d3f..0e870be 100644 --- a/components/MetadataInput.tsx +++ b/components/MetadataInput.tsx @@ -158,7 +158,7 @@ export const MetadataInput = (props: MetadataInputProps) => { } else if (!props.importedMetadata) { void parseMetadata(emptyMetadataFile) } - }, [props.selectedMetadataFile?.name]) + }, [props.selectedMetadataFile?.name, props.importedMetadata]) const nameStateMemo = useMemo(() => nameState, [nameState.value]) const descriptionStateMemo = useMemo(() => descriptionState, [descriptionState.value]) @@ -183,26 +183,28 @@ export const MetadataInput = (props: MetadataInputProps) => { useEffect(() => { if (props.importedMetadata) { - console.log('Imported metadata: ', props.importedMetadata) - nameState.onChange(props.importedMetadata.name || '') - descriptionState.onChange(props.importedMetadata.description || '') - externalUrlState.onChange(props.importedMetadata.external_url || '') - youtubeUrlState.onChange(props.importedMetadata.youtube_url || '') - if (props.importedMetadata?.attributes && props.importedMetadata?.attributes?.length > 0) { - attributesState.reset() - props.importedMetadata?.attributes?.forEach((attribute: { trait_type: string; value: string }) => { - attributesState.add({ - trait_type: attribute.trait_type, - value: attribute.value, + void parseMetadata(emptyMetadataFile).then(() => { + console.log('Imported metadata: ', props.importedMetadata) + nameState.onChange(props.importedMetadata.name || '') + descriptionState.onChange(props.importedMetadata.description || '') + externalUrlState.onChange(props.importedMetadata.external_url || '') + youtubeUrlState.onChange(props.importedMetadata.youtube_url || '') + if (props.importedMetadata?.attributes && props.importedMetadata?.attributes?.length > 0) { + attributesState.reset() + props.importedMetadata?.attributes?.forEach((attribute: { trait_type: string; value: string }) => { + attributesState.add({ + trait_type: attribute.trait_type, + value: attribute.value, + }) }) - }) - } else { - attributesState.reset() - attributesState.add({ - trait_type: '', - value: '', - }) - } + } else { + attributesState.reset() + attributesState.add({ + trait_type: '', + value: '', + }) + } + }) } }, [props.importedMetadata]) diff --git a/components/openEdition/OffChainMetadataUploadDetails.tsx b/components/openEdition/OffChainMetadataUploadDetails.tsx index abe06f6..e317a8e 100644 --- a/components/openEdition/OffChainMetadataUploadDetails.tsx +++ b/components/openEdition/OffChainMetadataUploadDetails.tsx @@ -250,7 +250,7 @@ export const OffChainMetadataUploadDetails = ({ setUploadMethod(importedOffChainMetadataUploadDetails.uploadMethod) tokenUriState.onChange(importedOffChainMetadataUploadDetails.tokenURI || '') coverImageUrlState.onChange(importedOffChainMetadataUploadDetails.imageUrl || '') - //setOpenEditionMinterMetadataFile(importedOffChainMetadataUploadDetails.openEditionMinterMetadataFile) + // setOpenEditionMinterMetadataFile(importedOffChainMetadataUploadDetails.openEditionMinterMetadataFile) } }, [importedOffChainMetadataUploadDetails]) From ae9aec3bd891f83397512919b68f71f363b39466 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 21 Aug 2023 12:40:54 +0300 Subject: [PATCH 42/51] Disable default upload method when importing --- components/collections/creation/UploadDetails.tsx | 2 +- pages/collections/create.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/collections/creation/UploadDetails.tsx b/components/collections/creation/UploadDetails.tsx index 0b8aa61..519f879 100644 --- a/components/collections/creation/UploadDetails.tsx +++ b/components/collections/creation/UploadDetails.tsx @@ -289,7 +289,7 @@ export const UploadDetails = ({ useEffect(() => { if (importedUploadDetails) { if (importedUploadDetails.uploadMethod === 'new') { - setUploadMethod('existing') + setUploadMethod('new') setUploadService(importedUploadDetails.uploadService) nftStorageApiKeyState.onChange(importedUploadDetails.nftStorageApiKey || '') pinataApiKeyState.onChange(importedUploadDetails.pinataApiKey || '') diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index 0053acd..bc83c42 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -1322,7 +1322,7 @@ const CollectionCreationPage: NextPage = () => { details.uploadDetails.baseTokenURI = details.baseTokenUri details.uploadDetails.imageUrl = details.coverImageUrl } - if (details.openEditionMinterDetails.openEditionMinterContractAddress) { + if (details.openEditionMinterDetails?.openEditionMinterContractAddress) { details.openEditionMinterDetails.offChainMetadataUploadDetails.uploadMethod = 'existing' details.openEditionMinterDetails.offChainMetadataUploadDetails.tokenURI = details.openEditionMinterDetails.tokenUri From 4cc6fdc0700245c9568fb9ba9a32d1736006beb8 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 21 Aug 2023 14:59:12 +0300 Subject: [PATCH 43/51] Update import/export component placement --- .../openEdition/OpenEditionMinterCreator.tsx | 2 +- pages/collections/create.tsx | 43 +++++++++++++------ 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/components/openEdition/OpenEditionMinterCreator.tsx b/components/openEdition/OpenEditionMinterCreator.tsx index 46e4363..bf2ab74 100644 --- a/components/openEdition/OpenEditionMinterCreator.tsx +++ b/components/openEdition/OpenEditionMinterCreator.tsx @@ -713,7 +713,7 @@ export const OpenEditionMinterCreator = ({ </div> </div> </Conditional> - <div className={clsx('my-4 mx-10')}> + <div className={clsx('my-0 mx-10')}> <Conditional test={metadataStorageMethod === 'off-chain'}> <div> <OffChainMetadataUploadDetails diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index bc83c42..5311fba 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -30,6 +30,7 @@ import type { RoyaltyDetailsDataProps } from 'components/collections/creation/Ro import type { UploadDetailsDataProps } from 'components/collections/creation/UploadDetails' import type { WhitelistDetailsDataProps } from 'components/collections/creation/WhitelistDetails' import { Conditional } from 'components/Conditional' +import { FormControl } from 'components/FormControl' import { LoadingModal } from 'components/LoadingModal' import type { OpenEditionMinterCreatorDataProps } from 'components/openEdition/OpenEditionMinterCreator' import { OpenEditionMinterCreator } from 'components/openEdition/OpenEditionMinterCreator' @@ -1305,12 +1306,20 @@ const CollectionCreationPage: NextPage = () => { const element = document.createElement('a') const file = new Blob([JSON.stringify(details)], { type: 'text/plain' }) element.href = URL.createObjectURL(file) - element.download = 'details.json' + element.download = `${ + minterType === 'vending' + ? collectionDetails?.name + ? `${collectionDetails.name}-` + : '' + : openEditionMinterDetails?.collectionDetails + ? `${openEditionMinterDetails.collectionDetails.name}-` + : '' + }configuration-${new Date().toLocaleString().replaceAll(',', '_')}.json` document.body.appendChild(element) // Required for this to work in FireFox element.click() } const importDetails = (event: ChangeEvent<HTMLInputElement>) => { - if (event.target.files === null) return toast.error('No files selected.') + if (event.target.files === null || event.target.files.length === 0) return toast.error('No files selected.') const file = event.target.files[0] const reader = new FileReader() reader.onload = (e) => { @@ -1408,15 +1417,7 @@ const CollectionCreationPage: NextPage = () => { : 'Create Collection' } /> - <Button className="absolute top-5 right-5" onClick={() => exportDetails()}> - Export Details - </Button> - <input - accept="application/json" - className="absolute top-5 right-20" - onChange={(e) => importDetails(e)} - type="file" - /> + <div className="mt-5 space-y-5 text-center"> <h1 className="font-heading text-4xl font-bold"> {minterType === 'base' && baseMinterDetails?.baseMinterAcquisitionMethod === 'existing' @@ -1436,6 +1437,7 @@ const CollectionCreationPage: NextPage = () => { on how to create your collection </p> </div> + <div className="mx-10" ref={scrollRef}> <Conditional test={minterType === 'openEdition' && openEditionMinterCreatorData?.openEditionMinterContractAddress !== null} @@ -1674,7 +1676,8 @@ const CollectionCreationPage: NextPage = () => { className={clsx( 'mx-10 mt-5', 'grid before:absolute relative grid-cols-3 grid-flow-col items-stretch rounded', - 'before:inset-x-0 before:bottom-0 before:border-white/25', + 'before:inset-x-0 before:bottom-0 before:border-white/25', + minterType !== 'base' ? 'rounded-none border-b-2 border-white/25' : 'border-0', )} > <div @@ -1748,6 +1751,22 @@ const CollectionCreationPage: NextPage = () => { </div> </div> + <Conditional test={minterType !== 'base'}> + <FormControl className={clsx('py-4 px-10 w-full')} title="Import Creation Configuration"> + <div className="flex flex-row justify-between mt-5 space-x-2"> + <input + accept="application/json" + className="py-4 px-4 w-1/3 rounded-sm border-[1px] border-zinc-500 border-dashed" + onChange={(e) => importDetails(e)} + type="file" + /> + <Button className="mt-3 h-1/2 w-1/8" onClick={() => exportDetails()}> + Export Creation Configuration + </Button> + </div> + </FormControl> + </Conditional> + {minterType === 'base' && ( <div> <BaseMinterDetails minterType={minterType} onChange={setBaseMinterDetails} /> From 5578c408a51a666b864690f39cd16322ab7422e1 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 21 Aug 2023 15:00:06 +0300 Subject: [PATCH 44/51] Bump Studio version --- .env.example | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index ffe2835..170dd6e 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -APP_VERSION=0.7.3 +APP_VERSION=0.7.4 NEXT_PUBLIC_PINATA_ENDPOINT_URL=https://api.pinata.cloud/pinning/pinFileToIPFS NEXT_PUBLIC_SG721_CODE_ID=2595 diff --git a/package.json b/package.json index ff54c94..acc68fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "stargaze-studio", - "version": "0.7.3", + "version": "0.7.4", "workspaces": [ "packages/*" ], From f25807f35579cbb1e2df4892ae2d7d19242f56b7 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 21 Aug 2023 16:29:52 +0300 Subject: [PATCH 45/51] Unmicro whitelist unit price --- components/collections/creation/WhitelistDetails.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/collections/creation/WhitelistDetails.tsx b/components/collections/creation/WhitelistDetails.tsx index aa0d908..880bf7b 100644 --- a/components/collections/creation/WhitelistDetails.tsx +++ b/components/collections/creation/WhitelistDetails.tsx @@ -160,7 +160,9 @@ export const WhitelistDetails = ({ whitelistAddressState.onChange( importedWhitelistDetails.contractAddress ? importedWhitelistDetails.contractAddress : '', ) - unitPriceState.onChange(importedWhitelistDetails.unitPrice ? Number(importedWhitelistDetails.unitPrice) : 0) + unitPriceState.onChange( + importedWhitelistDetails.unitPrice ? Number(importedWhitelistDetails.unitPrice) / 1000000 : 0, + ) memberLimitState.onChange(importedWhitelistDetails.memberLimit ? importedWhitelistDetails.memberLimit : 0) perAddressLimitState.onChange( importedWhitelistDetails.perAddressLimit ? importedWhitelistDetails.perAddressLimit : 0, From 1ca1d08b2ac7991d67b265286ee822cb63106f22 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 21 Aug 2023 16:43:05 +0300 Subject: [PATCH 46/51] Reset upload details on import for 1/1 collections --- components/collections/creation/UploadDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/collections/creation/UploadDetails.tsx b/components/collections/creation/UploadDetails.tsx index 519f879..bb99e3b 100644 --- a/components/collections/creation/UploadDetails.tsx +++ b/components/collections/creation/UploadDetails.tsx @@ -280,7 +280,7 @@ export const UploadDetails = ({ setMetadataFilesArray([]) if (assetFilesRef.current) assetFilesRef.current.value = '' setAssetFilesArray([]) - if (!importedUploadDetails) { + if (!importedUploadDetails || minterType === 'base') { baseTokenUriState.onChange('') coverImageUrlState.onChange('') } From 391b712bde688dcb01b923ec7cf151122b2e957f Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Mon, 21 Aug 2023 19:45:23 +0300 Subject: [PATCH 47/51] Prevent duplicate protocol in the base token uri --- pages/collections/create.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index 5311fba..4ba9555 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -1297,7 +1297,7 @@ const CollectionCreationPage: NextPage = () => { baseMinterDetails, openEditionMinterDetails, vendingMinterContractAddress, - baseTokenUri: `ipfs://${baseTokenUri}`, + baseTokenUri: `${baseTokenUri?.startsWith('ipfs://') ? baseTokenUri : `ipfs://${baseTokenUri}`}`, coverImageUrl: uploadDetails?.uploadMethod === 'new' ? `ipfs://${coverImageUrl}/${collectionDetails?.imageFile[0]?.name as string}` From 26a54235999486631dd56547cf41f3ece4b6dc42 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Tue, 22 Aug 2023 21:52:31 +0300 Subject: [PATCH 48/51] Address upload issue when token metadata lacks a description --- components/openEdition/OpenEditionMinterCreator.tsx | 6 ++++-- pages/collections/create.tsx | 12 ++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/components/openEdition/OpenEditionMinterCreator.tsx b/components/openEdition/OpenEditionMinterCreator.tsx index 1c62c29..190c279 100644 --- a/components/openEdition/OpenEditionMinterCreator.tsx +++ b/components/openEdition/OpenEditionMinterCreator.tsx @@ -468,8 +468,10 @@ export const OpenEditionMinterCreator = ({ if (getAssetType(offChainMetadataUploadDetails.assetFiles[0].name) !== 'html') data.image = `ipfs://${assetUri}/${offChainMetadataUploadDetails.assetFiles[0].name}` - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - data.description = data.description.replaceAll('\\n', '\n') + if (data.description) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + data.description = data.description.replaceAll('\\n', '\n') + } const metadataFileBlob = new Blob([JSON.stringify(data)], { type: 'application/json', }) diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index 008830f..2137308 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -741,8 +741,10 @@ const CollectionCreationPage: NextPage = () => { if (getAssetType(uploadDetails.assetFiles[i].name) !== 'html') data.image = `ipfs://${assetUri}/${uploadDetails.assetFiles[i].name}` - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - data.description = data.description.replaceAll('\\n', '\n') + if (data.description) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + data.description = data.description.replaceAll('\\n', '\n') + } const metadataFileBlob = new Blob([JSON.stringify(data)], { type: 'application/json', }) @@ -797,8 +799,10 @@ const CollectionCreationPage: NextPage = () => { type: 'application/json', }) - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - data.description = data.description.replaceAll('\\n', '\n') + if (data.description) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + data.description = data.description.replaceAll('\\n', '\n') + } console.log('Name: ', (uploadDetails.baseMinterMetadataFile as File).name) const updatedMetadataFile = new File( [metadataFileBlob], From f62348df0c1e51457e654997469861e3e90026d2 Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Fri, 25 Aug 2023 18:24:18 +0300 Subject: [PATCH 49/51] Update USDC denom for testnet --- config/token.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/config/token.ts b/config/token.ts index 7cb99b6..6513b8d 100644 --- a/config/token.ts +++ b/config/token.ts @@ -25,7 +25,10 @@ export const ibcAtom: TokenInfo = { export const ibcUsdc: TokenInfo = { id: 'ibc-usdc', - denom: 'ibc/D189335C6E4A68B513C10AB227BF1C1D38C746766278BA3EEB4FB14124F1D858', + denom: + NETWORK === 'mainnet' + ? 'ibc/D189335C6E4A68B513C10AB227BF1C1D38C746766278BA3EEB4FB14124F1D858' + : 'factory/stars1s8qx0zvz8yd6e4x0mqmqf7fr9vvfn622wtp3g3/uusdc', displayName: 'USDC', decimalPlaces: 6, } From 1c689cbb19394cf8007cd337b46370b13aabbedb Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Sat, 26 Aug 2023 18:28:05 +0300 Subject: [PATCH 50/51] Update enable updatable fee --- contracts/sg721/contract.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/sg721/contract.ts b/contracts/sg721/contract.ts index 91a7b38..6963d13 100644 --- a/contracts/sg721/contract.ts +++ b/contracts/sg721/contract.ts @@ -719,7 +719,7 @@ export const SG721 = (client: SigningCosmWasmClient, txSigner: string): SG721Con }, 'auto', '', - [coin('500000000', 'ustars')], + [coin('2000000000', 'ustars')], ) return res.transactionHash } @@ -1018,7 +1018,7 @@ export const SG721 = (client: SigningCosmWasmClient, txSigner: string): SG721Con msg: { enable_updatable: {}, }, - funds: [coin('500000000', 'ustars')], + funds: [coin('2000000000', 'ustars')], } } From bf697745d53273e3e6ed8c3f73969c516841fc0a Mon Sep 17 00:00:00 2001 From: Serkan Reis <serkanreis@gmail.com> Date: Tue, 29 Aug 2023 14:13:57 +0300 Subject: [PATCH 51/51] Fix factory switching related issues --- components/openEdition/OpenEditionMinterCreator.tsx | 8 +++++++- pages/collections/create.tsx | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/components/openEdition/OpenEditionMinterCreator.tsx b/components/openEdition/OpenEditionMinterCreator.tsx index 190c279..c722c8f 100644 --- a/components/openEdition/OpenEditionMinterCreator.tsx +++ b/components/openEdition/OpenEditionMinterCreator.tsx @@ -123,7 +123,13 @@ export const OpenEditionMinterCreator = ({ openEditionFactoryContract?.use( collectionDetails?.updatable ? updatableFactoryAddressForSelectedDenom : factoryAddressForSelectedDenom, ), - [openEditionFactoryContract, wallet.address], + [ + openEditionFactoryContract, + wallet.address, + collectionDetails?.updatable, + factoryAddressForSelectedDenom, + updatableFactoryAddressForSelectedDenom, + ], ) const performOpenEditionMinterChecks = () => { diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index 2137308..13896d7 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -124,7 +124,7 @@ const CollectionCreationPage: NextPage = () => { const vendingFactoryMessages = useMemo( () => vendingFactoryContract?.use(vendingFactoryAddress as string), - [vendingFactoryContract, wallet.address], + [vendingFactoryContract, wallet.address, vendingFactoryAddress], ) const baseFactoryMessages = useMemo(