Launchpad V2 sync (#34)
* V2 Sync * v2 sync * Launchpad V2 sync * Update trading start time description * Add explicit_content to CollectionDetails update dependencies * Minor UI changes * Update MintPriceMessage interface * Add symbolState.value to CollectionDetails update dependencies * Add external_link to Collection Details * Remove the tab Instantiate from the minter contract dashboard * Add price check for update_minting_price * Implement dynamic per address limit check * Add checks for trading start time * Update Minter Contract Dashboard Instantiate Tab - 1 * Update Minter Contract Dashboard Instantiate Tab - 2 * Remove Instantiate tab from SG721 Contract Dashboard * Update whitelist contract helpers * Update whitelist instantiate fee wrt member limit Co-authored-by: name-user1 <eray@deuslabs.fi> Co-authored-by: Serkan Reis <serkanreis@gmail.com>
This commit is contained in:
parent
2c8f66a5d6
commit
039b8b424b
@ -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
|
||||
|
@ -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`,
|
||||
|
@ -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 && <NumberInput {...tokenIdState} />}
|
||||
{showTokenIdListField && <TextInput {...tokenIdListState} />}
|
||||
{showNumberOfTokensField && <NumberInput {...batchNumberState} />}
|
||||
{showPriceField && <NumberInput {...priceState} />}
|
||||
{showAirdropFileField && (
|
||||
<FormGroup
|
||||
subtitle="CSV file that contains the airdrop addresses and the amount of tokens allocated for each address. Should start with the following header row: address,amount"
|
||||
|
@ -6,11 +6,15 @@ import { useSG721Contract } from 'contracts/sg721'
|
||||
export type ActionType = typeof ACTION_TYPES[number]
|
||||
|
||||
export const ACTION_TYPES = [
|
||||
'mint',
|
||||
'purge',
|
||||
'update_mint_price',
|
||||
'mint_to',
|
||||
'mint_for',
|
||||
'batch_mint',
|
||||
'set_whitelist',
|
||||
'update_start_time',
|
||||
'update_start_trading_time',
|
||||
'update_per_address_limit',
|
||||
'withdraw',
|
||||
'transfer',
|
||||
@ -19,6 +23,7 @@ export const ACTION_TYPES = [
|
||||
'batch_burn',
|
||||
'shuffle',
|
||||
'airdrop',
|
||||
'burn_remaining',
|
||||
] as const
|
||||
|
||||
export interface ActionListItem {
|
||||
@ -28,6 +33,21 @@ export interface ActionListItem {
|
||||
}
|
||||
|
||||
export const ACTION_LIST: ActionListItem[] = [
|
||||
{
|
||||
id: 'mint',
|
||||
name: 'Mint',
|
||||
description: `Mint a token`,
|
||||
},
|
||||
{
|
||||
id: 'purge',
|
||||
name: 'Purge',
|
||||
description: `Purge`,
|
||||
},
|
||||
{
|
||||
id: 'update_mint_price',
|
||||
name: 'Update Mint Price',
|
||||
description: `Update mint price`,
|
||||
},
|
||||
{
|
||||
id: 'mint_to',
|
||||
name: 'Mint To',
|
||||
@ -50,9 +70,14 @@ export const ACTION_LIST: ActionListItem[] = [
|
||||
},
|
||||
{
|
||||
id: 'update_start_time',
|
||||
name: 'Update Start Time',
|
||||
name: 'Update Minting Start Time',
|
||||
description: `Update start time for minting`,
|
||||
},
|
||||
{
|
||||
id: 'update_start_trading_time',
|
||||
name: 'Update Trading Start Time',
|
||||
description: `Update start time for trading`,
|
||||
},
|
||||
{
|
||||
id: 'update_per_address_limit',
|
||||
name: 'Update Tokens Per Address Limit',
|
||||
@ -93,6 +118,11 @@ export const ACTION_LIST: ActionListItem[] = [
|
||||
name: 'Airdrop Tokens',
|
||||
description: 'Airdrop tokens to given addresses',
|
||||
},
|
||||
{
|
||||
id: 'burn_remaining',
|
||||
name: 'Burn Remaining Tokens',
|
||||
description: 'Burn remaining tokens',
|
||||
},
|
||||
]
|
||||
|
||||
export interface DispatchExecuteProps {
|
||||
@ -111,11 +141,15 @@ export type DispatchExecuteArgs = {
|
||||
txSigner: string
|
||||
} & (
|
||||
| { type: undefined }
|
||||
| { type: Select<'mint'> }
|
||||
| { 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 {}
|
||||
}
|
||||
|
@ -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<File | null>(null)
|
||||
const [timestamp, setTimestamp] = useState<Date | undefined>()
|
||||
const [explicit, setExplicit] = useState<boolean>(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<HTMLInputElement>) => {
|
||||
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 (
|
||||
<div>
|
||||
<FormGroup subtitle="Information about your collection" title="Collection Details">
|
||||
<TextInput {...nameState} isRequired />
|
||||
<TextInput {...descriptionState} isRequired />
|
||||
<TextInput {...symbolState} isRequired />
|
||||
<TextInput {...externalLinkState} />
|
||||
<FormControl htmlId="timestamp" subtitle="Trading start time (local)" title="Trading Start Time (optional)">
|
||||
<InputDateTime minDate={new Date()} onChange={(date) => setTimestamp(date)} value={timestamp} />
|
||||
</FormControl>
|
||||
|
||||
<FormControl isRequired={uploadMethod === 'new'} title="Cover Image">
|
||||
{uploadMethod === 'new' && (
|
||||
@ -135,8 +158,51 @@ export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl }: Col
|
||||
<span className="italic font-light ">Waiting for cover image URL to be specified.</span>
|
||||
)}
|
||||
</FormControl>
|
||||
|
||||
<TextInput {...externalLinkState} />
|
||||
<div className="flex flex-col space-y-2">
|
||||
<div>
|
||||
<div className="flex">
|
||||
<span className="mt-1 text-sm first-letter:capitalize">
|
||||
Does the collection contain explicit content?
|
||||
</span>
|
||||
<div className="ml-2 font-bold form-check form-check-inline">
|
||||
<input
|
||||
checked={explicit}
|
||||
className="peer sr-only"
|
||||
id="explicitRadio1"
|
||||
name="explicitRadioOptions1"
|
||||
onClick={() => {
|
||||
setExplicit(true)
|
||||
}}
|
||||
type="radio"
|
||||
/>
|
||||
<label
|
||||
className="inline-block py-1 px-2 text-sm text-gray peer-checked:text-white hover:text-white peer-checked:bg-black hover:rounded-sm peer-checked:border-b-2 hover:border-b-2 peer-checked:border-plumbus hover:border-plumbus cursor-pointer form-check-label"
|
||||
htmlFor="explicitRadio1"
|
||||
>
|
||||
YES
|
||||
</label>
|
||||
</div>
|
||||
<div className="ml-2 font-bold form-check form-check-inline">
|
||||
<input
|
||||
checked={!explicit}
|
||||
className="peer sr-only"
|
||||
id="explicitRadio2"
|
||||
name="explicitRadioOptions2"
|
||||
onClick={() => {
|
||||
setExplicit(false)
|
||||
}}
|
||||
type="radio"
|
||||
/>
|
||||
<label
|
||||
className="inline-block py-1 px-2 text-sm text-gray peer-checked:text-white hover:text-white peer-checked:bg-black hover:rounded-sm peer-checked:border-b-2 hover:border-b-2 peer-checked:border-plumbus hover:border-plumbus cursor-pointer form-check-label"
|
||||
htmlFor="explicitRadio2"
|
||||
>
|
||||
NO
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FormGroup>
|
||||
</div>
|
||||
)
|
||||
|
@ -69,6 +69,26 @@ export const TextInput = forwardRef<HTMLInputElement, FormInputProps>(
|
||||
//
|
||||
)
|
||||
|
||||
export const CheckBoxInput = forwardRef<HTMLInputElement, FormInputProps>(
|
||||
function CheckBoxInput(props, ref) {
|
||||
return (
|
||||
<div className="flex flex-col space-y-2">
|
||||
<label className="flex flex-col space-y-1" htmlFor="explicit">
|
||||
<span className="font-bold first-letter:capitalize">Explicit Content</span>
|
||||
</label>
|
||||
<input
|
||||
className="placeholder:text-white/50 bg-white/10 rounded border-2 border-white/20 focus:ring focus:ring-plumbus-20"
|
||||
id="explicit"
|
||||
name="explicit"
|
||||
type="checkbox"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
//
|
||||
)
|
||||
|
||||
export const UrlInput = forwardRef<HTMLInputElement, FormInputProps>(
|
||||
function UrlInput(props, ref) {
|
||||
return <FormInput {...props} ref={ref} type="url" />
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -28,9 +28,12 @@ export interface MinterInstance {
|
||||
getMintCount: (address: string) => Promise<any>
|
||||
|
||||
//Execute
|
||||
mint: (senderAddress: string, price: string) => Promise<string>
|
||||
mint: (senderAddress: string) => Promise<string>
|
||||
purge: (senderAddress: string) => Promise<string>
|
||||
updateMintPrice: (senderAddress: string, price: string) => Promise<string>
|
||||
setWhitelist: (senderAddress: string, whitelist: string) => Promise<string>
|
||||
updateStartTime: (senderAddress: string, time: Timestamp) => Promise<string>
|
||||
updateStartTradingTime: (senderAddress: string, time: Timestamp) => Promise<string>
|
||||
updatePerAddressLimit: (senderAddress: string, perAddressLimit: number) => Promise<string>
|
||||
mintTo: (senderAddress: string, recipient: string) => Promise<string>
|
||||
mintFor: (senderAddress: string, recipient: string, tokenId: number) => Promise<string>
|
||||
@ -38,12 +41,16 @@ export interface MinterInstance {
|
||||
shuffle: (senderAddress: string) => Promise<string>
|
||||
withdraw: (senderAddress: string) => Promise<string>
|
||||
airdrop: (senderAddress: string, recipients: string[]) => Promise<string>
|
||||
burnRemaining: (senderAddress: string) => Promise<string>
|
||||
}
|
||||
|
||||
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<string, never>
|
||||
}
|
||||
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<string, never>
|
||||
}
|
||||
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<any> => {
|
||||
const getMintPrice = async (): Promise<MintPriceMessage> => {
|
||||
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<string> => {
|
||||
const mint = async (senderAddress: string): Promise<string> => {
|
||||
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<string> => {
|
||||
const res = await client.execute(
|
||||
senderAddress,
|
||||
contractAddress,
|
||||
{
|
||||
purge: {},
|
||||
},
|
||||
'auto',
|
||||
'',
|
||||
)
|
||||
|
||||
return res.transactionHash
|
||||
}
|
||||
|
||||
const updateMintPrice = async (senderAddress: string, price: string): Promise<string> => {
|
||||
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<string> => {
|
||||
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<string> => {
|
||||
const res = await client.execute(
|
||||
senderAddress,
|
||||
contractAddress,
|
||||
{
|
||||
update_start_trading_time: { time },
|
||||
},
|
||||
'auto',
|
||||
'',
|
||||
)
|
||||
|
||||
return res.transactionHash
|
||||
}
|
||||
|
||||
const updatePerAddressLimit = async (senderAddress: string, perAddressLimit: number): Promise<string> => {
|
||||
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<string> => {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {}
|
||||
}
|
||||
|
82
contracts/vendingFactory/contract.ts
Normal file
82
contracts/vendingFactory/contract.ts
Normal file
@ -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<string, unknown>, funds: Coin[]) => Promise<CreateMinterResponse>
|
||||
}
|
||||
|
||||
export interface VendingFactoryMessages {
|
||||
createMinter: (msg: Record<string, unknown>) => CreateMinterMessage
|
||||
}
|
||||
|
||||
export interface CreateMinterMessage {
|
||||
sender: string
|
||||
contract: string
|
||||
msg: Record<string, unknown>
|
||||
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<string, unknown>,
|
||||
funds: Coin[],
|
||||
): Promise<CreateMinterResponse> => {
|
||||
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<string, unknown>): CreateMinterMessage => {
|
||||
return {
|
||||
sender: txSigner,
|
||||
contract: contractAddress,
|
||||
msg,
|
||||
funds: [coin('1000000000', 'ustars')],
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
createMinter,
|
||||
}
|
||||
}
|
||||
|
||||
return { use, messages }
|
||||
}
|
2
contracts/vendingFactory/index.ts
Normal file
2
contracts/vendingFactory/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './contract'
|
||||
export * from './useContract'
|
28
contracts/vendingFactory/messages/execute.ts
Normal file
28
contracts/vendingFactory/messages/execute.ts
Normal file
@ -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<string, unknown>
|
||||
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)
|
||||
}
|
76
contracts/vendingFactory/useContract.ts
Normal file
76
contracts/vendingFactory/useContract.ts
Normal file
@ -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<string>('')
|
||||
const [vendingFactory, setVendingFactory] = useState<VendingFactoryContract>()
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
@ -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<InstantiateResponse> => {
|
||||
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,
|
||||
})
|
||||
|
||||
|
18
env.
18
env.
@ -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
|
3
env.d.ts
vendored
3
env.d.ts
vendored
@ -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
|
||||
|
@ -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<HTMLDivElement>(null)
|
||||
const messages = useMemo(
|
||||
() => vendingFactoryContract?.use(VENDING_FACTORY_ADDRESS),
|
||||
[vendingFactoryContract, wallet.address],
|
||||
)
|
||||
|
||||
const [uploadDetails, setUploadDetails] = useState<UploadDetailsDataProps | null>(null)
|
||||
const [collectionDetails, setCollectionDetails] = useState<CollectionDetailsDataProps | null>(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<string> => {
|
||||
@ -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')
|
||||
}
|
||||
|
@ -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<Date | undefined>(undefined)
|
||||
const [timestamp, setTimestamp] = useState<Date | undefined>()
|
||||
const [explicit, setExplicit] = useState<boolean>(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<InstantiateResponse | null> => {
|
||||
async (event: FormEvent): Promise<CreateMinterResponse | null> => {
|
||||
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<CreateMinterResponse>,
|
||||
{
|
||||
loading: 'Instantiating contract...',
|
||||
error: 'Instantiation failed!',
|
||||
success: 'Instantiation success!',
|
||||
},
|
||||
)
|
||||
},
|
||||
{
|
||||
onError: (error) => {
|
||||
@ -240,7 +246,6 @@ const MinterInstantiatePage: NextPage = () => {
|
||||
<NumberInput isRequired {...codeIdState} />
|
||||
<TextInput isRequired {...nameState} />
|
||||
<TextInput isRequired {...symbolState} />
|
||||
<TextInput isRequired {...minterState} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup subtitle="Information about your collection" title="Collection Details">
|
||||
@ -248,6 +253,54 @@ const MinterInstantiatePage: NextPage = () => {
|
||||
<FormTextArea isRequired {...descriptionState} />
|
||||
<TextInput isRequired {...imageState} />
|
||||
<TextInput {...externalLinkState} />
|
||||
<FormControl htmlId="timestamp" subtitle="Trading start time (local)" title="Trading Start Time (optional)">
|
||||
<InputDateTime minDate={new Date()} onChange={(date) => setTimestamp(date)} value={timestamp} />
|
||||
</FormControl>
|
||||
<div className="flex flex-col space-y-2">
|
||||
<div>
|
||||
<div className="flex">
|
||||
<span className="mt-1 text-sm first-letter:capitalize">
|
||||
Does the collection contain explicit content?
|
||||
</span>
|
||||
<div className="ml-2 font-bold form-check form-check-inline">
|
||||
<input
|
||||
checked={explicit}
|
||||
className="peer sr-only"
|
||||
id="explicitRadio1"
|
||||
name="explicitRadioOptions1"
|
||||
onClick={() => {
|
||||
setExplicit(true)
|
||||
}}
|
||||
type="radio"
|
||||
/>
|
||||
<label
|
||||
className="inline-block py-1 px-2 text-sm text-gray peer-checked:text-white hover:text-white peer-checked:bg-black hover:rounded-sm peer-checked:border-b-2 hover:border-b-2 peer-checked:border-plumbus hover:border-plumbus cursor-pointer form-check-label"
|
||||
htmlFor="explicitRadio1"
|
||||
>
|
||||
YES
|
||||
</label>
|
||||
</div>
|
||||
<div className="ml-2 font-bold form-check form-check-inline">
|
||||
<input
|
||||
checked={!explicit}
|
||||
className="peer sr-only"
|
||||
id="explicitRadio2"
|
||||
name="explicitRadioOptions2"
|
||||
onClick={() => {
|
||||
setExplicit(false)
|
||||
}}
|
||||
type="radio"
|
||||
/>
|
||||
<label
|
||||
className="inline-block py-1 px-2 text-sm text-gray peer-checked:text-white hover:text-white peer-checked:bg-black hover:rounded-sm peer-checked:border-b-2 hover:border-b-2 peer-checked:border-plumbus hover:border-plumbus cursor-pointer form-check-label"
|
||||
htmlFor="explicitRadio2"
|
||||
>
|
||||
NO
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup subtitle="Information about royalty" title="Royalty Details">
|
||||
|
@ -145,7 +145,7 @@ const Sg721ExecutePage: NextPage = () => {
|
||||
link={links.Documentation}
|
||||
title="Sg721 Contract"
|
||||
/>
|
||||
<LinkTabs activeIndex={2} data={sg721LinkTabs} />
|
||||
<LinkTabs activeIndex={1} data={sg721LinkTabs} />
|
||||
|
||||
<form className="grid grid-cols-2 p-4 space-x-8" onSubmit={mutate}>
|
||||
<div className="space-y-8">
|
||||
|
@ -1 +1 @@
|
||||
export { default } from './instantiate'
|
||||
export { default } from './query'
|
||||
|
@ -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<InstantiateResponse | null> => {
|
||||
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 (
|
||||
<form className="py-6 px-12 space-y-4" onSubmit={mutate}>
|
||||
<NextSeo title="Instantiate Sg721 Contract" />
|
||||
<ContractPageHeader
|
||||
description="Sg721 contract is a wrapper contract that has a set of optional extensions on top of cw721-base."
|
||||
link={links.Documentation}
|
||||
title="Sg721 Contract"
|
||||
/>
|
||||
<LinkTabs activeIndex={0} data={sg721LinkTabs} />
|
||||
|
||||
<Conditional test={Boolean(data)}>
|
||||
<Alert type="info">
|
||||
<b>Instantiate success!</b> Here is the transaction result containing the contract address and the transaction
|
||||
hash.
|
||||
</Alert>
|
||||
<JsonPreview content={data} title="Transaction Result" />
|
||||
<br />
|
||||
</Conditional>
|
||||
|
||||
<FormGroup subtitle="Information about your sg721 contract" title="SG721 Contract Details">
|
||||
<TextInput isRequired {...nameState} />
|
||||
<TextInput isRequired {...symbolState} />
|
||||
<TextInput isRequired {...minterState} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup subtitle="Information about your collection" title="Collection Details">
|
||||
<TextInput isRequired {...creatorState} />
|
||||
<FormTextArea isRequired {...descriptionState} />
|
||||
<TextInput isRequired {...imageState} />
|
||||
<TextInput {...externalLinkState} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup subtitle="Information about royalty" title="Royalty Details">
|
||||
<TextInput {...royaltyPaymentAddressState} />
|
||||
<NumberInput {...royaltyShareState} />
|
||||
</FormGroup>
|
||||
|
||||
<div className="flex items-center p-4">
|
||||
<div className="flex-grow" />
|
||||
<Button isLoading={isLoading} isWide rightIcon={<FaAsterisk />} type="submit">
|
||||
Instantiate Contract
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
export default withMetadata(Sg721InstantiatePage, { center: false })
|
@ -96,7 +96,7 @@ const Sg721QueryPage: NextPage = () => {
|
||||
link={links.Documentation}
|
||||
title="Sg721 Contract"
|
||||
/>
|
||||
<LinkTabs activeIndex={1} data={sg721LinkTabs} />
|
||||
<LinkTabs activeIndex={0} data={sg721LinkTabs} />
|
||||
|
||||
<div className="grid grid-cols-2 p-4 space-x-8">
|
||||
<div className="space-y-8">
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user