Merge pull request #117 from public-awesome/develop
Sync development > main
This commit is contained in:
commit
d4ad8148bb
@ -1,4 +1,4 @@
|
|||||||
APP_VERSION=0.4.6
|
APP_VERSION=0.4.7
|
||||||
|
|
||||||
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=793
|
NEXT_PUBLIC_SG721_CODE_ID=793
|
||||||
|
@ -114,8 +114,8 @@ export const CollectionActions = ({
|
|||||||
const priceState = useNumberInputState({
|
const priceState = useNumberInputState({
|
||||||
id: 'update-mint-price',
|
id: 'update-mint-price',
|
||||||
name: 'updateMintPrice',
|
name: 'updateMintPrice',
|
||||||
title: 'Update Mint Price',
|
title: type === 'update_discount_price' ? 'Discount Price' : 'Update Mint Price',
|
||||||
subtitle: 'New minting price in STARS',
|
subtitle: type === 'update_discount_price' ? 'New discount price in STARS' : 'New minting price in STARS',
|
||||||
})
|
})
|
||||||
|
|
||||||
const descriptionState = useInputState({
|
const descriptionState = useInputState({
|
||||||
@ -170,7 +170,7 @@ export const CollectionActions = ({
|
|||||||
'batch_mint_for',
|
'batch_mint_for',
|
||||||
])
|
])
|
||||||
const showAirdropFileField = type === 'airdrop'
|
const showAirdropFileField = type === 'airdrop'
|
||||||
const showPriceField = type === 'update_mint_price'
|
const showPriceField = isEitherType(type, ['update_mint_price', 'update_discount_price'])
|
||||||
const showDescriptionField = type === 'update_collection_info'
|
const showDescriptionField = type === 'update_collection_info'
|
||||||
const showImageField = type === 'update_collection_info'
|
const showImageField = type === 'update_collection_info'
|
||||||
const showExternalLinkField = type === 'update_collection_info'
|
const showExternalLinkField = type === 'update_collection_info'
|
||||||
@ -357,8 +357,8 @@ export const CollectionActions = ({
|
|||||||
{showLimitField && <NumberInput {...limitState} />}
|
{showLimitField && <NumberInput {...limitState} />}
|
||||||
{showTokenIdField && <NumberInput {...tokenIdState} />}
|
{showTokenIdField && <NumberInput {...tokenIdState} />}
|
||||||
{showTokenIdListField && <TextInput className="mt-2" {...tokenIdListState} />}
|
{showTokenIdListField && <TextInput className="mt-2" {...tokenIdListState} />}
|
||||||
{showNumberOfTokensField && <NumberInput {...batchNumberState} />}
|
{showNumberOfTokensField && <NumberInput className="mt-2" {...batchNumberState} />}
|
||||||
{showPriceField && <NumberInput {...priceState} />}
|
{showPriceField && <NumberInput className="mt-2" {...priceState} />}
|
||||||
{showDescriptionField && <TextInput className="my-2" {...descriptionState} />}
|
{showDescriptionField && <TextInput className="my-2" {...descriptionState} />}
|
||||||
{showImageField && <TextInput className="mb-2" {...imageState} />}
|
{showImageField && <TextInput className="mb-2" {...imageState} />}
|
||||||
{showExternalLinkField && <TextInput className="mb-2" {...externalLinkState} />}
|
{showExternalLinkField && <TextInput className="mb-2" {...externalLinkState} />}
|
||||||
|
@ -13,6 +13,8 @@ export const ACTION_TYPES = [
|
|||||||
'mint_token_uri',
|
'mint_token_uri',
|
||||||
'purge',
|
'purge',
|
||||||
'update_mint_price',
|
'update_mint_price',
|
||||||
|
'update_discount_price',
|
||||||
|
'remove_discount_price',
|
||||||
'mint_to',
|
'mint_to',
|
||||||
'mint_for',
|
'mint_for',
|
||||||
'batch_mint',
|
'batch_mint',
|
||||||
@ -87,16 +89,21 @@ export const VENDING_ACTION_LIST: ActionListItem[] = [
|
|||||||
name: 'Mint',
|
name: 'Mint',
|
||||||
description: `Mint a token`,
|
description: `Mint a token`,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'purge',
|
|
||||||
name: 'Purge',
|
|
||||||
description: `Purge`,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'update_mint_price',
|
id: 'update_mint_price',
|
||||||
name: 'Update Mint Price',
|
name: 'Update Mint Price',
|
||||||
description: `Update mint price`,
|
description: `Update mint price`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'update_discount_price',
|
||||||
|
name: 'Update Discount Price',
|
||||||
|
description: `Update discount price`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'remove_discount_price',
|
||||||
|
name: 'Remove Discount Price',
|
||||||
|
description: `Remove discount price`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'mint_to',
|
id: 'mint_to',
|
||||||
name: 'Mint To',
|
name: 'Mint To',
|
||||||
@ -182,6 +189,11 @@ export const VENDING_ACTION_LIST: ActionListItem[] = [
|
|||||||
name: 'Burn Remaining Tokens',
|
name: 'Burn Remaining Tokens',
|
||||||
description: 'Burn remaining tokens',
|
description: 'Burn remaining tokens',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'purge',
|
||||||
|
name: 'Purge',
|
||||||
|
description: `Purge`,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export interface DispatchExecuteProps {
|
export interface DispatchExecuteProps {
|
||||||
@ -205,6 +217,8 @@ export type DispatchExecuteArgs = {
|
|||||||
| { type: Select<'mint_token_uri'>; tokenUri: string }
|
| { type: Select<'mint_token_uri'>; tokenUri: string }
|
||||||
| { type: Select<'purge'> }
|
| { type: Select<'purge'> }
|
||||||
| { type: Select<'update_mint_price'>; price: string }
|
| { type: Select<'update_mint_price'>; price: string }
|
||||||
|
| { type: Select<'update_discount_price'>; price: string }
|
||||||
|
| { type: Select<'remove_discount_price'> }
|
||||||
| { type: Select<'mint_to'>; recipient: string }
|
| { type: Select<'mint_to'>; recipient: string }
|
||||||
| { type: Select<'mint_for'>; recipient: string; tokenId: number }
|
| { type: Select<'mint_for'>; recipient: string; tokenId: number }
|
||||||
| { type: Select<'batch_mint'>; recipient: string; batchNumber: number }
|
| { type: Select<'batch_mint'>; recipient: string; batchNumber: number }
|
||||||
@ -242,6 +256,12 @@ export const dispatchExecute = async (args: DispatchExecuteArgs) => {
|
|||||||
case 'update_mint_price': {
|
case 'update_mint_price': {
|
||||||
return vendingMinterMessages.updateMintPrice(txSigner, args.price)
|
return vendingMinterMessages.updateMintPrice(txSigner, args.price)
|
||||||
}
|
}
|
||||||
|
case 'update_discount_price': {
|
||||||
|
return vendingMinterMessages.updateDiscountPrice(txSigner, args.price)
|
||||||
|
}
|
||||||
|
case 'remove_discount_price': {
|
||||||
|
return vendingMinterMessages.removeDiscountPrice(txSigner)
|
||||||
|
}
|
||||||
case 'mint_to': {
|
case 'mint_to': {
|
||||||
return vendingMinterMessages.mintTo(txSigner, args.recipient)
|
return vendingMinterMessages.mintTo(txSigner, args.recipient)
|
||||||
}
|
}
|
||||||
@ -320,6 +340,12 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => {
|
|||||||
case 'update_mint_price': {
|
case 'update_mint_price': {
|
||||||
return vendingMinterMessages(minterContract)?.updateMintPrice(args.price)
|
return vendingMinterMessages(minterContract)?.updateMintPrice(args.price)
|
||||||
}
|
}
|
||||||
|
case 'update_discount_price': {
|
||||||
|
return vendingMinterMessages(minterContract)?.updateDiscountPrice(args.price)
|
||||||
|
}
|
||||||
|
case 'remove_discount_price': {
|
||||||
|
return vendingMinterMessages(minterContract)?.removeDiscountPrice()
|
||||||
|
}
|
||||||
case 'mint_to': {
|
case 'mint_to': {
|
||||||
return vendingMinterMessages(minterContract)?.mintTo(args.recipient)
|
return vendingMinterMessages(minterContract)?.mintTo(args.recipient)
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"path": "/assets/",
|
"path": "/assets/",
|
||||||
"appName": "StargazeStudio",
|
"appName": "StargazeStudio",
|
||||||
"appShortName": "StargazeStudio",
|
"appShortName": "StargazeStudio",
|
||||||
"appDescription": "Stargaze Studio is built to provide useful smart contract interfaces that helps you build and deploy your own NFT collection in no time.",
|
"appDescription": "Stargaze Studio is built to provide useful smart contract interfaces that help you build and deploy your own NFT collection in no time.",
|
||||||
"developerName": "StargazeStudio",
|
"developerName": "StargazeStudio",
|
||||||
"developerURL": "https://",
|
"developerURL": "https://",
|
||||||
"background": "#FFC27D",
|
"background": "#FFC27D",
|
||||||
|
@ -48,6 +48,8 @@ export interface VendingMinterInstance {
|
|||||||
withdraw: (senderAddress: string) => Promise<string>
|
withdraw: (senderAddress: string) => Promise<string>
|
||||||
airdrop: (senderAddress: string, recipients: string[]) => Promise<string>
|
airdrop: (senderAddress: string, recipients: string[]) => Promise<string>
|
||||||
burnRemaining: (senderAddress: string) => Promise<string>
|
burnRemaining: (senderAddress: string) => Promise<string>
|
||||||
|
updateDiscountPrice: (senderAddress: string, price: string) => Promise<string>
|
||||||
|
removeDiscountPrice: (senderAddress: string) => Promise<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VendingMinterMessages {
|
export interface VendingMinterMessages {
|
||||||
@ -66,6 +68,8 @@ export interface VendingMinterMessages {
|
|||||||
withdraw: () => WithdrawMessage
|
withdraw: () => WithdrawMessage
|
||||||
airdrop: (recipients: string[]) => CustomMessage
|
airdrop: (recipients: string[]) => CustomMessage
|
||||||
burnRemaining: () => BurnRemainingMessage
|
burnRemaining: () => BurnRemainingMessage
|
||||||
|
updateDiscountPrice: (price: string) => UpdateDiscountPriceMessage
|
||||||
|
removeDiscountPrice: () => RemoveDiscountPriceMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MintMessage {
|
export interface MintMessage {
|
||||||
@ -97,6 +101,26 @@ export interface UpdateMintPriceMessage {
|
|||||||
funds: Coin[]
|
funds: Coin[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UpdateDiscountPriceMessage {
|
||||||
|
sender: string
|
||||||
|
contract: string
|
||||||
|
msg: {
|
||||||
|
update_discount_price: {
|
||||||
|
price: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
funds: Coin[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RemoveDiscountPriceMessage {
|
||||||
|
sender: string
|
||||||
|
contract: string
|
||||||
|
msg: {
|
||||||
|
remove_discount_price: Record<string, never>
|
||||||
|
}
|
||||||
|
funds: Coin[]
|
||||||
|
}
|
||||||
|
|
||||||
export interface SetWhitelistMessage {
|
export interface SetWhitelistMessage {
|
||||||
sender: string
|
sender: string
|
||||||
contract: string
|
contract: string
|
||||||
@ -326,6 +350,36 @@ export const vendingMinter = (client: SigningCosmWasmClient, txSigner: string):
|
|||||||
return res.transactionHash
|
return res.transactionHash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateDiscountPrice = async (senderAddress: string, price: string): Promise<string> => {
|
||||||
|
const res = await client.execute(
|
||||||
|
senderAddress,
|
||||||
|
contractAddress,
|
||||||
|
{
|
||||||
|
update_discount_price: {
|
||||||
|
price: (Number(price) * 1000000).toString(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'auto',
|
||||||
|
'',
|
||||||
|
)
|
||||||
|
|
||||||
|
return res.transactionHash
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeDiscountPrice = async (senderAddress: string): Promise<string> => {
|
||||||
|
const res = await client.execute(
|
||||||
|
senderAddress,
|
||||||
|
contractAddress,
|
||||||
|
{
|
||||||
|
remove_discount_price: {},
|
||||||
|
},
|
||||||
|
'auto',
|
||||||
|
'',
|
||||||
|
)
|
||||||
|
|
||||||
|
return res.transactionHash
|
||||||
|
}
|
||||||
|
|
||||||
const setWhitelist = async (senderAddress: string, whitelist: string): Promise<string> => {
|
const setWhitelist = async (senderAddress: string, whitelist: string): Promise<string> => {
|
||||||
const res = await client.execute(
|
const res = await client.execute(
|
||||||
senderAddress,
|
senderAddress,
|
||||||
@ -552,6 +606,8 @@ export const vendingMinter = (client: SigningCosmWasmClient, txSigner: string):
|
|||||||
mint,
|
mint,
|
||||||
purge,
|
purge,
|
||||||
updateMintPrice,
|
updateMintPrice,
|
||||||
|
updateDiscountPrice,
|
||||||
|
removeDiscountPrice,
|
||||||
setWhitelist,
|
setWhitelist,
|
||||||
updateStartTime,
|
updateStartTime,
|
||||||
updateStartTradingTime,
|
updateStartTradingTime,
|
||||||
@ -633,6 +689,30 @@ export const vendingMinter = (client: SigningCosmWasmClient, txSigner: string):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateDiscountPrice = (price: string): UpdateDiscountPriceMessage => {
|
||||||
|
return {
|
||||||
|
sender: txSigner,
|
||||||
|
contract: contractAddress,
|
||||||
|
msg: {
|
||||||
|
update_discount_price: {
|
||||||
|
price: (Number(price) * 1000000).toString(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
funds: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeDiscountPrice = (): RemoveDiscountPriceMessage => {
|
||||||
|
return {
|
||||||
|
sender: txSigner,
|
||||||
|
contract: contractAddress,
|
||||||
|
msg: {
|
||||||
|
remove_discount_price: {},
|
||||||
|
},
|
||||||
|
funds: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const setWhitelist = (whitelist: string): SetWhitelistMessage => {
|
const setWhitelist = (whitelist: string): SetWhitelistMessage => {
|
||||||
return {
|
return {
|
||||||
sender: txSigner,
|
sender: txSigner,
|
||||||
@ -795,6 +875,8 @@ export const vendingMinter = (client: SigningCosmWasmClient, txSigner: string):
|
|||||||
mint,
|
mint,
|
||||||
purge,
|
purge,
|
||||||
updateMintPrice,
|
updateMintPrice,
|
||||||
|
updateDiscountPrice,
|
||||||
|
removeDiscountPrice,
|
||||||
setWhitelist,
|
setWhitelist,
|
||||||
updateStartTime,
|
updateStartTime,
|
||||||
updateStartTradingTime,
|
updateStartTradingTime,
|
||||||
|
@ -7,6 +7,8 @@ export const EXECUTE_TYPES = [
|
|||||||
'mint',
|
'mint',
|
||||||
'purge',
|
'purge',
|
||||||
'update_mint_price',
|
'update_mint_price',
|
||||||
|
'update_discount_price',
|
||||||
|
'remove_discount_price',
|
||||||
'set_whitelist',
|
'set_whitelist',
|
||||||
'update_start_time',
|
'update_start_time',
|
||||||
'update_start_trading_time',
|
'update_start_trading_time',
|
||||||
@ -39,6 +41,16 @@ export const EXECUTE_LIST: ExecuteListItem[] = [
|
|||||||
name: 'Update Mint Price',
|
name: 'Update Mint Price',
|
||||||
description: `Update mint price`,
|
description: `Update mint price`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'update_discount_price',
|
||||||
|
name: 'Update Discount Price',
|
||||||
|
description: `Update discount price`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'remove_discount_price',
|
||||||
|
name: 'Remove Discount Price',
|
||||||
|
description: `Remove discount price`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'set_whitelist',
|
id: 'set_whitelist',
|
||||||
name: 'Set Whitelist',
|
name: 'Set Whitelist',
|
||||||
@ -98,6 +110,8 @@ export type DispatchExecuteArgs = {
|
|||||||
| { type: Select<'mint'> }
|
| { type: Select<'mint'> }
|
||||||
| { type: Select<'purge'> }
|
| { type: Select<'purge'> }
|
||||||
| { type: Select<'update_mint_price'>; price: string }
|
| { type: Select<'update_mint_price'>; price: string }
|
||||||
|
| { type: Select<'update_discount_price'>; price: string }
|
||||||
|
| { type: Select<'remove_discount_price'> }
|
||||||
| { type: Select<'set_whitelist'>; whitelist: string }
|
| { type: Select<'set_whitelist'>; whitelist: string }
|
||||||
| { type: Select<'update_start_time'>; startTime: string }
|
| { type: Select<'update_start_time'>; startTime: string }
|
||||||
| { type: Select<'update_start_trading_time'>; startTime?: string }
|
| { type: Select<'update_start_trading_time'>; startTime?: string }
|
||||||
@ -123,6 +137,12 @@ export const dispatchExecute = async (args: DispatchExecuteArgs) => {
|
|||||||
case 'update_mint_price': {
|
case 'update_mint_price': {
|
||||||
return messages.updateMintPrice(txSigner, args.price)
|
return messages.updateMintPrice(txSigner, args.price)
|
||||||
}
|
}
|
||||||
|
case 'update_discount_price': {
|
||||||
|
return messages.updateDiscountPrice(txSigner, args.price)
|
||||||
|
}
|
||||||
|
case 'remove_discount_price': {
|
||||||
|
return messages.removeDiscountPrice(txSigner)
|
||||||
|
}
|
||||||
case 'set_whitelist': {
|
case 'set_whitelist': {
|
||||||
return messages.setWhitelist(txSigner, args.whitelist)
|
return messages.setWhitelist(txSigner, args.whitelist)
|
||||||
}
|
}
|
||||||
@ -167,6 +187,12 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => {
|
|||||||
case 'update_mint_price': {
|
case 'update_mint_price': {
|
||||||
return messages(contract)?.updateMintPrice(args.price)
|
return messages(contract)?.updateMintPrice(args.price)
|
||||||
}
|
}
|
||||||
|
case 'update_discount_price': {
|
||||||
|
return messages(contract)?.updateDiscountPrice(args.price)
|
||||||
|
}
|
||||||
|
case 'remove_discount_price': {
|
||||||
|
return messages(contract)?.removeDiscountPrice()
|
||||||
|
}
|
||||||
case 'set_whitelist': {
|
case 'set_whitelist': {
|
||||||
return messages(contract)?.setWhitelist(args.whitelist)
|
return messages(contract)?.setWhitelist(args.whitelist)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "stargaze-studio",
|
"name": "stargaze-studio",
|
||||||
"version": "0.4.6",
|
"version": "0.4.7",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
|
@ -748,12 +748,17 @@ const CollectionCreationPage: NextPage = () => {
|
|||||||
mintingDetails.perAddressLimit > mintingDetails.numTokens
|
mintingDetails.perAddressLimit > mintingDetails.numTokens
|
||||||
)
|
)
|
||||||
throw new Error('Invalid limit for tokens per address')
|
throw new Error('Invalid limit for tokens per address')
|
||||||
|
if (mintingDetails.numTokens < 100 && mintingDetails.perAddressLimit > 3)
|
||||||
|
throw new Error(
|
||||||
|
'Invalid limit for tokens per address. Tokens per address limit cannot exceed 3 for collections with less than 100 tokens in total.',
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
mintingDetails.numTokens > 100 &&
|
mintingDetails.numTokens >= 100 &&
|
||||||
mintingDetails.numTokens < 100 * mintingDetails.perAddressLimit &&
|
mintingDetails.perAddressLimit > Math.ceil((mintingDetails.numTokens / 100) * 3)
|
||||||
mintingDetails.perAddressLimit > mintingDetails.numTokens / 100
|
|
||||||
)
|
)
|
||||||
throw new Error('Invalid limit for tokens per address. The limit cannot exceed 1% of the total number of tokens.')
|
throw new Error(
|
||||||
|
'Invalid limit for tokens per address. Tokens per address limit cannot exceed 3% of the total number of tokens in the collection.',
|
||||||
|
)
|
||||||
if (mintingDetails.startTime === '') throw new Error('Start time is required')
|
if (mintingDetails.startTime === '') throw new Error('Start time is required')
|
||||||
if (Number(mintingDetails.startTime) < new Date().getTime() * 1000000) throw new Error('Invalid start time')
|
if (Number(mintingDetails.startTime) < new Date().getTime() * 1000000) throw new Error('Invalid start time')
|
||||||
}
|
}
|
||||||
|
@ -55,8 +55,8 @@ const VendingMinterExecutePage: NextPage = () => {
|
|||||||
const priceState = useNumberInputState({
|
const priceState = useNumberInputState({
|
||||||
id: 'price',
|
id: 'price',
|
||||||
name: 'price',
|
name: 'price',
|
||||||
title: 'Price',
|
title: type === 'update_discount_price' ? 'Discount Price' : 'Price',
|
||||||
subtitle: 'Enter the token price',
|
subtitle: type === 'update_discount_price' ? 'New discount price in STARS' : 'Enter the token price',
|
||||||
})
|
})
|
||||||
|
|
||||||
const contractState = useInputState({
|
const contractState = useInputState({
|
||||||
@ -86,7 +86,7 @@ const VendingMinterExecutePage: NextPage = () => {
|
|||||||
const showLimitField = type === 'update_per_address_limit'
|
const showLimitField = type === 'update_per_address_limit'
|
||||||
const showTokenIdField = type === 'mint_for'
|
const showTokenIdField = type === 'mint_for'
|
||||||
const showRecipientField = isEitherType(type, ['mint_to', 'mint_for'])
|
const showRecipientField = isEitherType(type, ['mint_to', 'mint_for'])
|
||||||
const showPriceField = type === 'update_mint_price'
|
const showPriceField = isEitherType(type, ['update_mint_price', 'update_discount_price'])
|
||||||
|
|
||||||
const messages = useMemo(() => contract?.use(contractState.value), [contract, wallet.address, contractState.value])
|
const messages = useMemo(() => contract?.use(contractState.value), [contract, wallet.address, contractState.value])
|
||||||
const payload: DispatchExecuteArgs = {
|
const payload: DispatchExecuteArgs = {
|
||||||
|
@ -15,8 +15,8 @@ const HomePage: NextPage = () => {
|
|||||||
Looking for a fast and efficient way to build an NFT collection? Stargaze Studio is the solution.
|
Looking for a fast and efficient way to build an NFT collection? Stargaze Studio is the solution.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
Stargaze Studio is built to provide useful smart contract interfaces that helps you build and deploy your own
|
Stargaze Studio is built to provide useful smart contract interfaces that help you build and deploy your own NFT
|
||||||
NFT collections in no time.
|
collections in no time.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
Loading…
Reference in New Issue
Block a user