diff --git a/.env.example b/.env.example index 95986a6..e6b2671 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,10 @@ APP_VERSION=0.1.0 NEXT_PUBLIC_PINATA_ENDPOINT_URL=https://api.pinata.cloud/pinning/pinFileToIPFS -NEXT_PUBLIC_WHITELIST_CODE_ID=3 -NEXT_PUBLIC_MINTER_CODE_ID=2 -NEXT_PUBLIC_SG721_CODE_ID=1 +NEXT_PUBLIC_SG721_CODE_ID=256 +NEXT_PUBLIC_VENDING_MINTER_CODE_ID=257 +NEXT_PUBLIC_VENDING_FACTORY_ADDRESS="stars1qdcxmc82uh8tqf56kprjddkfy7p4ft4z46kh9f6lhnjxtgekra5qjj5r6c" +NEXT_PUBLIC_WHITELIST_CODE_ID=267 NEXT_PUBLIC_API_URL=https:// NEXT_PUBLIC_BLOCK_EXPLORER_URL=https://testnet-explorer.publicawesome.dev/stargaze diff --git a/components/LinkTabs.data.ts b/components/LinkTabs.data.ts index 3a38fe3..8bed0bf 100644 --- a/components/LinkTabs.data.ts +++ b/components/LinkTabs.data.ts @@ -1,11 +1,6 @@ import type { LinkTabProps } from './LinkTab' export const sg721LinkTabs: LinkTabProps[] = [ - { - title: 'Instantiate', - description: `Create a new SG721 contract`, - href: '/contracts/sg721/instantiate', - }, { title: 'Query', description: `Dispatch queries with your SG721 contract`, diff --git a/components/collections/actions/Action.tsx b/components/collections/actions/Action.tsx index 19779a9..77dcab9 100644 --- a/components/collections/actions/Action.tsx +++ b/components/collections/actions/Action.tsx @@ -90,14 +90,22 @@ export const CollectionActions = ({ subtitle: 'Address of the whitelist contract', }) + const priceState = useNumberInputState({ + id: 'update-mint-price', + name: 'updateMintPrice', + title: 'Update Mint Price', + subtitle: 'New minting price in STARS', + }) + const showWhitelistField = type === 'set_whitelist' - const showDateField = type === 'update_start_time' + const showDateField = isEitherType(type, ['update_start_time', 'update_start_trading_time']) const showLimitField = type === 'update_per_address_limit' const showTokenIdField = isEitherType(type, ['transfer', 'mint_for', 'burn']) const showNumberOfTokensField = type === 'batch_mint' const showTokenIdListField = isEitherType(type, ['batch_burn', 'batch_transfer']) const showRecipientField = isEitherType(type, ['transfer', 'mint_to', 'mint_for', 'batch_mint', 'batch_transfer']) const showAirdropFileField = type === 'airdrop' + const showPriceField = type === 'update_mint_price' const payload: DispatchExecuteArgs = { whitelist: whitelistState.value, @@ -114,6 +122,7 @@ export const CollectionActions = ({ recipients: airdropArray, txSigner: wallet.address, type, + price: priceState.value.toString(), } useEffect(() => { @@ -140,6 +149,11 @@ export const CollectionActions = ({ if (minterContractAddress === '' && sg721ContractAddress === '') { throw new Error('Please enter minter and sg721 contract addresses!') } + if (type === 'update_mint_price' && priceState.value < 50) { + console.log('here') + throw new Error('Mint price must be at least 50 STARS') + } + const txHash = await toast.promise(dispatchExecute(payload), { error: `${type.charAt(0).toUpperCase() + type.slice(1)} execute failed!`, loading: 'Executing message...', @@ -172,6 +186,7 @@ export const CollectionActions = ({ {showTokenIdField && } {showTokenIdListField && } {showNumberOfTokensField && } + {showPriceField && } {showAirdropFileField && ( } + | { type: Select<'purge'> } + | { type: Select<'update_mint_price'>; price: string } | { type: Select<'mint_to'>; recipient: string } | { type: Select<'mint_for'>; recipient: string; tokenId: number } | { type: Select<'batch_mint'>; recipient: string; batchNumber: number } | { type: Select<'set_whitelist'>; whitelist: string } | { type: Select<'update_start_time'>; startTime: string } + | { type: Select<'update_start_trading_time'>; startTime: string } | { type: Select<'update_per_address_limit'>; limit: number } | { type: Select<'shuffle'> } | { type: Select<'withdraw'> } @@ -124,6 +158,7 @@ export type DispatchExecuteArgs = { | { type: Select<'burn'>; tokenId: number } | { type: Select<'batch_burn'>; tokenIds: string } | { type: Select<'airdrop'>; recipients: string[] } + | { type: Select<'burn_remaining'> } ) export const dispatchExecute = async (args: DispatchExecuteArgs) => { @@ -132,6 +167,15 @@ export const dispatchExecute = async (args: DispatchExecuteArgs) => { throw new Error('Cannot execute actions') } switch (args.type) { + case 'mint': { + return minterMessages.mint(txSigner) + } + case 'purge': { + return minterMessages.purge(txSigner) + } + case 'update_mint_price': { + return minterMessages.updateMintPrice(txSigner, args.price) + } case 'mint_to': { return minterMessages.mintTo(txSigner, args.recipient) } @@ -147,6 +191,9 @@ export const dispatchExecute = async (args: DispatchExecuteArgs) => { case 'update_start_time': { return minterMessages.updateStartTime(txSigner, args.startTime) } + case 'update_start_trading_time': { + return minterMessages.updateStartTradingTime(txSigner, args.startTime) + } case 'update_per_address_limit': { return minterMessages.updatePerAddressLimit(txSigner, args.limit) } @@ -171,6 +218,9 @@ export const dispatchExecute = async (args: DispatchExecuteArgs) => { case 'airdrop': { return minterMessages.airdrop(txSigner, args.recipients) } + case 'burn_remaining': { + return minterMessages.burnRemaining(txSigner) + } default: { throw new Error('Unknown action') } @@ -184,6 +234,15 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => { const { messages: sg721Messages } = useSG721Contract() const { minterContract, sg721Contract } = args switch (args.type) { + case 'mint': { + return minterMessages(minterContract)?.mint() + } + case 'purge': { + return minterMessages(minterContract)?.purge() + } + case 'update_mint_price': { + return minterMessages(minterContract)?.updateMintPrice(args.price) + } case 'mint_to': { return minterMessages(minterContract)?.mintTo(args.recipient) } @@ -199,6 +258,9 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => { case 'update_start_time': { return minterMessages(minterContract)?.updateStartTime(args.startTime) } + case 'update_start_trading_time': { + return minterMessages(minterContract)?.updateStartTradingTime(args.startTime) + } case 'update_per_address_limit': { return minterMessages(minterContract)?.updatePerAddressLimit(args.limit) } @@ -223,6 +285,9 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => { case 'airdrop': { return minterMessages(minterContract)?.airdrop(args.recipients) } + case 'burn_remaining': { + return minterMessages(minterContract)?.burnRemaining() + } default: { return {} } diff --git a/components/collections/creation/CollectionDetails.tsx b/components/collections/creation/CollectionDetails.tsx index bf936d0..d8b56cc 100644 --- a/components/collections/creation/CollectionDetails.tsx +++ b/components/collections/creation/CollectionDetails.tsx @@ -7,6 +7,7 @@ import clsx from 'clsx' import { FormControl } from 'components/FormControl' import { FormGroup } from 'components/FormGroup' import { useInputState } from 'components/forms/FormInput.hooks' +import { InputDateTime } from 'components/InputDateTime' import type { ChangeEvent } from 'react' import { useEffect, useState } from 'react' import { toast } from 'react-hot-toast' @@ -26,10 +27,14 @@ export interface CollectionDetailsDataProps { symbol: string imageFile: File[] externalLink?: string + startTradingTime?: string + explicit: boolean } export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl }: CollectionDetailsProps) => { const [coverImage, setCoverImage] = useState(null) + const [timestamp, setTimestamp] = useState() + const [explicit, setExplicit] = useState(false) const nameState = useInputState({ id: 'name', @@ -67,6 +72,8 @@ export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl }: Col symbol: symbolState.value, imageFile: coverImage ? [coverImage] : [], externalLink: externalLinkState.value, + startTradingTime: timestamp ? (timestamp.getTime() * 1_000_000).toString() : '', + explicit, } onChange(data) // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -74,7 +81,15 @@ export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl }: Col toast.error(error.message) } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [nameState.value, descriptionState.value, coverImage, externalLinkState.value]) + }, [ + nameState.value, + descriptionState.value, + symbolState.value, + externalLinkState.value, + coverImage, + timestamp, + explicit, + ]) const selectCoverImage = (event: ChangeEvent) => { if (event.target.files === null) return toast.error('Error selecting cover image') @@ -92,12 +107,20 @@ export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl }: Col reader.readAsArrayBuffer(event.target.files[0]) } + useEffect(() => { + console.log(explicit) + }, [explicit]) + return (
+ + + setTimestamp(date)} value={timestamp} /> + {uploadMethod === 'new' && ( @@ -135,8 +158,51 @@ export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl }: Col Waiting for cover image URL to be specified. )} - - +
+
+
+ + Does the collection contain explicit content? + +
+ { + setExplicit(true) + }} + type="radio" + /> + +
+
+ { + setExplicit(false) + }} + type="radio" + /> + +
+
+
+
) diff --git a/components/forms/FormInput.tsx b/components/forms/FormInput.tsx index db74c52..c3cc9b5 100644 --- a/components/forms/FormInput.tsx +++ b/components/forms/FormInput.tsx @@ -69,6 +69,26 @@ export const TextInput = forwardRef( // ) +export const CheckBoxInput = forwardRef( + function CheckBoxInput(props, ref) { + return ( +
+ + +
+ ) + }, + // +) + export const UrlInput = forwardRef( function UrlInput(props, ref) { return diff --git a/contexts/contracts.tsx b/contexts/contracts.tsx index 4171df4..5094c4a 100644 --- a/contexts/contracts.tsx +++ b/contexts/contracts.tsx @@ -2,6 +2,8 @@ import type { UseMinterContractProps } from 'contracts/minter' import { useMinterContract } from 'contracts/minter' import type { UseSG721ContractProps } from 'contracts/sg721' import { useSG721Contract } from 'contracts/sg721' +import type { UseVendingFactoryContractProps } from 'contracts/vendingFactory' +import { useVendingFactoryContract } from 'contracts/vendingFactory' import type { UseWhiteListContractProps } from 'contracts/whitelist' import { useWhiteListContract } from 'contracts/whitelist' import type { ReactNode, VFC } from 'react' @@ -16,6 +18,7 @@ export interface ContractsStore extends State { sg721: UseSG721ContractProps | null minter: UseMinterContractProps | null whitelist: UseWhiteListContractProps | null + vendingFactory: UseVendingFactoryContractProps | null } /** @@ -25,6 +28,7 @@ export const defaultValues: ContractsStore = { sg721: null, minter: null, whitelist: null, + vendingFactory: null, } /** @@ -51,14 +55,16 @@ const ContractsSubscription: VFC = () => { const sg721 = useSG721Contract() const minter = useMinterContract() const whitelist = useWhiteListContract() + const vendingFactory = useVendingFactoryContract() useEffect(() => { useContracts.setState({ sg721, minter, whitelist, + vendingFactory, }) - }, [sg721, minter, whitelist]) + }, [sg721, minter, whitelist, vendingFactory]) return null } diff --git a/contracts/minter/contract.ts b/contracts/minter/contract.ts index 3392b62..eb77006 100644 --- a/contracts/minter/contract.ts +++ b/contracts/minter/contract.ts @@ -28,9 +28,12 @@ export interface MinterInstance { getMintCount: (address: string) => Promise //Execute - mint: (senderAddress: string, price: string) => Promise + mint: (senderAddress: string) => Promise + purge: (senderAddress: string) => Promise + updateMintPrice: (senderAddress: string, price: string) => Promise setWhitelist: (senderAddress: string, whitelist: string) => Promise updateStartTime: (senderAddress: string, time: Timestamp) => Promise + updateStartTradingTime: (senderAddress: string, time: Timestamp) => Promise updatePerAddressLimit: (senderAddress: string, perAddressLimit: number) => Promise mintTo: (senderAddress: string, recipient: string) => Promise mintFor: (senderAddress: string, recipient: string, tokenId: number) => Promise @@ -38,12 +41,16 @@ export interface MinterInstance { shuffle: (senderAddress: string) => Promise withdraw: (senderAddress: string) => Promise airdrop: (senderAddress: string, recipients: string[]) => Promise + burnRemaining: (senderAddress: string) => Promise } export interface MinterMessages { - mint: (price: string) => MintMessage + mint: () => MintMessage + purge: () => PurgeMessage + updateMintPrice: (price: string) => UpdateMintPriceMessage setWhitelist: (whitelist: string) => SetWhitelistMessage - updateStartTime: (time: Timestamp) => UpdateStarTimeMessage + updateStartTime: (time: Timestamp) => UpdateStartTimeMessage + updateStartTradingTime: (time: Timestamp) => UpdateStartTradingTimeMessage updatePerAddressLimit: (perAddressLimit: number) => UpdatePerAddressLimitMessage mintTo: (recipient: string) => MintToMessage mintFor: (recipient: string, tokenId: number) => MintForMessage @@ -51,6 +58,7 @@ export interface MinterMessages { shuffle: () => ShuffleMessage withdraw: () => WithdrawMessage airdrop: (recipients: string[]) => CustomMessage + burnRemaining: () => BurnRemainingMessage } export interface MintMessage { @@ -62,6 +70,26 @@ export interface MintMessage { funds: Coin[] } +export interface PurgeMessage { + sender: string + contract: string + msg: { + purge: Record + } + funds: Coin[] +} + +export interface UpdateMintPriceMessage { + sender: string + contract: string + msg: { + update_mint_price: { + price: string + } + } + funds: Coin[] +} + export interface SetWhitelistMessage { sender: string contract: string @@ -73,7 +101,7 @@ export interface SetWhitelistMessage { funds: Coin[] } -export interface UpdateStarTimeMessage { +export interface UpdateStartTimeMessage { sender: string contract: string msg: { @@ -82,6 +110,15 @@ export interface UpdateStarTimeMessage { funds: Coin[] } +export interface UpdateStartTradingTimeMessage { + sender: string + contract: string + msg: { + update_start_trading_time: string + } + funds: Coin[] +} + export interface UpdatePerAddressLimitMessage { sender: string contract: string @@ -141,6 +178,34 @@ export interface WithdrawMessage { funds: Coin[] } +export interface BurnRemainingMessage { + sender: string + contract: string + msg: { + burn_remaining: Record + } + funds: Coin[] +} + +export interface MintPriceMessage { + public_price: { + denom: string + amount: string + } + airdrop_price: { + denom: string + amount: string + } + whitelist_price?: { + denom: string + amount: string + } + current_price: { + denom: string + amount: string + } +} + export interface MinterContract { instantiate: ( senderAddress: string, @@ -180,7 +245,7 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC return res } - const getMintPrice = async (): Promise => { + const getMintPrice = async (): Promise => { const res = await client.queryContractSmart(contractAddress, { mint_price: {}, }) @@ -195,7 +260,8 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC } //Execute - const mint = async (senderAddress: string, price: string): Promise => { + const mint = async (senderAddress: string): Promise => { + const price = (await getMintPrice()).public_price.amount const res = await client.execute( senderAddress, contractAddress, @@ -210,6 +276,36 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC return res.transactionHash } + const purge = async (senderAddress: string): Promise => { + const res = await client.execute( + senderAddress, + contractAddress, + { + purge: {}, + }, + 'auto', + '', + ) + + return res.transactionHash + } + + const updateMintPrice = async (senderAddress: string, price: string): Promise => { + const res = await client.execute( + senderAddress, + contractAddress, + { + update_mint_price: { + price: (Number(price) * 1000000).toString(), + }, + }, + 'auto', + '', + ) + + return res.transactionHash + } + const setWhitelist = async (senderAddress: string, whitelist: string): Promise => { const res = await client.execute( senderAddress, @@ -238,6 +334,20 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC return res.transactionHash } + const updateStartTradingTime = async (senderAddress: string, time: Timestamp): Promise => { + const res = await client.execute( + senderAddress, + contractAddress, + { + update_start_trading_time: { time }, + }, + 'auto', + '', + ) + + return res.transactionHash + } + const updatePerAddressLimit = async (senderAddress: string, perAddressLimit: number): Promise => { const res = await client.execute( senderAddress, @@ -335,6 +445,7 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC }, 'auto', '', + [coin(500000000, 'ustars')], ) return res.transactionHash @@ -354,6 +465,20 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC return res.transactionHash } + const burnRemaining = async (senderAddress: string): Promise => { + const res = await client.execute( + senderAddress, + contractAddress, + { + burn_remaining: {}, + }, + 'auto', + '', + ) + + return res.transactionHash + } + return { contractAddress, getConfig, @@ -362,8 +487,11 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC getMintPrice, getMintCount, mint, + purge, + updateMintPrice, setWhitelist, updateStartTime, + updateStartTradingTime, updatePerAddressLimit, mintTo, mintFor, @@ -371,6 +499,7 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC airdrop, shuffle, withdraw, + burnRemaining, } } @@ -392,14 +521,38 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC } const messages = (contractAddress: string) => { - const mint = (price: string): MintMessage => { + const mint = (): MintMessage => { return { sender: txSigner, contract: contractAddress, msg: { mint: {}, }, - funds: [coin(price, 'ustars')], + funds: [], + } + } + + const purge = (): PurgeMessage => { + return { + sender: txSigner, + contract: contractAddress, + msg: { + purge: {}, + }, + funds: [], + } + } + + const updateMintPrice = (price: string): UpdateMintPriceMessage => { + return { + sender: txSigner, + contract: contractAddress, + msg: { + update_mint_price: { + price: (Number(price) * 1000000).toString(), + }, + }, + funds: [], } } @@ -416,7 +569,7 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC } } - const updateStartTime = (startTime: string): UpdateStarTimeMessage => { + const updateStartTime = (startTime: string): UpdateStartTimeMessage => { return { sender: txSigner, contract: contractAddress, @@ -427,6 +580,17 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC } } + const updateStartTradingTime = (startTime: string): UpdateStartTradingTimeMessage => { + return { + sender: txSigner, + contract: contractAddress, + msg: { + update_start_trading_time: startTime, + }, + funds: [], + } + } + const updatePerAddressLimit = (limit: number): UpdatePerAddressLimitMessage => { return { sender: txSigner, @@ -515,10 +679,24 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC } } + const burnRemaining = (): BurnRemainingMessage => { + return { + sender: txSigner, + contract: contractAddress, + msg: { + burn_remaining: {}, + }, + funds: [], + } + } + return { mint, + purge, + updateMintPrice, setWhitelist, updateStartTime, + updateStartTradingTime, updatePerAddressLimit, mintTo, mintFor, @@ -526,6 +704,7 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC airdrop, shuffle, withdraw, + burnRemaining, } } diff --git a/contracts/minter/messages/execute.ts b/contracts/minter/messages/execute.ts index 6883f7e..b57f45b 100644 --- a/contracts/minter/messages/execute.ts +++ b/contracts/minter/messages/execute.ts @@ -5,13 +5,17 @@ export type ExecuteType = typeof EXECUTE_TYPES[number] export const EXECUTE_TYPES = [ 'mint', + 'purge', + 'update_mint_price', 'set_whitelist', 'update_start_time', + 'update_start_trading_time', 'update_per_address_limit', 'mint_to', 'mint_for', 'shuffle', 'withdraw', + 'burn_remaining', ] as const export interface ExecuteListItem { @@ -26,6 +30,16 @@ export const EXECUTE_LIST: ExecuteListItem[] = [ name: 'Mint', description: `Mint new tokens for a given address`, }, + { + id: 'purge', + name: 'Purge', + description: `Purge`, + }, + { + id: 'update_mint_price', + name: 'Update Mint Price', + description: `Update mint price`, + }, { id: 'set_whitelist', name: 'Set Whitelist', @@ -36,6 +50,11 @@ export const EXECUTE_LIST: ExecuteListItem[] = [ name: 'Update Start Time', description: `Update start time for minting`, }, + { + id: 'update_start_trading_time', + name: 'Update Start Trading Time', + description: `Update start trading time for minting`, + }, { id: 'update_per_address_limit', name: 'Update Per Address Limit', @@ -56,6 +75,11 @@ export const EXECUTE_LIST: ExecuteListItem[] = [ name: 'Shuffle', description: `Shuffle the token IDs`, }, + { + id: 'burn_remaining', + name: 'Burn Remaining', + description: `Burn remaining tokens`, + }, ] export interface DispatchExecuteProps { @@ -72,14 +96,18 @@ export type DispatchExecuteArgs = { txSigner: string } & ( | { type: undefined } - | { type: Select<'mint'>; price: string } + | { type: Select<'mint'> } + | { type: Select<'purge'> } + | { type: Select<'update_mint_price'>; price: string } | { type: Select<'set_whitelist'>; whitelist: string } | { type: Select<'update_start_time'>; startTime: string } + | { type: Select<'update_start_trading_time'>; startTime: string } | { type: Select<'update_per_address_limit'>; limit: number } | { type: Select<'mint_to'>; recipient: string } | { type: Select<'mint_for'>; recipient: string; tokenId: number } | { type: Select<'shuffle'> } | { type: Select<'withdraw'> } + | { type: Select<'burn_remaining'> } ) export const dispatchExecute = async (args: DispatchExecuteArgs) => { @@ -89,7 +117,13 @@ export const dispatchExecute = async (args: DispatchExecuteArgs) => { } switch (args.type) { case 'mint': { - return messages.mint(txSigner, args.price === '' ? '0' : args.price) + return messages.mint(txSigner) + } + case 'purge': { + return messages.purge(txSigner) + } + case 'update_mint_price': { + return messages.updateMintPrice(txSigner, args.price) } case 'set_whitelist': { return messages.setWhitelist(txSigner, args.whitelist) @@ -97,6 +131,9 @@ export const dispatchExecute = async (args: DispatchExecuteArgs) => { case 'update_start_time': { return messages.updateStartTime(txSigner, args.startTime) } + case 'update_start_trading_time': { + return messages.updateStartTradingTime(txSigner, args.startTime) + } case 'update_per_address_limit': { return messages.updatePerAddressLimit(txSigner, args.limit) } @@ -112,6 +149,9 @@ export const dispatchExecute = async (args: DispatchExecuteArgs) => { case 'withdraw': { return messages.withdraw(txSigner) } + case 'burn_remaining': { + return messages.burnRemaining(txSigner) + } default: { throw new Error('unknown execute type') } @@ -124,7 +164,13 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => { const { contract } = args switch (args.type) { case 'mint': { - return messages(contract)?.mint(args.price === '' ? '0' : args.price) + return messages(contract)?.mint() + } + case 'purge': { + return messages(contract)?.purge() + } + case 'update_mint_price': { + return messages(contract)?.updateMintPrice(args.price) } case 'set_whitelist': { return messages(contract)?.setWhitelist(args.whitelist) @@ -132,6 +178,9 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => { case 'update_start_time': { return messages(contract)?.updateStartTime(args.startTime) } + case 'update_start_trading_time': { + return messages(contract)?.updateStartTradingTime(args.startTime) + } case 'update_per_address_limit': { return messages(contract)?.updatePerAddressLimit(args.limit) } @@ -147,6 +196,9 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => { case 'withdraw': { return messages(contract)?.withdraw() } + case 'burn_remaining': { + return messages(contract)?.burnRemaining() + } default: { return {} } diff --git a/contracts/vendingFactory/contract.ts b/contracts/vendingFactory/contract.ts new file mode 100644 index 0000000..a172157 --- /dev/null +++ b/contracts/vendingFactory/contract.ts @@ -0,0 +1,82 @@ +import type { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate' +import type { Coin } from '@cosmjs/proto-signing' +import { coin } from '@cosmjs/proto-signing' +import type { logs } from '@cosmjs/stargate' +import { VENDING_FACTORY_ADDRESS } from 'utils/constants' + +export interface CreateMinterResponse { + readonly minterAddress: string + readonly sg721Address: string + readonly transactionHash: string + readonly logs: readonly logs.Log[] +} + +export interface VendingFactoryInstance { + readonly contractAddress: string + + //Query + + //Execute + createMinter: (senderAddress: string, msg: Record, funds: Coin[]) => Promise +} + +export interface VendingFactoryMessages { + createMinter: (msg: Record) => CreateMinterMessage +} + +export interface CreateMinterMessage { + sender: string + contract: string + msg: Record + funds: Coin[] +} + +export interface VendingFactoryContract { + use: (contractAddress: string) => VendingFactoryInstance + + messages: (contractAddress: string) => VendingFactoryMessages +} + +export const vendingFactory = (client: SigningCosmWasmClient, txSigner: string): VendingFactoryContract => { + const use = (contractAddress: string): VendingFactoryInstance => { + //Query + + //Execute + const createMinter = async ( + senderAddress: string, + msg: Record, + funds: Coin[], + ): Promise => { + const result = await client.execute(senderAddress, VENDING_FACTORY_ADDRESS, msg, 'auto', '', funds) + + return { + minterAddress: result.logs[0].events[5].attributes[0].value, + sg721Address: result.logs[0].events[5].attributes[2].value, + transactionHash: result.transactionHash, + logs: result.logs, + } + } + + return { + contractAddress, + createMinter, + } + } + + const messages = (contractAddress: string) => { + const createMinter = (msg: Record): CreateMinterMessage => { + return { + sender: txSigner, + contract: contractAddress, + msg, + funds: [coin('1000000000', 'ustars')], + } + } + + return { + createMinter, + } + } + + return { use, messages } +} diff --git a/contracts/vendingFactory/index.ts b/contracts/vendingFactory/index.ts new file mode 100644 index 0000000..6dc6461 --- /dev/null +++ b/contracts/vendingFactory/index.ts @@ -0,0 +1,2 @@ +export * from './contract' +export * from './useContract' diff --git a/contracts/vendingFactory/messages/execute.ts b/contracts/vendingFactory/messages/execute.ts new file mode 100644 index 0000000..fedf1f2 --- /dev/null +++ b/contracts/vendingFactory/messages/execute.ts @@ -0,0 +1,28 @@ +import type { Coin } from '@cosmjs/proto-signing' + +import type { VendingFactoryInstance } from '../index' +import { useVendingFactoryContract } from '../index' + +/** @see {@link VendingFactoryInstance} */ +export interface DispatchExecuteArgs { + contract: string + messages?: VendingFactoryInstance + txSigner: string + msg: Record + funds: Coin[] +} + +export const dispatchExecute = async (args: DispatchExecuteArgs) => { + const { messages, txSigner } = args + if (!messages) { + throw new Error('cannot dispatch execute, messages is not defined') + } + return messages.createMinter(txSigner, args.msg, args.funds) +} + +export const previewExecutePayload = (args: DispatchExecuteArgs) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const { messages } = useVendingFactoryContract() + const { contract } = args + return messages(contract)?.createMinter(args.msg) +} diff --git a/contracts/vendingFactory/useContract.ts b/contracts/vendingFactory/useContract.ts new file mode 100644 index 0000000..a6636b3 --- /dev/null +++ b/contracts/vendingFactory/useContract.ts @@ -0,0 +1,76 @@ +import type { logs } from '@cosmjs/stargate' +import { useWallet } from 'contexts/wallet' +import { useCallback, useEffect, useState } from 'react' + +import type { VendingFactoryContract, VendingFactoryInstance, VendingFactoryMessages } from './contract' +import { vendingFactory as initContract } from './contract' + +/*export interface InstantiateResponse { + /** The address of the newly instantiated contract *-/ + readonly contractAddress: string + readonly logs: readonly logs.Log[] + /** Block height in which the transaction is included *-/ + readonly height: number + /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex *-/ + readonly transactionHash: string + readonly gasWanted: number + readonly gasUsed: number +}*/ + +interface InstantiateResponse { + readonly contractAddress: string + readonly transactionHash: string + readonly logs: readonly logs.Log[] +} + +export interface UseVendingFactoryContractProps { + use: (customAddress: string) => VendingFactoryInstance | undefined + updateContractAddress: (contractAddress: string) => void + getContractAddress: () => string | undefined + messages: (contractAddress: string) => VendingFactoryMessages | undefined +} + +export function useVendingFactoryContract(): UseVendingFactoryContractProps { + const wallet = useWallet() + + const [address, setAddress] = useState('') + const [vendingFactory, setVendingFactory] = useState() + + useEffect(() => { + setAddress(localStorage.getItem('contract_address') || '') + }, []) + + useEffect(() => { + const VendingFactoryBaseContract = initContract(wallet.getClient(), wallet.address) + setVendingFactory(VendingFactoryBaseContract) + }, [wallet]) + + const updateContractAddress = (contractAddress: string) => { + setAddress(contractAddress) + } + + const use = useCallback( + (customAddress = ''): VendingFactoryInstance | undefined => { + return vendingFactory?.use(address || customAddress) + }, + [vendingFactory, address], + ) + + const getContractAddress = (): string | undefined => { + return address + } + + const messages = useCallback( + (customAddress = ''): VendingFactoryMessages | undefined => { + return vendingFactory?.messages(address || customAddress) + }, + [vendingFactory, address], + ) + + return { + use, + updateContractAddress, + getContractAddress, + messages, + } +} diff --git a/contracts/whitelist/contract.ts b/contracts/whitelist/contract.ts index 3d7e618..6dd9ca0 100644 --- a/contracts/whitelist/contract.ts +++ b/contracts/whitelist/contract.ts @@ -13,7 +13,7 @@ export interface ConfigResponse { readonly member_limit: number readonly start_time: string readonly end_time: string - readonly unit_price: Coin + readonly mint_price: Coin readonly is_active: boolean } export interface WhiteListInstance { @@ -219,7 +219,7 @@ export const WhiteList = (client: SigningCosmWasmClient, txSigner: string): Whit admin?: string, ): Promise => { const result = await client.instantiate(txSigner, codeId, initMsg, label, 'auto', { - funds: [coin('100000000', 'ustars')], + funds: [coin((Math.ceil(Number(initMsg.member_limit) / 1000) * 100000000).toString(), 'ustars')], admin, }) diff --git a/env. b/env. deleted file mode 100644 index 95986a6..0000000 --- a/env. +++ /dev/null @@ -1,18 +0,0 @@ -APP_VERSION=0.1.0 - -NEXT_PUBLIC_PINATA_ENDPOINT_URL=https://api.pinata.cloud/pinning/pinFileToIPFS -NEXT_PUBLIC_WHITELIST_CODE_ID=3 -NEXT_PUBLIC_MINTER_CODE_ID=2 -NEXT_PUBLIC_SG721_CODE_ID=1 - -NEXT_PUBLIC_API_URL=https:// -NEXT_PUBLIC_BLOCK_EXPLORER_URL=https://testnet-explorer.publicawesome.dev/stargaze -NEXT_PUBLIC_NETWORK=testnet -NEXT_PUBLIC_STARGAZE_WEBSITE_URL=https://testnet.publicawesome.dev -NEXT_PUBLIC_WEBSITE_URL=https:// - -NEXT_PUBLIC_S3_BUCKET= # TODO -NEXT_PUBLIC_S3_ENDPOINT= # TODO -NEXT_PUBLIC_S3_KEY= # TODO -NEXT_PUBLIC_S3_REGION= # TODO -NEXT_PUBLIC_S3_SECRET= # TODO \ No newline at end of file diff --git a/env.d.ts b/env.d.ts index 0859147..51d0974 100644 --- a/env.d.ts +++ b/env.d.ts @@ -15,8 +15,9 @@ declare namespace NodeJS { readonly APP_VERSION: string readonly NEXT_PUBLIC_SG721_CODE_ID: string - readonly NEXT_PUBLIC_MINTER_CODE_ID: string readonly NEXT_PUBLIC_WHITELIST_CODE_ID: string + readonly NEXT_PUBLIC_VENDING_MINTER_CODE_ID: string + readonly NEXT_PUBLIC_VENDING_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_PINATA_ENDPOINT_URL: string readonly NEXT_PUBLIC_API_URL: string diff --git a/pages/collections/create.tsx b/pages/collections/create.tsx index 51d8887..7780014 100644 --- a/pages/collections/create.tsx +++ b/pages/collections/create.tsx @@ -23,18 +23,20 @@ import { Conditional } from 'components/Conditional' import { LoadingModal } from 'components/LoadingModal' import { useContracts } from 'contexts/contracts' import { useWallet } from 'contexts/wallet' +import type { DispatchExecuteArgs } from 'contracts/vendingFactory/messages/execute' +import { dispatchExecute } from 'contracts/vendingFactory/messages/execute' import type { NextPage } from 'next' import { NextSeo } from 'next-seo' -import { useEffect, useRef, useState } from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { toast } from 'react-hot-toast' import { upload } from 'services/upload' import { compareFileArrays } from 'utils/compareFileArrays' import { BLOCK_EXPLORER_URL, - MINTER_CODE_ID, NETWORK, SG721_CODE_ID, STARGAZE_URL, + VENDING_FACTORY_ADDRESS, WHITELIST_CODE_ID, } from 'utils/constants' import { withMetadata } from 'utils/layout' @@ -46,8 +48,16 @@ import { getAssetType } from '../../utils/getAssetType' const CollectionCreationPage: NextPage = () => { const wallet = useWallet() - const { minter: minterContract, whitelist: whitelistContract } = useContracts() + const { + minter: minterContract, + whitelist: whitelistContract, + vendingFactory: vendingFactoryContract, + } = useContracts() const scrollRef = useRef(null) + const messages = useMemo( + () => vendingFactoryContract?.use(VENDING_FACTORY_ADDRESS), + [vendingFactoryContract, wallet.address], + ) const [uploadDetails, setUploadDetails] = useState(null) const [collectionDetails, setCollectionDetails] = useState(null) @@ -148,7 +158,7 @@ const CollectionCreationPage: NextPage = () => { members: whitelistDetails?.members, start_time: whitelistDetails?.startTime, end_time: whitelistDetails?.endTime, - unit_price: coin(String(Number(whitelistDetails?.unitPrice)), 'ustars'), + mint_price: coin(String(Number(whitelistDetails?.unitPrice)), 'ustars'), per_address_limit: whitelistDetails?.perAddressLimit, member_limit: whitelistDetails?.memberLimit, } @@ -176,35 +186,50 @@ const CollectionCreationPage: NextPage = () => { } const msg = { - base_token_uri: `${uploadDetails?.uploadMethod === 'new' ? `ipfs://${baseUri}/` : `${baseUri}`}`, - num_tokens: mintingDetails?.numTokens, - sg721_code_id: SG721_CODE_ID, - sg721_instantiate_msg: { - name: collectionDetails?.name, - symbol: collectionDetails?.symbol, - minter: wallet.address, - collection_info: { - creator: wallet.address, - description: collectionDetails?.description, - image: `${ - uploadDetails?.uploadMethod === 'new' - ? `ipfs://${coverImageUri}/${collectionDetails?.imageFile[0].name as string}` - : `${coverImageUri}` - }`, - external_link: collectionDetails?.externalLink === '' ? null : collectionDetails?.externalLink, - royalty_info: royaltyInfo, + create_minter: { + init_msg: { + base_token_uri: `${uploadDetails?.uploadMethod === 'new' ? `ipfs://${baseUri}/` : `${baseUri}`}`, + start_time: mintingDetails?.startTime, + num_tokens: mintingDetails?.numTokens, + mint_price: { + amount: mintingDetails?.unitPrice, + denom: 'ustars', + }, + per_address_limit: mintingDetails?.perAddressLimit, + whitelist, + }, + collection_params: { + code_id: SG721_CODE_ID, + name: collectionDetails?.name, + symbol: collectionDetails?.symbol, + info: { + creator: wallet.address, + description: collectionDetails?.description, + image: `${ + uploadDetails?.uploadMethod === 'new' + ? `ipfs://${coverImageUri}/${collectionDetails?.imageFile[0].name as string}` + : `${coverImageUri}` + }`, + external_link: collectionDetails?.externalLink, + explicit_content: collectionDetails?.explicit, + royalty_info: royaltyInfo, + start_trading_time: collectionDetails?.startTradingTime || null, + }, }, }, - per_address_limit: mintingDetails?.perAddressLimit, - unit_price: coin(String(Number(mintingDetails?.unitPrice)), 'ustars'), - whitelist, - start_time: mintingDetails?.startTime, } - const data = await minterContract.instantiate(MINTER_CODE_ID, msg, 'Stargaze Minter Contract', wallet.address) + const payload: DispatchExecuteArgs = { + contract: VENDING_FACTORY_ADDRESS, + messages, + txSigner: wallet.address, + msg, + funds: [coin('1000000000', 'ustars')], + } + const data = await dispatchExecute(payload) setTransactionHash(data.transactionHash) - setMinterContractAddress(data.contractAddress) - setSg721ContractAddress(data.logs[0].events[3].attributes[2].value) + setMinterContractAddress(data.minterAddress) + setSg721ContractAddress(data.sg721Address) } const uploadFiles = async (): Promise => { @@ -302,6 +327,16 @@ const CollectionCreationPage: NextPage = () => { if (collectionDetails.description === '') throw new Error('Collection description is required') if (uploadDetails?.uploadMethod === 'new' && collectionDetails.imageFile.length === 0) throw new Error('Collection cover image is required') + if ( + collectionDetails.startTradingTime && + Number(collectionDetails.startTradingTime) < new Date().getTime() * 1000000 + ) + throw new Error('Invalid trading start time') + if ( + collectionDetails.startTradingTime && + Number(collectionDetails.startTradingTime) < Number(mintingDetails?.startTime) + ) + throw new Error('Trading start time must be after minting start time') } const checkMintingDetails = () => { @@ -315,6 +350,12 @@ const CollectionCreationPage: NextPage = () => { mintingDetails.perAddressLimit > mintingDetails.numTokens ) throw new Error('Invalid limit for tokens per address') + if ( + mintingDetails.numTokens > 100 && + mintingDetails.numTokens < 100 * mintingDetails.perAddressLimit && + 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.') if (mintingDetails.startTime === '') throw new Error('Start time is required') if (Number(mintingDetails.startTime) < new Date().getTime() * 1000000) throw new Error('Invalid start time') } diff --git a/pages/contracts/minter/instantiate.tsx b/pages/contracts/minter/instantiate.tsx index 4080c55..c5a675d 100644 --- a/pages/contracts/minter/instantiate.tsx +++ b/pages/contracts/minter/instantiate.tsx @@ -14,7 +14,6 @@ import { LinkTabs } from 'components/LinkTabs' import { minterLinkTabs } from 'components/LinkTabs.data' import { useContracts } from 'contexts/contracts' import { useWallet } from 'contexts/wallet' -import type { InstantiateResponse } from 'contracts/minter' import type { NextPage } from 'next' import { NextSeo } from 'next-seo' import type { FormEvent } from 'react' @@ -22,15 +21,19 @@ import { useState } from 'react' import { toast } from 'react-hot-toast' import { FaAsterisk } from 'react-icons/fa' import { useMutation } from 'react-query' -import { MINTER_CODE_ID } from 'utils/constants' +import { VENDING_FACTORY_ADDRESS } from 'utils/constants' import { withMetadata } from 'utils/layout' import { links } from 'utils/links' +import type { CreateMinterResponse } from '../../../contracts/vendingFactory/contract' + const MinterInstantiatePage: NextPage = () => { const wallet = useWallet() - const contract = useContracts().minter + const contract = useContracts().vendingFactory const [startDate, setStartDate] = useState(undefined) + const [timestamp, setTimestamp] = useState() + const [explicit, setExplicit] = useState(false) const nameState = useInputState({ id: 'name', @@ -48,14 +51,6 @@ const MinterInstantiatePage: NextPage = () => { subtitle: 'Symbol of the sg721 contract', }) - const minterState = useInputState({ - id: 'minter-address', - name: 'minterAddress', - title: 'Minter Address', - placeholder: 'stars1234567890abcdefghijklmnopqrstuvwxyz...', - subtitle: 'Address that has the permissions to mint on sg721 contract', - }) - const codeIdState = useNumberInputState({ id: 'code-id', name: 'code-id', @@ -102,12 +97,12 @@ const MinterInstantiatePage: NextPage = () => { placeholder: 'stars1234567890abcdefghijklmnopqrstuvwxyz...', }) - const royaltyShareState = useNumberInputState({ + const royaltyShareState = useInputState({ id: 'royalty-share', name: 'royaltyShare', title: 'Share Percentage', subtitle: 'Percentage of royalties to be paid', - placeholder: '8', + placeholder: '8%', }) const unitPriceState = useNumberInputState({ @@ -115,7 +110,7 @@ const MinterInstantiatePage: NextPage = () => { name: 'unitPrice', title: 'Unit Price', subtitle: 'Price of each tokens in collection', - placeholder: '500', + placeholder: '50', }) const baseTokenUriState = useInputState({ @@ -151,7 +146,7 @@ const MinterInstantiatePage: NextPage = () => { }) const { data, isLoading, mutate } = useMutation( - async (event: FormEvent): Promise => { + async (event: FormEvent): Promise => { event.preventDefault() if (!contract) { throw new Error('Smart contract connection failed') @@ -160,8 +155,8 @@ const MinterInstantiatePage: NextPage = () => { let royaltyInfo = null if (royaltyPaymentAddressState.value && royaltyShareState.value) { royaltyInfo = { - paymentAddress: royaltyPaymentAddressState.value, - share: royaltyShareState.value, + payment_address: royaltyPaymentAddressState.value, + share: (Number(royaltyShareState.value) / 100).toString(), } } @@ -173,8 +168,8 @@ const MinterInstantiatePage: NextPage = () => { throw new Error('Per address limit must be between 1 and 50') } - if (Number(unitPriceState.value) < 500) { - throw new Error('Unit price must be greater than 500 STARS') + if (Number(unitPriceState.value) < 50) { + throw new Error('Unit price must be greater than 50 STARS') } if (!startDate) { @@ -182,31 +177,42 @@ const MinterInstantiatePage: NextPage = () => { } const msg = { - base_token_uri: baseTokenUriState.value, - num_tokens: tokenNumberState.value, - sg721_code_id: codeIdState.value, - sg721_instantiate_msg: { - name: nameState.value, - symbol: symbolState.value, - minter: minterState.value, - collection_info: { - creator: creatorState.value, - description: descriptionState.value, - image: imageState.value, - external_link: externalLinkState.value || null, - royalty_info: royaltyInfo, + create_minter: { + init_msg: { + base_token_uri: baseTokenUriState.value, + start_time: (startDate.getTime() * 1_000_000).toString(), + num_tokens: tokenNumberState.value, + mint_price: coin(String(Number(unitPriceState.value) * 1000000), 'ustars'), + per_address_limit: perAddressLimitState.value, + whitelist: whitelistAddressState.value || null, + }, + collection_params: { + code_id: codeIdState.value, + name: nameState.value, + symbol: symbolState.value, + info: { + creator: creatorState.value, + description: descriptionState.value, + image: imageState.value, + external_link: externalLinkState.value || null, + explicit_content: explicit, + start_trading_time: timestamp ? (timestamp.getTime() * 1_000_000).toString() : null, + royalty_info: royaltyInfo, + }, }, }, - per_address_limit: perAddressLimitState.value, - unit_price: coin(String(Number(unitPriceState.value) * 1000000), 'ustars'), - whitelist_address: whitelistAddressState.value || null, - start_time: (startDate.getTime() * 1_000_000).toString(), } - return toast.promise(contract.instantiate(MINTER_CODE_ID, msg, 'Stargaze Minter Contract', wallet.address), { - loading: 'Instantiating contract...', - error: 'Instantiation failed!', - success: 'Instantiation success!', - }) + + return toast.promise( + contract + .use(VENDING_FACTORY_ADDRESS) + ?.createMinter(wallet.address, msg, [coin('1000000000', 'ustars')]) as Promise, + { + loading: 'Instantiating contract...', + error: 'Instantiation failed!', + success: 'Instantiation success!', + }, + ) }, { onError: (error) => { @@ -240,7 +246,6 @@ const MinterInstantiatePage: NextPage = () => { -
@@ -248,6 +253,54 @@ const MinterInstantiatePage: NextPage = () => { + + setTimestamp(date)} value={timestamp} /> + +
+
+
+ + Does the collection contain explicit content? + +
+ { + setExplicit(true) + }} + type="radio" + /> + +
+
+ { + setExplicit(false) + }} + type="radio" + /> + +
+
+
+
diff --git a/pages/contracts/sg721/execute.tsx b/pages/contracts/sg721/execute.tsx index 59c800c..6477532 100644 --- a/pages/contracts/sg721/execute.tsx +++ b/pages/contracts/sg721/execute.tsx @@ -145,7 +145,7 @@ const Sg721ExecutePage: NextPage = () => { link={links.Documentation} title="Sg721 Contract" /> - +
diff --git a/pages/contracts/sg721/index.tsx b/pages/contracts/sg721/index.tsx index 561b4b3..98c1e67 100644 --- a/pages/contracts/sg721/index.tsx +++ b/pages/contracts/sg721/index.tsx @@ -1 +1 @@ -export { default } from './instantiate' +export { default } from './query' diff --git a/pages/contracts/sg721/instantiate.tsx b/pages/contracts/sg721/instantiate.tsx deleted file mode 100644 index f02483e..0000000 --- a/pages/contracts/sg721/instantiate.tsx +++ /dev/null @@ -1,186 +0,0 @@ -import { Alert } from 'components/Alert' -import { Button } from 'components/Button' -import { Conditional } from 'components/Conditional' -import { ContractPageHeader } from 'components/ContractPageHeader' -import { FormGroup } from 'components/FormGroup' -import { NumberInput, TextInput } from 'components/forms/FormInput' -import { useInputState, useNumberInputState } from 'components/forms/FormInput.hooks' -import { FormTextArea } from 'components/forms/FormTextArea' -import { JsonPreview } from 'components/JsonPreview' -import { LinkTabs } from 'components/LinkTabs' -import { sg721LinkTabs } from 'components/LinkTabs.data' -import { useContracts } from 'contexts/contracts' -import { useWallet } from 'contexts/wallet' -import type { InstantiateResponse } from 'contracts/sg721' -import type { NextPage } from 'next' -import { NextSeo } from 'next-seo' -import type { FormEvent } from 'react' -import { toast } from 'react-hot-toast' -import { FaAsterisk } from 'react-icons/fa' -import { useMutation } from 'react-query' -import { SG721_CODE_ID } from 'utils/constants' -import { withMetadata } from 'utils/layout' -import { links } from 'utils/links' - -const Sg721InstantiatePage: NextPage = () => { - const wallet = useWallet() - const contract = useContracts().sg721 - - const nameState = useInputState({ - id: 'name', - name: 'name', - title: 'Name', - placeholder: 'My Awesome SG721 Contract', - subtitle: 'Name of the sg721 contract', - }) - - const symbolState = useInputState({ - id: 'symbol', - name: 'symbol', - title: 'Symbol', - placeholder: 'AWSM', - subtitle: 'Symbol of the sg721 contract', - }) - - const minterState = useInputState({ - id: 'minter-address', - name: 'minterAddress', - title: 'Minter Address', - placeholder: 'stars1234567890abcdefghijklmnopqrstuvwxyz...', - subtitle: 'Address that has the permissions to mint on sg721 contract', - }) - - const creatorState = useInputState({ - id: 'creator-address', - name: 'creatorAddress', - title: 'Creator Address', - placeholder: 'stars1234567890abcdefghijklmnopqrstuvwxyz...', - subtitle: 'Address of the collection creator', - }) - - const descriptionState = useInputState({ - id: 'description', - name: 'description', - title: 'Description', - subtitle: 'Description of the collection', - }) - - const imageState = useInputState({ - id: 'image', - name: 'image', - title: 'Image', - subtitle: 'Image of the collection', - placeholder: 'ipfs://bafybe....', - }) - - const externalLinkState = useInputState({ - id: 'external-link', - name: 'externalLink', - title: 'External Link', - subtitle: 'External link to the collection', - }) - - const royaltyPaymentAddressState = useInputState({ - id: 'royalty-payment-address', - name: 'royaltyPaymentAddress', - title: 'Payment Address', - subtitle: 'Address to receive royalties', - placeholder: 'stars1234567890abcdefghijklmnopqrstuvwxyz...', - }) - - const royaltyShareState = useNumberInputState({ - id: 'royalty-share', - name: 'royaltyShare', - title: 'Share Percentage', - subtitle: 'Percentage of royalties to be paid', - placeholder: '8', - }) - - const { data, isLoading, mutate } = useMutation( - async (event: FormEvent): Promise => { - event.preventDefault() - if (!contract) { - throw new Error('Smart contract connection failed') - } - - let royaltyInfo = null - if (royaltyPaymentAddressState.value && royaltyShareState.value) { - royaltyInfo = { - paymentAddress: royaltyPaymentAddressState.value, - share: royaltyShareState.value, - } - } - - const msg = { - name: nameState.value, - symbol: symbolState.value, - minter: minterState.value, - collection_info: { - creator: creatorState.value, - description: descriptionState.value, - image: imageState.value, - external_link: externalLinkState.value || null, - royalty_info: royaltyInfo, - }, - } - return toast.promise(contract.instantiate(SG721_CODE_ID, msg, 'Stargaze Sg721 Contract', wallet.address), { - loading: 'Instantiating contract...', - error: 'Instantiation failed!', - success: 'Instantiation success!', - }) - }, - { - onError: (error) => { - toast.error(String(error)) - }, - }, - ) - - return ( - - - - - - - - Instantiate success! Here is the transaction result containing the contract address and the transaction - hash. - - -
-
- - - - - - - - - - - - - - - - - - - -
-
- -
- - ) -} - -export default withMetadata(Sg721InstantiatePage, { center: false }) diff --git a/pages/contracts/sg721/query.tsx b/pages/contracts/sg721/query.tsx index f966f60..22c93d0 100644 --- a/pages/contracts/sg721/query.tsx +++ b/pages/contracts/sg721/query.tsx @@ -96,7 +96,7 @@ const Sg721QueryPage: NextPage = () => { link={links.Documentation} title="Sg721 Contract" /> - +
diff --git a/pages/contracts/whitelist/instantiate.tsx b/pages/contracts/whitelist/instantiate.tsx index 2fd6649..440f1d4 100644 --- a/pages/contracts/whitelist/instantiate.tsx +++ b/pages/contracts/whitelist/instantiate.tsx @@ -76,7 +76,7 @@ const Sg721InstantiatePage: NextPage = () => { members: whitelistArray, start_time: (startDate.getTime() * 1_000_000).toString(), end_time: (endDate.getTime() * 1_000_000).toString(), - unit_price: coin(String(Number(unitPriceState.value) * 1000000), 'ustars'), + mint_price: coin(String(Number(unitPriceState.value) * 1000000), 'ustars'), per_address_limit: perAddressLimitState.value, member_limit: memberLimitState.value, } diff --git a/utils/constants.ts b/utils/constants.ts index bf13826..b6933db 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -1,6 +1,7 @@ export const SG721_CODE_ID = parseInt(process.env.NEXT_PUBLIC_SG721_CODE_ID, 10) -export const MINTER_CODE_ID = parseInt(process.env.NEXT_PUBLIC_MINTER_CODE_ID, 10) export const WHITELIST_CODE_ID = parseInt(process.env.NEXT_PUBLIC_WHITELIST_CODE_ID, 10) +export const VENDING_MINTER_CODE_ID = parseInt(process.env.NEXT_PUBLIC_VENDING_MINTER_CODE_ID, 10) +export const VENDING_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_VENDING_FACTORY_ADDRESS export const PINATA_ENDPOINT_URL = process.env.NEXT_PUBLIC_PINATA_ENDPOINT_URL export const NETWORK = process.env.NEXT_PUBLIC_NETWORK