Merge pull request #91 from public-awesome/develop

Sync development > main
This commit is contained in:
Serkan Reis 2023-01-17 16:48:15 +03:00 committed by GitHub
commit 4c6442595c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 182 additions and 73 deletions

View File

@ -1,4 +1,4 @@
APP_VERSION=0.3.8 APP_VERSION=0.3.9
NEXT_PUBLIC_PINATA_ENDPOINT_URL=https://api.pinata.cloud/pinning/pinFileToIPFS NEXT_PUBLIC_PINATA_ENDPOINT_URL=https://api.pinata.cloud/pinning/pinFileToIPFS
NEXT_PUBLIC_SG721_CODE_ID=274 NEXT_PUBLIC_SG721_CODE_ID=274

View File

@ -1,4 +1,5 @@
/* eslint-disable eslint-comments/disable-enable-pair */ /* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable no-nested-ternary */
/* eslint-disable no-misleading-character-class */ /* eslint-disable no-misleading-character-class */
/* eslint-disable no-control-regex */ /* eslint-disable no-control-regex */
/* eslint-disable @typescript-eslint/no-loop-func */ /* eslint-disable @typescript-eslint/no-loop-func */
@ -50,7 +51,6 @@ export const UploadDetails = ({ onChange, minterType, baseMinterAcquisitionMetho
const [metadataFileArrayIndex, setMetadataFileArrayIndex] = useState(0) const [metadataFileArrayIndex, setMetadataFileArrayIndex] = useState(0)
const [refreshMetadata, setRefreshMetadata] = useState(false) const [refreshMetadata, setRefreshMetadata] = useState(false)
//let baseMinterMetadataFile: File | undefined
const [baseMinterMetadataFile, setBaseMinterMetadataFile] = useState<File | undefined>() const [baseMinterMetadataFile, setBaseMinterMetadataFile] = useState<File | undefined>()
const assetFilesRef = useRef<HTMLInputElement | null>(null) const assetFilesRef = useRef<HTMLInputElement | null>(null)
@ -98,7 +98,7 @@ export const UploadDetails = ({ onChange, minterType, baseMinterAcquisitionMetho
setAssetFilesArray([]) setAssetFilesArray([])
setMetadataFilesArray([]) setMetadataFilesArray([])
if (event.target.files === null) return if (event.target.files === null) return
if (minterType === 'vending') { if (minterType === 'vending' || (minterType === 'base' && event.target.files.length > 1)) {
//sort the files //sort the files
const sortedFiles = Array.from(event.target.files).sort((a, b) => naturalCompare(a.name, b.name)) const sortedFiles = Array.from(event.target.files).sort((a, b) => naturalCompare(a.name, b.name))
//check if the sorted file names are in numerical order //check if the sorted file names are in numerical order
@ -137,11 +137,14 @@ export const UploadDetails = ({ onChange, minterType, baseMinterAcquisitionMetho
const selectMetadata = (event: ChangeEvent<HTMLInputElement>) => { const selectMetadata = (event: ChangeEvent<HTMLInputElement>) => {
setMetadataFilesArray([]) setMetadataFilesArray([])
if (event.target.files === null) return toast.error('No files selected.') if (event.target.files === null) return toast.error('No files selected.')
if (minterType === 'vending' && event.target.files.length !== assetFilesArray.length) { if (
(minterType === 'vending' || (minterType === 'base' && assetFilesArray.length > 1)) &&
event.target.files.length !== assetFilesArray.length
) {
event.target.value = '' event.target.value = ''
return toast.error('The number of metadata files should be equal to the number of asset files.') return toast.error('The number of metadata files should be equal to the number of asset files.')
} }
if (minterType === 'vending') { if (minterType === 'vending' || (minterType === 'base' && assetFilesArray.length > 1)) {
//sort the files //sort the files
const sortedFiles = Array.from(event.target.files).sort((a, b) => naturalCompare(a.name, b.name)) const sortedFiles = Array.from(event.target.files).sort((a, b) => naturalCompare(a.name, b.name))
//check if the sorted file names are in numerical order //check if the sorted file names are in numerical order
@ -149,7 +152,6 @@ export const UploadDetails = ({ onChange, minterType, baseMinterAcquisitionMetho
for (let i = 0; i < sortedFileNames.length; i++) { for (let i = 0; i < sortedFileNames.length; i++) {
if (isNaN(Number(sortedFileNames[i])) || parseInt(sortedFileNames[i]) !== i + 1) { if (isNaN(Number(sortedFileNames[i])) || parseInt(sortedFileNames[i]) !== i + 1) {
toast.error('The file names should be in numerical order starting from 1.') toast.error('The file names should be in numerical order starting from 1.')
//clear the input
event.target.value = '' event.target.value = ''
return return
} }
@ -445,7 +447,7 @@ export const UploadDetails = ({ onChange, minterType, baseMinterAcquisitionMetho
'before:absolute before:inset-0 before:hover:bg-white/5 before:transition', 'before:absolute before:inset-0 before:hover:bg-white/5 before:transition',
)} )}
id="assetFiles" id="assetFiles"
multiple={minterType === 'vending'} multiple
onChange={selectAssets} onChange={selectAssets}
ref={assetFilesRef} ref={assetFilesRef}
type="file" type="file"
@ -459,7 +461,11 @@ export const UploadDetails = ({ onChange, minterType, baseMinterAcquisitionMetho
className="block mt-5 mr-1 mb-1 ml-8 w-full font-bold text-white dark:text-gray-300" className="block mt-5 mr-1 mb-1 ml-8 w-full font-bold text-white dark:text-gray-300"
htmlFor="metadataFiles" htmlFor="metadataFiles"
> >
{minterType === 'vending' ? 'Metadata Selection' : 'Metadata Selection (optional)'} {minterType === 'vending'
? 'Metadata Selection'
: assetFilesArray.length === 1
? 'Metadata Selection (optional)'
: 'Metadata Selection'}
</label> </label>
<div <div
className={clsx( className={clsx(
@ -474,7 +480,7 @@ export const UploadDetails = ({ onChange, minterType, baseMinterAcquisitionMetho
'before:absolute before:inset-0 before:hover:bg-white/5 before:transition', 'before:absolute before:inset-0 before:hover:bg-white/5 before:transition',
)} )}
id="metadataFiles" id="metadataFiles"
multiple={minterType === 'vending'} multiple
onChange={selectMetadata} onChange={selectMetadata}
ref={metadataFilesRef} ref={metadataFilesRef}
type="file" type="file"
@ -482,7 +488,7 @@ export const UploadDetails = ({ onChange, minterType, baseMinterAcquisitionMetho
</div> </div>
</div> </div>
)} )}
<Conditional test={minterType === 'vending'}> <Conditional test={assetFilesArray.length > 1}>
<MetadataModal <MetadataModal
assetFile={assetFilesArray[metadataFileArrayIndex]} assetFile={assetFilesArray[metadataFileArrayIndex]}
metadataFile={metadataFilesArray[metadataFileArrayIndex]} metadataFile={metadataFilesArray[metadataFileArrayIndex]}
@ -496,14 +502,22 @@ export const UploadDetails = ({ onChange, minterType, baseMinterAcquisitionMetho
<AssetsPreview assetFilesArray={assetFilesArray} updateMetadataFileIndex={updateMetadataFileIndex} /> <AssetsPreview assetFilesArray={assetFilesArray} updateMetadataFileIndex={updateMetadataFileIndex} />
</Conditional> </Conditional>
<Conditional test={assetFilesArray.length > 0 && minterType === 'base'}> <Conditional test={assetFilesArray.length > 0 && minterType === 'base'}>
<SingleAssetPreview <Conditional test={assetFilesArray.length === 1}>
relatedAsset={assetFilesArray[0]} <SingleAssetPreview
subtitle={`Asset filename: ${assetFilesArray[0]?.name}`} relatedAsset={assetFilesArray[0]}
updateMetadataFileIndex={updateMetadataFileIndex} subtitle={`Asset filename: ${assetFilesArray[0]?.name}`}
/> updateMetadataFileIndex={updateMetadataFileIndex}
/>
</Conditional>
<Conditional test={assetFilesArray.length > 1}>
<AssetsPreview
assetFilesArray={assetFilesArray}
updateMetadataFileIndex={updateMetadataFileIndex}
/>
</Conditional>
</Conditional> </Conditional>
</div> </div>
<Conditional test={minterType === 'base' && assetFilesArray.length > 0}> <Conditional test={minterType === 'base' && assetFilesArray.length === 1}>
<MetadataInput <MetadataInput
selectedAssetFile={assetFilesArray[0]} selectedAssetFile={assetFilesArray[0]}
selectedMetadataFile={metadataFilesArray[0]} selectedMetadataFile={metadataFilesArray[0]}

View File

@ -1,8 +1,10 @@
import type { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate' import type { MsgExecuteContractEncodeObject, SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate'
import { toUtf8 } from '@cosmjs/encoding'
import type { Coin } from '@cosmjs/proto-signing' import type { Coin } from '@cosmjs/proto-signing'
import { coin } from '@cosmjs/proto-signing' import { coin } from '@cosmjs/proto-signing'
import type { logs } from '@cosmjs/stargate' import type { logs } from '@cosmjs/stargate'
import type { Timestamp } from '@stargazezone/types/contracts/minter/shared-types' import type { Timestamp } from '@stargazezone/types/contracts/minter/shared-types'
import { MsgExecuteContract } from 'cosmjs-types/cosmwasm/wasm/v1/tx'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import { BASE_FACTORY_ADDRESS } from 'utils/constants' import { BASE_FACTORY_ADDRESS } from 'utils/constants'
@ -32,11 +34,13 @@ export interface BaseMinterInstance {
//Execute //Execute
mint: (senderAddress: string, tokenUri: string) => Promise<string> mint: (senderAddress: string, tokenUri: string) => Promise<string>
updateStartTradingTime: (senderAddress: string, time?: Timestamp) => Promise<string> updateStartTradingTime: (senderAddress: string, time?: Timestamp) => Promise<string>
batchMint: (senderAddress: string, recipient: string, batchCount: number) => Promise<string>
} }
export interface BaseMinterMessages { export interface BaseMinterMessages {
mint: (tokenUri: string) => MintMessage mint: (tokenUri: string) => MintMessage
updateStartTradingTime: (time: Timestamp) => UpdateStartTradingTimeMessage updateStartTradingTime: (time: Timestamp) => UpdateStartTradingTimeMessage
batchMint: (recipient: string, batchNumber: number) => CustomMessage
} }
export interface MintMessage { export interface MintMessage {
@ -176,12 +180,52 @@ export const baseMinter = (client: SigningCosmWasmClient, txSigner: string): Bas
return res.transactionHash return res.transactionHash
} }
const batchMint = async (senderAddress: string, baseUri: string, batchCount: number): Promise<string> => {
const factoryParameters = await toast.promise(getFactoryParameters(), {
loading: 'Querying Factory Parameters...',
error: 'Querying Factory Parameters failed!',
success: 'Query successful! Minting...',
})
console.log(factoryParameters.params.mint_fee_bps)
const price = (await getConfig()).config?.mint_price.amount
if (!price) {
throw new Error(
'Unable to retrieve a valid mint price. It may be that the given contract address does not belong to a Base Minter contract.',
)
}
console.log((Number(price) * Number(factoryParameters.params.mint_fee_bps)) / 100)
const executeContractMsgs: MsgExecuteContractEncodeObject[] = []
for (let i = 0; i < batchCount; i++) {
const msg = {
mint: { token_uri: `${baseUri}/${i + 1}` },
}
const executeContractMsg: MsgExecuteContractEncodeObject = {
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
value: MsgExecuteContract.fromPartial({
sender: senderAddress,
contract: contractAddress,
msg: toUtf8(JSON.stringify(msg)),
funds: [coin((Number(price) * Number(factoryParameters.params.mint_fee_bps)) / 100 / 100, 'ustars')],
}),
}
executeContractMsgs.push(executeContractMsg)
}
const res = await client.signAndBroadcast(senderAddress, executeContractMsgs, 'auto', 'batch mint')
return res.transactionHash
}
return { return {
contractAddress, contractAddress,
getConfig, getConfig,
getStatus, getStatus,
mint, mint,
updateStartTradingTime, updateStartTradingTime,
batchMint,
} }
} }
@ -237,9 +281,23 @@ export const baseMinter = (client: SigningCosmWasmClient, txSigner: string): Bas
} }
} }
const batchMint = (baseUri: string, batchCount: number): CustomMessage => {
const msg: Record<string, unknown>[] = []
for (let i = 0; i < batchCount; i++) {
msg.push({ mint: { token_uri: `${baseUri}/${i + 1}` } })
}
return {
sender: txSigner,
contract: contractAddress,
msg,
funds: [],
}
}
return { return {
mint, mint,
updateStartTradingTime, updateStartTradingTime,
batchMint,
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "stargaze-studio", "name": "stargaze-studio",
"version": "0.3.8", "version": "0.3.9",
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
], ],

View File

@ -244,22 +244,28 @@ const CollectionCreationPage: NextPage = () => {
) )
setUploading(false) setUploading(false)
if (uploadDetails.assetFiles.length === 1) {
setBaseTokenUri( setBaseTokenUri(
`${baseUri}/${(uploadDetails.baseMinterMetadataFile as File).name.substring( `${baseUri}/${(uploadDetails.baseMinterMetadataFile as File).name.substring(
0, 0,
(uploadDetails.baseMinterMetadataFile as File).name.lastIndexOf('.'), (uploadDetails.baseMinterMetadataFile as File).name.lastIndexOf('.'),
)}`, )}`,
) )
} else {
setBaseTokenUri(baseUri)
}
setCoverImageUrl(coverImageUri) setCoverImageUrl(coverImageUri)
if (uploadDetails.assetFiles.length === 1) {
await instantiateBaseMinter( await instantiateBaseMinter(
`ipfs://${baseUri}/${(uploadDetails.baseMinterMetadataFile as File).name.substring( `ipfs://${baseUri}/${(uploadDetails.baseMinterMetadataFile as File).name.substring(
0, 0,
(uploadDetails.baseMinterMetadataFile as File).name.lastIndexOf('.'), (uploadDetails.baseMinterMetadataFile as File).name.lastIndexOf('.'),
)}`, )}`,
coverImageUri, coverImageUri,
) )
} else {
await instantiateBaseMinter(`ipfs://${baseUri}`, coverImageUri)
}
} else { } else {
setBaseTokenUri(uploadDetails?.baseTokenURI as string) setBaseTokenUri(uploadDetails?.baseTokenURI as string)
setCoverImageUrl(uploadDetails?.imageUrl as string) setCoverImageUrl(uploadDetails?.imageUrl as string)
@ -292,27 +298,35 @@ const CollectionCreationPage: NextPage = () => {
await uploadFiles() await uploadFiles()
.then(async (baseUri) => { .then(async (baseUri) => {
setUploading(false) setUploading(false)
setBaseTokenUri( if (uploadDetails.assetFiles.length === 1) {
`${baseUri}/${(uploadDetails.baseMinterMetadataFile as File).name.substring( setBaseTokenUri(
0, `${baseUri}/${(uploadDetails.baseMinterMetadataFile as File).name.substring(
(uploadDetails.baseMinterMetadataFile as File).name.lastIndexOf('.'),
)}`,
)
const result = await baseMinterContract
.use(baseMinterDetails?.existingBaseMinter as string)
?.mint(
wallet.address,
`ipfs://${baseUri}/${(uploadDetails.baseMinterMetadataFile as File).name.substring(
0, 0,
(uploadDetails.baseMinterMetadataFile as File).name.lastIndexOf('.'), (uploadDetails.baseMinterMetadataFile as File).name.lastIndexOf('.'),
)}`, )}`,
) )
const result = await baseMinterContract
.use(baseMinterDetails?.existingBaseMinter as string)
?.mint(
wallet.address,
`ipfs://${baseUri}/${(uploadDetails.baseMinterMetadataFile as File).name.substring(
0,
(uploadDetails.baseMinterMetadataFile as File).name.lastIndexOf('.'),
)}`,
)
console.log(result)
return result
}
setBaseTokenUri(baseUri)
const result = await baseMinterContract
.use(baseMinterDetails?.existingBaseMinter as string)
?.batchMint(wallet.address, `ipfs://${baseUri}`, uploadDetails.assetFiles.length)
console.log(result) console.log(result)
return result return result
}) })
.then((result) => { .then((result) => {
toast.success(`Token minted & appended to the collection successfully! Tx Hash: ${result}`, { toast.success(`Token(s) minted & appended to the collection successfully! Tx Hash: ${result}`, {
style: { maxWidth: 'none' }, style: { maxWidth: 'none' },
duration: 5000, duration: 5000,
}) })
@ -482,28 +496,51 @@ const CollectionCreationPage: NextPage = () => {
setTransactionHash(data.transactionHash) setTransactionHash(data.transactionHash)
setVendingMinterContractAddress(data.baseMinterAddress) setVendingMinterContractAddress(data.baseMinterAddress)
setSg721ContractAddress(data.sg721Address) setSg721ContractAddress(data.sg721Address)
await toast if (uploadDetails?.assetFiles.length === 1) {
.promise( await toast
baseMinterContract .promise(
.use(data.baseMinterAddress) baseMinterContract.use(data.baseMinterAddress)?.mint(wallet.address, baseUri) as Promise<string>,
{
?.mint(wallet.address, baseUri) as Promise<string>, loading: 'Minting token...',
{ success: (result) => {
loading: 'Minting token...', setIsMintingComplete(true)
success: (result) => { return `Token minted successfully! Tx Hash: ${result}`
setIsMintingComplete(true) },
return `Token minted successfully! Tx Hash: ${result}` error: (error) => `Failed to mint token: ${error.message}`,
}, },
error: (error) => `Failed to mint token: ${error.message}`, { style: { maxWidth: 'none' } },
}, )
{ style: { maxWidth: 'none' } }, .catch((error) => {
) toast.error(error.message, { style: { maxWidth: 'none' } })
.catch((error) => { setUploading(false)
toast.error(error.message, { style: { maxWidth: 'none' } }) setIsMintingComplete(false)
setUploading(false) setCreatingCollection(false)
setIsMintingComplete(false) })
setCreatingCollection(false) } else {
}) console.log('Here')
console.log(data.baseMinterAddress)
await toast
.promise(
baseMinterContract
.use(data.baseMinterAddress)
?.batchMint(wallet.address, baseUri, uploadDetails?.assetFiles.length as number) as Promise<string>,
{
loading: 'Minting tokens...',
success: (result) => {
setIsMintingComplete(true)
return `Tokens minted successfully! Tx Hash: ${result}`
},
error: (error) => `Failed to mint tokens: ${error.message}`,
},
{ style: { maxWidth: 'none' } },
)
.catch((error) => {
toast.error(error.message, { style: { maxWidth: 'none' } })
setUploading(false)
setIsMintingComplete(false)
setCreatingCollection(false)
})
}
setUploading(false) setUploading(false)
setCreatingCollection(false) setCreatingCollection(false)
}) })
@ -526,7 +563,7 @@ const CollectionCreationPage: NextPage = () => {
uploadDetails.pinataSecretKey as string, uploadDetails.pinataSecretKey as string,
) )
.then((assetUri: string) => { .then((assetUri: string) => {
if (minterType === 'vending') { if (minterType === 'vending' || (minterType === 'base' && uploadDetails.assetFiles.length > 1)) {
const fileArray: File[] = [] const fileArray: File[] = []
let reader: FileReader let reader: FileReader
@ -577,7 +614,7 @@ const CollectionCreationPage: NextPage = () => {
} }
reader.readAsText(uploadDetails.metadataFiles[i], 'utf8') reader.readAsText(uploadDetails.metadataFiles[i], 'utf8')
} }
} else if (minterType === 'base') { } else if (minterType === 'base' && uploadDetails.assetFiles.length === 1) {
const fileArray: File[] = [] const fileArray: File[] = []
const reader: FileReader = new FileReader() const reader: FileReader = new FileReader()
@ -636,9 +673,9 @@ const CollectionCreationPage: NextPage = () => {
if (!uploadDetails) { if (!uploadDetails) {
throw new Error('Please select assets and metadata') throw new Error('Please select assets and metadata')
} }
if (minterType === 'base' && uploadDetails.uploadMethod === 'new' && uploadDetails.assetFiles.length > 1) { // if (minterType === 'base' && uploadDetails.uploadMethod === 'new' && uploadDetails.assetFiles.length > 1) {
throw new Error('Base Minter can only mint one asset at a time. Please select only one asset.') // throw new Error('Base Minter can only mint one asset at a time. Please select only one asset.')
} // }
if (uploadDetails.uploadMethod === 'new' && uploadDetails.assetFiles.length === 0) { if (uploadDetails.uploadMethod === 'new' && uploadDetails.assetFiles.length === 0) {
throw new Error('Please select the assets') throw new Error('Please select the assets')
} }
@ -1091,7 +1128,7 @@ const CollectionCreationPage: NextPage = () => {
onClick={performUploadAndMintChecks} onClick={performUploadAndMintChecks}
variant="solid" variant="solid"
> >
Mint & Append Token Mint & Append Token(s)
</Button> </Button>
</Conditional> </Conditional>
</div> </div>