Add burn & batch burn for sg721 (#39)
* Add burn & batch burn for sg721 * Add batch burn range * Refactor contract messages logic Co-authored-by: findolor <anakisci@gmail.com>
This commit is contained in:
parent
1624f0c332
commit
aae2b14a01
@ -14,6 +14,8 @@ export const ACTION_TYPES = [
|
||||
'update_per_address_limit',
|
||||
'withdraw',
|
||||
'transfer',
|
||||
'burn',
|
||||
'batch_burn',
|
||||
'shuffle',
|
||||
] as const
|
||||
|
||||
@ -64,6 +66,16 @@ export const ACTION_LIST: ActionListItem[] = [
|
||||
name: 'Transfer Tokens',
|
||||
description: `Transfer tokens from one address to another`,
|
||||
},
|
||||
{
|
||||
id: 'burn',
|
||||
name: 'Burn Token',
|
||||
description: `Burn a specified token from the collection`,
|
||||
},
|
||||
{
|
||||
id: 'batch_burn',
|
||||
name: 'Batch Burn Tokens',
|
||||
description: `Burn a list of tokens from the collection`,
|
||||
},
|
||||
{
|
||||
id: 'shuffle',
|
||||
name: 'Shuffle Tokens',
|
||||
@ -96,6 +108,8 @@ export type DispatchExecuteArgs = {
|
||||
| { type: Select<'shuffle'> }
|
||||
| { type: Select<'withdraw'> }
|
||||
| { type: Select<'transfer'>; recipient: string; tokenId: number }
|
||||
| { type: Select<'burn'>; tokenId: number }
|
||||
| { type: Select<'batch_burn'>; tokenIds: string }
|
||||
)
|
||||
|
||||
export const dispatchExecute = async (args: DispatchExecuteArgs) => {
|
||||
@ -131,6 +145,12 @@ export const dispatchExecute = async (args: DispatchExecuteArgs) => {
|
||||
case 'transfer': {
|
||||
return sg721Messages.transferNft(args.recipient, args.tokenId.toString())
|
||||
}
|
||||
case 'burn': {
|
||||
return sg721Messages.burn(args.tokenId.toString())
|
||||
}
|
||||
case 'batch_burn': {
|
||||
return sg721Messages.batchBurn(args.tokenIds)
|
||||
}
|
||||
default: {
|
||||
throw new Error('Unknown action')
|
||||
}
|
||||
@ -145,32 +165,38 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => {
|
||||
const { minterContract, sg721Contract } = args
|
||||
switch (args.type) {
|
||||
case 'mint_to': {
|
||||
return minterMessages()?.mintTo(minterContract, args.recipient)
|
||||
return minterMessages(minterContract)?.mintTo(args.recipient)
|
||||
}
|
||||
case 'mint_for': {
|
||||
return minterMessages()?.mintFor(minterContract, args.recipient, args.tokenId)
|
||||
return minterMessages(minterContract)?.mintFor(args.recipient, args.tokenId)
|
||||
}
|
||||
case 'batch_mint': {
|
||||
return minterMessages()?.batchMint(minterContract, args.recipient, args.batchNumber)
|
||||
return minterMessages(minterContract)?.batchMint(args.recipient, args.batchNumber)
|
||||
}
|
||||
case 'set_whitelist': {
|
||||
return minterMessages()?.setWhitelist(minterContract, args.whitelist)
|
||||
return minterMessages(minterContract)?.setWhitelist(args.whitelist)
|
||||
}
|
||||
case 'update_start_time': {
|
||||
return minterMessages()?.updateStartTime(minterContract, args.startTime)
|
||||
return minterMessages(minterContract)?.updateStartTime(args.startTime)
|
||||
}
|
||||
case 'update_per_address_limit': {
|
||||
return minterMessages()?.updatePerAddressLimit(minterContract, args.limit)
|
||||
return minterMessages(minterContract)?.updatePerAddressLimit(args.limit)
|
||||
}
|
||||
case 'shuffle': {
|
||||
return minterMessages()?.shuffle(minterContract)
|
||||
return minterMessages(minterContract)?.shuffle()
|
||||
}
|
||||
case 'withdraw': {
|
||||
return minterMessages()?.withdraw(minterContract)
|
||||
return minterMessages(minterContract)?.withdraw()
|
||||
}
|
||||
case 'transfer': {
|
||||
return sg721Messages(sg721Contract)?.transferNft(args.recipient, args.tokenId.toString())
|
||||
}
|
||||
case 'burn': {
|
||||
return sg721Messages(sg721Contract)?.burn(args.tokenId.toString())
|
||||
}
|
||||
case 'batch_burn': {
|
||||
return sg721Messages(sg721Contract)?.batchBurn(args.tokenIds)
|
||||
}
|
||||
default: {
|
||||
return {}
|
||||
}
|
||||
|
@ -40,15 +40,15 @@ export interface MinterInstance {
|
||||
}
|
||||
|
||||
export interface MinterMessages {
|
||||
mint: (contractAddress: string, price: string) => MintMessage
|
||||
setWhitelist: (contractAddress: string, whitelist: string) => SetWhitelistMessage
|
||||
updateStartTime: (contractAddress: string, time: Timestamp) => UpdateStarTimeMessage
|
||||
updatePerAddressLimit: (contractAddress: string, perAddressLimit: number) => UpdatePerAddressLimitMessage
|
||||
mintTo: (contractAddress: string, recipient: string) => MintToMessage
|
||||
mintFor: (contractAddress: string, recipient: string, tokenId: number) => MintForMessage
|
||||
batchMint: (contractAddress: string, recipient: string, batchNumber: number) => BatchMintMessage
|
||||
shuffle: (contractAddress: string) => ShuffleMessage
|
||||
withdraw: (contractAddress: string) => WithdrawMessage
|
||||
mint: (price: string) => MintMessage
|
||||
setWhitelist: (whitelist: string) => SetWhitelistMessage
|
||||
updateStartTime: (time: Timestamp) => UpdateStarTimeMessage
|
||||
updatePerAddressLimit: (perAddressLimit: number) => UpdatePerAddressLimitMessage
|
||||
mintTo: (recipient: string) => MintToMessage
|
||||
mintFor: (recipient: string, tokenId: number) => MintForMessage
|
||||
batchMint: (recipient: string, batchNumber: number) => BatchMintMessage
|
||||
shuffle: () => ShuffleMessage
|
||||
withdraw: () => WithdrawMessage
|
||||
}
|
||||
|
||||
export interface MintMessage {
|
||||
@ -151,7 +151,7 @@ export interface MinterContract {
|
||||
|
||||
use: (contractAddress: string) => MinterInstance
|
||||
|
||||
messages: () => MinterMessages
|
||||
messages: (contractAddress: string) => MinterMessages
|
||||
}
|
||||
|
||||
export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterContract => {
|
||||
@ -365,8 +365,8 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC
|
||||
}
|
||||
}
|
||||
|
||||
const messages = () => {
|
||||
const mint = (contractAddress: string, price: string): MintMessage => {
|
||||
const messages = (contractAddress: string) => {
|
||||
const mint = (price: string): MintMessage => {
|
||||
return {
|
||||
sender: txSigner,
|
||||
contract: contractAddress,
|
||||
@ -377,7 +377,7 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC
|
||||
}
|
||||
}
|
||||
|
||||
const setWhitelist = (contractAddress: string, whitelist: string): SetWhitelistMessage => {
|
||||
const setWhitelist = (whitelist: string): SetWhitelistMessage => {
|
||||
return {
|
||||
sender: txSigner,
|
||||
contract: contractAddress,
|
||||
@ -390,7 +390,7 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC
|
||||
}
|
||||
}
|
||||
|
||||
const updateStartTime = (contractAddress: string, startTime: string): UpdateStarTimeMessage => {
|
||||
const updateStartTime = (startTime: string): UpdateStarTimeMessage => {
|
||||
return {
|
||||
sender: txSigner,
|
||||
contract: contractAddress,
|
||||
@ -401,7 +401,7 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC
|
||||
}
|
||||
}
|
||||
|
||||
const updatePerAddressLimit = (contractAddress: string, limit: number): UpdatePerAddressLimitMessage => {
|
||||
const updatePerAddressLimit = (limit: number): UpdatePerAddressLimitMessage => {
|
||||
return {
|
||||
sender: txSigner,
|
||||
contract: contractAddress,
|
||||
@ -414,7 +414,7 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC
|
||||
}
|
||||
}
|
||||
|
||||
const mintTo = (contractAddress: string, recipient: string): MintToMessage => {
|
||||
const mintTo = (recipient: string): MintToMessage => {
|
||||
return {
|
||||
sender: txSigner,
|
||||
contract: contractAddress,
|
||||
@ -427,7 +427,7 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC
|
||||
}
|
||||
}
|
||||
|
||||
const mintFor = (contractAddress: string, recipient: string, tokenId: number): MintForMessage => {
|
||||
const mintFor = (recipient: string, tokenId: number): MintForMessage => {
|
||||
return {
|
||||
sender: txSigner,
|
||||
contract: contractAddress,
|
||||
@ -441,7 +441,7 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC
|
||||
}
|
||||
}
|
||||
|
||||
const batchMint = (contractAddress: string, recipient: string, batchNumber: number): BatchMintMessage => {
|
||||
const batchMint = (recipient: string, batchNumber: number): BatchMintMessage => {
|
||||
const msg: Record<string, unknown>[] = []
|
||||
for (let i = 0; i < batchNumber; i++) {
|
||||
msg.push({ mint_to: { recipient } })
|
||||
@ -454,7 +454,7 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC
|
||||
}
|
||||
}
|
||||
|
||||
const shuffle = (contractAddress: string): ShuffleMessage => {
|
||||
const shuffle = (): ShuffleMessage => {
|
||||
return {
|
||||
sender: txSigner,
|
||||
contract: contractAddress,
|
||||
@ -465,7 +465,7 @@ export const minter = (client: SigningCosmWasmClient, txSigner: string): MinterC
|
||||
}
|
||||
}
|
||||
|
||||
const withdraw = (contractAddress: string): WithdrawMessage => {
|
||||
const withdraw = (): WithdrawMessage => {
|
||||
return {
|
||||
sender: txSigner,
|
||||
contract: contractAddress,
|
||||
|
@ -35,7 +35,7 @@ export interface UseMinterContractProps {
|
||||
use: (customAddress: string) => MinterInstance | undefined
|
||||
updateContractAddress: (contractAddress: string) => void
|
||||
getContractAddress: () => string | undefined
|
||||
messages: () => MinterMessages | undefined
|
||||
messages: (contractAddress: string) => MinterMessages | undefined
|
||||
}
|
||||
|
||||
export function useMinterContract(): UseMinterContractProps {
|
||||
@ -81,9 +81,12 @@ export function useMinterContract(): UseMinterContractProps {
|
||||
return address
|
||||
}
|
||||
|
||||
const messages = useCallback((): MinterMessages | undefined => {
|
||||
return minter?.messages()
|
||||
}, [minter])
|
||||
const messages = useCallback(
|
||||
(customAddress = ''): MinterMessages | undefined => {
|
||||
return minter?.messages(address || customAddress)
|
||||
},
|
||||
[minter, address],
|
||||
)
|
||||
|
||||
return {
|
||||
instantiate,
|
||||
|
@ -1,7 +1,8 @@
|
||||
import type { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate'
|
||||
import type { MsgExecuteContractEncodeObject, SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate'
|
||||
import { toBase64, toUtf8 } from '@cosmjs/encoding'
|
||||
import type { Coin } from '@cosmjs/stargate'
|
||||
import { coin } from '@cosmjs/stargate'
|
||||
import { MsgExecuteContract } from 'cosmjs-types/cosmwasm/wasm/v1/tx'
|
||||
|
||||
export interface InstantiateResponse {
|
||||
readonly contractAddress: string
|
||||
@ -67,6 +68,7 @@ export interface SG721Instance {
|
||||
|
||||
/// Burn an NFT the sender has access to
|
||||
burn: (tokenId: string) => Promise<string>
|
||||
batchBurn: (tokenIds: string) => Promise<string>
|
||||
}
|
||||
|
||||
export interface Sg721Messages {
|
||||
@ -78,6 +80,7 @@ export interface Sg721Messages {
|
||||
revokeAll: (operator: string) => RevokeAllMessage
|
||||
mint: (tokenId: string, owner: string, tokenURI?: string) => MintMessage
|
||||
burn: (tokenId: string) => BurnMessage
|
||||
batchBurn: (tokenIds: string) => BatchBurnMessage
|
||||
}
|
||||
|
||||
export interface TransferNFTMessage {
|
||||
@ -178,6 +181,13 @@ export interface BurnMessage {
|
||||
funds: Coin[]
|
||||
}
|
||||
|
||||
export interface BatchBurnMessage {
|
||||
sender: string
|
||||
contract: string
|
||||
msg: Record<string, unknown>[]
|
||||
funds: Coin[]
|
||||
}
|
||||
|
||||
export interface SG721Contract {
|
||||
instantiate: (
|
||||
senderAddress: string,
|
||||
@ -408,6 +418,49 @@ export const SG721 = (client: SigningCosmWasmClient, txSigner: string): SG721Con
|
||||
return res.transactionHash
|
||||
}
|
||||
|
||||
const batchBurn = async (tokenIds: string): Promise<string> => {
|
||||
const executeContractMsgs: MsgExecuteContractEncodeObject[] = []
|
||||
if (tokenIds.includes(':')) {
|
||||
const [start, end] = tokenIds.split(':').map(Number)
|
||||
for (let i = start; i <= end; i++) {
|
||||
const msg = {
|
||||
burn: { token_id: i.toString() },
|
||||
}
|
||||
const executeContractMsg: MsgExecuteContractEncodeObject = {
|
||||
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
|
||||
value: MsgExecuteContract.fromPartial({
|
||||
sender: txSigner,
|
||||
contract: contractAddress,
|
||||
msg: toUtf8(JSON.stringify(msg)),
|
||||
}),
|
||||
}
|
||||
|
||||
executeContractMsgs.push(executeContractMsg)
|
||||
}
|
||||
} else {
|
||||
const tokenNumbers = tokenIds.split(',').map(Number)
|
||||
for (let i = 0; i < tokenNumbers.length; i++) {
|
||||
const msg = {
|
||||
burn: { token_id: tokenNumbers[i].toString() },
|
||||
}
|
||||
const executeContractMsg: MsgExecuteContractEncodeObject = {
|
||||
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
|
||||
value: MsgExecuteContract.fromPartial({
|
||||
sender: txSigner,
|
||||
contract: contractAddress,
|
||||
msg: toUtf8(JSON.stringify(msg)),
|
||||
}),
|
||||
}
|
||||
|
||||
executeContractMsgs.push(executeContractMsg)
|
||||
}
|
||||
}
|
||||
|
||||
const res = await client.signAndBroadcast(txSigner, executeContractMsgs, 'auto', 'batch burn')
|
||||
|
||||
return res.transactionHash
|
||||
}
|
||||
|
||||
return {
|
||||
contractAddress,
|
||||
ownerOf,
|
||||
@ -430,6 +483,7 @@ export const SG721 = (client: SigningCosmWasmClient, txSigner: string): SG721Con
|
||||
revokeAll,
|
||||
mint,
|
||||
burn,
|
||||
batchBurn,
|
||||
}
|
||||
}
|
||||
|
||||
@ -552,10 +606,10 @@ export const SG721 = (client: SigningCosmWasmClient, txSigner: string): SG721Con
|
||||
}
|
||||
}
|
||||
|
||||
const burn = (tokenId: string) => {
|
||||
const burn = (contractAddr: string, tokenId: string) => {
|
||||
return {
|
||||
sender: txSigner,
|
||||
contract: contractAddress,
|
||||
contract: contractAddr,
|
||||
msg: {
|
||||
burn: {
|
||||
token_id: tokenId,
|
||||
@ -565,6 +619,30 @@ export const SG721 = (client: SigningCosmWasmClient, txSigner: string): SG721Con
|
||||
}
|
||||
}
|
||||
|
||||
const batchBurn = (contractAddr: string, tokenIds: string): BatchBurnMessage => {
|
||||
const msg: Record<string, unknown>[] = []
|
||||
if (tokenIds.includes(':')) {
|
||||
const [start, end] = tokenIds.split(':').map(Number)
|
||||
for (let i = start; i <= end; i++) {
|
||||
msg.push({
|
||||
burn: { token_id: i.toString() },
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const tokenNumbers = tokenIds.split(',').map(Number)
|
||||
for (let i = 0; i < tokenNumbers.length; i++) {
|
||||
msg.push({ burn: { token_id: tokenNumbers[i].toString() } })
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
sender: txSigner,
|
||||
contract: contractAddr,
|
||||
msg,
|
||||
funds: [],
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
transferNft,
|
||||
sendNft,
|
||||
@ -574,6 +652,7 @@ export const SG721 = (client: SigningCosmWasmClient, txSigner: string): SG721Con
|
||||
revokeAll,
|
||||
mint,
|
||||
burn,
|
||||
batchBurn,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => {
|
||||
return messages(contract)?.mint(args.recipient, args.tokenId, args.tokenURI)
|
||||
}
|
||||
case 'burn': {
|
||||
return messages(contract)?.burn(args.tokenId)
|
||||
return messages(contract)?.burn(args.contract, args.tokenId)
|
||||
}
|
||||
default: {
|
||||
return {}
|
||||
|
@ -62,9 +62,12 @@ export function useSG721Contract(): UseSG721ContractProps {
|
||||
[SG721, address],
|
||||
)
|
||||
|
||||
const messages = useCallback((): Sg721Messages | undefined => {
|
||||
return SG721?.messages(address)
|
||||
}, [SG721, address])
|
||||
const messages = useCallback(
|
||||
(customAddress = ''): Sg721Messages | undefined => {
|
||||
return SG721?.messages(address || customAddress)
|
||||
},
|
||||
[SG721, address],
|
||||
)
|
||||
|
||||
return {
|
||||
instantiate,
|
||||
|
@ -58,9 +58,12 @@ export function useWhiteListContract(): UseWhiteListContractProps {
|
||||
[whiteList, address],
|
||||
)
|
||||
|
||||
const messages = useCallback((): WhitelistMessages | undefined => {
|
||||
return whiteList?.messages(address)
|
||||
}, [whiteList, address])
|
||||
const messages = useCallback(
|
||||
(customAddress = ''): WhitelistMessages | undefined => {
|
||||
return whiteList?.messages(address || customAddress)
|
||||
},
|
||||
[whiteList, address],
|
||||
)
|
||||
|
||||
return {
|
||||
instantiate,
|
||||
|
@ -23,6 +23,8 @@ import { useMutation } from 'react-query'
|
||||
import { withMetadata } from 'utils/layout'
|
||||
import { links } from 'utils/links'
|
||||
|
||||
import { TextInput } from '../../components/forms/FormInput'
|
||||
|
||||
const CollectionActionsPage: NextPage = () => {
|
||||
const { minter: minterContract, sg721: sg721Contract } = useContracts()
|
||||
const wallet = useWallet()
|
||||
@ -68,6 +70,14 @@ const CollectionActionsPage: NextPage = () => {
|
||||
subtitle: 'Enter the number of tokens to mint',
|
||||
})
|
||||
|
||||
const tokenIdListState = useInputState({
|
||||
id: 'token-id-list',
|
||||
name: 'tokenIdList',
|
||||
title: 'The list of token IDs',
|
||||
subtitle:
|
||||
'Specify individual token IDs separated by commas (e.g., 2, 4, 8) or a range of IDs separated by a colon (e.g, 8:13)',
|
||||
})
|
||||
|
||||
const recipientState = useInputState({
|
||||
id: 'recipient-address',
|
||||
name: 'recipient',
|
||||
@ -85,8 +95,9 @@ const CollectionActionsPage: NextPage = () => {
|
||||
const showWhitelistField = type === 'set_whitelist'
|
||||
const showDateField = type === 'update_start_time'
|
||||
const showLimitField = type === 'update_per_address_limit'
|
||||
const showTokenIdField = isEitherType(type, ['transfer', 'mint_for'])
|
||||
const showTokenIdListField = type === 'batch_mint'
|
||||
const showTokenIdField = isEitherType(type, ['transfer', 'mint_for', 'burn'])
|
||||
const showNumberOfTokensField = type === 'batch_mint'
|
||||
const showTokenIdListField = type === 'batch_burn'
|
||||
const showRecipientField = isEitherType(type, ['transfer', 'mint_to', 'mint_for', 'batch_mint'])
|
||||
|
||||
const minterMessages = useMemo(
|
||||
@ -104,6 +115,7 @@ const CollectionActionsPage: NextPage = () => {
|
||||
minterContract: minterContractState.value,
|
||||
sg721Contract: sg721ContractState.value,
|
||||
tokenId: tokenIdState.value,
|
||||
tokenIds: tokenIdListState.value,
|
||||
batchNumber: batchNumberState.value,
|
||||
minterMessages,
|
||||
sg721Messages,
|
||||
@ -154,7 +166,8 @@ const CollectionActionsPage: NextPage = () => {
|
||||
{showWhitelistField && <AddressInput {...whitelistState} />}
|
||||
{showLimitField && <NumberInput {...limitState} />}
|
||||
{showTokenIdField && <NumberInput {...tokenIdState} />}
|
||||
{showTokenIdListField && <NumberInput {...batchNumberState} />}
|
||||
{showTokenIdListField && <TextInput {...tokenIdListState} />}
|
||||
{showNumberOfTokensField && <NumberInput {...batchNumberState} />}
|
||||
<Conditional test={showDateField}>
|
||||
<FormControl htmlId="start-date" subtitle="Start time for the minting" title="Start Time">
|
||||
<InputDateTime minDate={new Date()} onChange={(date) => setTimestamp(date)} value={timestamp} />
|
||||
|
Loading…
Reference in New Issue
Block a user