Add update_collection_info() to collection actions

This commit is contained in:
Serkan Reis 2022-10-24 12:09:43 +03:00
parent 7e42100567
commit 8a07a182e5
6 changed files with 190 additions and 9 deletions

View File

@ -22,6 +22,7 @@ import { FaArrowRight } from 'react-icons/fa'
import { useMutation } from 'react-query' import { useMutation } from 'react-query'
import type { AirdropAllocation } from 'utils/isValidAccountsFile' import type { AirdropAllocation } from 'utils/isValidAccountsFile'
import type { CollectionInfo } from '../../../contracts/sg721/contract'
import { TextInput } from '../../forms/FormInput' import { TextInput } from '../../forms/FormInput'
interface CollectionActionsProps { interface CollectionActionsProps {
@ -43,12 +44,14 @@ export const CollectionActions = ({
const [timestamp, setTimestamp] = useState<Date | undefined>(undefined) const [timestamp, setTimestamp] = useState<Date | undefined>(undefined)
const [airdropAllocationArray, setAirdropAllocationArray] = useState<AirdropAllocation[]>([]) const [airdropAllocationArray, setAirdropAllocationArray] = useState<AirdropAllocation[]>([])
const [airdropArray, setAirdropArray] = useState<string[]>([]) const [airdropArray, setAirdropArray] = useState<string[]>([])
const [collectionInfo, setCollectionInfo] = useState<CollectionInfo>()
const [explicitContent, setExplicitContent] = useState(false)
const actionComboboxState = useActionsComboboxState() const actionComboboxState = useActionsComboboxState()
const type = actionComboboxState.value?.id const type = actionComboboxState.value?.id
const limitState = useNumberInputState({ const limitState = useNumberInputState({
id: 'per-address-limi', id: 'per-address-limit',
name: 'perAddressLimit', name: 'perAddressLimit',
title: 'Per Address Limit', title: 'Per Address Limit',
subtitle: 'Enter the per address limit', subtitle: 'Enter the per address limit',
@ -97,6 +100,41 @@ export const CollectionActions = ({
subtitle: 'New minting price in STARS', subtitle: 'New minting price in STARS',
}) })
const descriptionState = useInputState({
id: 'collection-description',
name: 'description',
title: 'Collection Description',
})
const imageState = useInputState({
id: 'collection-cover-image',
name: 'cover_image',
title: 'Collection Cover Image',
subtitle: 'URL for collection cover image.',
})
const externalLinkState = useInputState({
id: 'collection-ext-link',
name: 'external_link',
title: 'External Link',
subtitle: 'External URL for the collection.',
})
const royaltyPaymentAddressState = useInputState({
id: 'royalty-payment-address',
name: 'royaltyPaymentAddress',
title: 'Royalty Payment Address',
subtitle: 'Address to receive royalties.',
})
const royaltyShareState = useInputState({
id: 'royalty-share',
name: 'royaltyShare',
title: 'Share Percentage',
subtitle: 'Percentage of royalties to be paid',
placeholder: '8%',
})
const showWhitelistField = type === 'set_whitelist' const showWhitelistField = type === 'set_whitelist'
const showDateField = isEitherType(type, ['update_start_time', 'update_start_trading_time']) const showDateField = isEitherType(type, ['update_start_time', 'update_start_trading_time'])
const showLimitField = type === 'update_per_address_limit' const showLimitField = type === 'update_per_address_limit'
@ -106,6 +144,11 @@ export const CollectionActions = ({
const showRecipientField = isEitherType(type, ['transfer', 'mint_to', 'mint_for', 'batch_mint', 'batch_transfer']) const showRecipientField = isEitherType(type, ['transfer', 'mint_to', 'mint_for', 'batch_mint', 'batch_transfer'])
const showAirdropFileField = type === 'airdrop' const showAirdropFileField = type === 'airdrop'
const showPriceField = type === 'update_mint_price' const showPriceField = type === 'update_mint_price'
const showDescriptionField = type === 'update_collection_info'
const showImageField = type === 'update_collection_info'
const showExternalLinkField = type === 'update_collection_info'
const showRoyaltyRelatedFields = type === 'update_collection_info'
const showExplicitContentField = type === 'update_collection_info'
const payload: DispatchExecuteArgs = { const payload: DispatchExecuteArgs = {
whitelist: whitelistState.value, whitelist: whitelistState.value,
@ -123,8 +166,32 @@ export const CollectionActions = ({
txSigner: wallet.address, txSigner: wallet.address,
type, type,
price: priceState.value.toString(), price: priceState.value.toString(),
collectionInfo,
} }
useEffect(() => {
setCollectionInfo({
description: descriptionState.value || undefined,
image: imageState.value || undefined,
explicit_content: explicitContent,
external_link: externalLinkState.value || undefined,
royalty_info:
royaltyPaymentAddressState.value && royaltyShareState.value
? {
payment_address: royaltyPaymentAddressState.value,
share: (Number(royaltyShareState.value) / 100).toString(),
}
: undefined,
})
}, [
descriptionState.value,
imageState.value,
explicitContent,
externalLinkState.value,
royaltyPaymentAddressState.value,
royaltyShareState.value,
])
useEffect(() => { useEffect(() => {
const addresses: string[] = [] const addresses: string[] = []
airdropAllocationArray.forEach((allocation) => { airdropAllocationArray.forEach((allocation) => {
@ -187,6 +254,62 @@ export const CollectionActions = ({
{showTokenIdListField && <TextInput {...tokenIdListState} />} {showTokenIdListField && <TextInput {...tokenIdListState} />}
{showNumberOfTokensField && <NumberInput {...batchNumberState} />} {showNumberOfTokensField && <NumberInput {...batchNumberState} />}
{showPriceField && <NumberInput {...priceState} />} {showPriceField && <NumberInput {...priceState} />}
{showDescriptionField && <TextInput className="mb-2" {...descriptionState} />}
{showImageField && <TextInput className="mb-2" {...imageState} />}
{showExternalLinkField && <TextInput className="mb-2" {...externalLinkState} />}
{showRoyaltyRelatedFields && (
<>
<TextInput className="mb-2" {...royaltyPaymentAddressState} />
<NumberInput className="mb-2" {...royaltyShareState} />
</>
)}
{showExplicitContentField && (
<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={explicitContent}
className="peer sr-only"
id="explicitRadio1"
name="explicitRadioOptions1"
onClick={() => {
setExplicitContent(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={!explicitContent}
className="peer sr-only"
id="explicitRadio2"
name="explicitRadioOptions2"
onClick={() => {
setExplicitContent(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>
)}
{showAirdropFileField && ( {showAirdropFileField && (
<FormGroup <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" 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"

View File

@ -1,6 +1,6 @@
import type { MinterInstance } from 'contracts/minter' import type { MinterInstance } from 'contracts/minter'
import { useMinterContract } from 'contracts/minter' import { useMinterContract } from 'contracts/minter'
import type { SG721Instance } from 'contracts/sg721' import type { CollectionInfo, SG721Instance } from 'contracts/sg721'
import { useSG721Contract } from 'contracts/sg721' import { useSG721Contract } from 'contracts/sg721'
export type ActionType = typeof ACTION_TYPES[number] export type ActionType = typeof ACTION_TYPES[number]
@ -16,6 +16,7 @@ export const ACTION_TYPES = [
'update_start_time', 'update_start_time',
'update_start_trading_time', 'update_start_trading_time',
'update_per_address_limit', 'update_per_address_limit',
'update_collection_info',
'withdraw', 'withdraw',
'transfer', 'transfer',
'batch_transfer', 'batch_transfer',
@ -83,6 +84,11 @@ export const ACTION_LIST: ActionListItem[] = [
name: 'Update Tokens Per Address Limit', name: 'Update Tokens Per Address Limit',
description: `Update token per address limit`, description: `Update token per address limit`,
}, },
{
id: 'update_collection_info',
name: 'Update Collection Info',
description: `Update Collection Info`,
},
{ {
id: 'withdraw', id: 'withdraw',
name: 'Withdraw Tokens', name: 'Withdraw Tokens',
@ -159,6 +165,7 @@ export type DispatchExecuteArgs = {
| { type: Select<'batch_burn'>; tokenIds: string } | { type: Select<'batch_burn'>; tokenIds: string }
| { type: Select<'airdrop'>; recipients: string[] } | { type: Select<'airdrop'>; recipients: string[] }
| { type: Select<'burn_remaining'> } | { type: Select<'burn_remaining'> }
| { type: Select<'update_collection_info'>; collectionInfo: CollectionInfo | undefined }
) )
export const dispatchExecute = async (args: DispatchExecuteArgs) => { export const dispatchExecute = async (args: DispatchExecuteArgs) => {
@ -197,6 +204,9 @@ export const dispatchExecute = async (args: DispatchExecuteArgs) => {
case 'update_per_address_limit': { case 'update_per_address_limit': {
return minterMessages.updatePerAddressLimit(txSigner, args.limit) return minterMessages.updatePerAddressLimit(txSigner, args.limit)
} }
case 'update_collection_info': {
return sg721Messages.updateCollectionInfo(args.collectionInfo as CollectionInfo)
}
case 'shuffle': { case 'shuffle': {
return minterMessages.shuffle(txSigner) return minterMessages.shuffle(txSigner)
} }
@ -264,6 +274,9 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => {
case 'update_per_address_limit': { case 'update_per_address_limit': {
return minterMessages(minterContract)?.updatePerAddressLimit(args.limit) return minterMessages(minterContract)?.updatePerAddressLimit(args.limit)
} }
case 'update_collection_info': {
return sg721Messages(sg721Contract)?.updateCollectionInfo(args.collectionInfo as CollectionInfo)
}
case 'shuffle': { case 'shuffle': {
return minterMessages(minterContract)?.shuffle() return minterMessages(minterContract)?.shuffle()
} }

View File

@ -107,10 +107,6 @@ export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl }: Col
reader.readAsArrayBuffer(event.target.files[0]) reader.readAsArrayBuffer(event.target.files[0])
} }
useEffect(() => {
console.log(explicit)
}, [explicit])
return ( return (
<div> <div>
<FormGroup subtitle="Information about your collection" title="Collection Details"> <FormGroup subtitle="Information about your collection" title="Collection Details">

View File

@ -12,7 +12,7 @@ export interface InstantiateResponse {
readonly logs: readonly logs.Log[] readonly logs: readonly logs.Log[]
} }
export interface RoyalityInfo { export interface RoyaltyInfo {
payment_address: string payment_address: string
share: string share: string
} }

View File

@ -4,6 +4,8 @@ import type { Coin } from '@cosmjs/stargate'
import { coin } from '@cosmjs/stargate' import { coin } from '@cosmjs/stargate'
import { MsgExecuteContract } from 'cosmjs-types/cosmwasm/wasm/v1/tx' import { MsgExecuteContract } from 'cosmjs-types/cosmwasm/wasm/v1/tx'
import type { RoyaltyInfo } from '../minter/contract'
export interface InstantiateResponse { export interface InstantiateResponse {
readonly contractAddress: string readonly contractAddress: string
readonly transactionHash: string readonly transactionHash: string
@ -11,6 +13,14 @@ export interface InstantiateResponse {
export type Expiration = { at_height: number } | { at_time: string } | { never: Record<string, never> } export type Expiration = { at_height: number } | { at_time: string } | { never: Record<string, never> }
export interface CollectionInfo {
description?: string
image?: string
external_link?: string
explicit_content?: boolean
royalty_info?: RoyaltyInfo | undefined
}
export interface SG721Instance { export interface SG721Instance {
readonly contractAddress: string readonly contractAddress: string
@ -65,7 +75,7 @@ export interface SG721Instance {
revokeAll: (operator: string) => Promise<string> revokeAll: (operator: string) => Promise<string>
/// Mint a new NFT, can only be called by the contract minter /// Mint a new NFT, can only be called by the contract minter
mint: (tokenId: string, owner: string, tokenURI?: string) => Promise<string> //MintMsg<T> mint: (tokenId: string, owner: string, tokenURI?: string) => Promise<string> //MintMsg<T>
updateCollectionInfo: (collectionInfo: CollectionInfo) => Promise<string>
/// Burn an NFT the sender has access to /// Burn an NFT the sender has access to
burn: (tokenId: string) => Promise<string> burn: (tokenId: string) => Promise<string>
batchBurn: (tokenIds: string) => Promise<string> batchBurn: (tokenIds: string) => Promise<string>
@ -83,6 +93,7 @@ export interface Sg721Messages {
burn: (tokenId: string) => BurnMessage burn: (tokenId: string) => BurnMessage
batchBurn: (tokenIds: string) => BatchBurnMessage batchBurn: (tokenIds: string) => BatchBurnMessage
batchTransfer: (recipient: string, tokenIds: string) => BatchTransferMessage batchTransfer: (recipient: string, tokenIds: string) => BatchTransferMessage
updateCollectionInfo: (collectionInfo: CollectionInfo) => UpdateCollectionInfoMessage
} }
export interface TransferNFTMessage { export interface TransferNFTMessage {
@ -197,6 +208,17 @@ export interface BatchTransferMessage {
funds: Coin[] funds: Coin[]
} }
export interface UpdateCollectionInfoMessage {
sender: string
contract: string
msg: {
update_collection_info: {
collection_info: CollectionInfo
}
}
funds: Coin[]
}
export interface SG721Contract { export interface SG721Contract {
instantiate: ( instantiate: (
senderAddress: string, senderAddress: string,
@ -513,6 +535,21 @@ export const SG721 = (client: SigningCosmWasmClient, txSigner: string): SG721Con
return res.transactionHash return res.transactionHash
} }
// eslint-disable-next-line @typescript-eslint/no-shadow
const updateCollectionInfo = async (collectionInfo: CollectionInfo): Promise<string> => {
console.log(collectionInfo)
const res = await client.execute(
txSigner,
contractAddress,
{
update_collection_info: { collection_info: collectionInfo },
},
'auto',
'',
)
return res.transactionHash
}
return { return {
contractAddress, contractAddress,
ownerOf, ownerOf,
@ -537,6 +574,7 @@ export const SG721 = (client: SigningCosmWasmClient, txSigner: string): SG721Con
burn, burn,
batchBurn, batchBurn,
batchTransfer, batchTransfer,
updateCollectionInfo,
} }
} }
@ -719,6 +757,16 @@ export const SG721 = (client: SigningCosmWasmClient, txSigner: string): SG721Con
funds: [], funds: [],
} }
} }
const updateCollectionInfo = (collectionInfo: CollectionInfo) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
update_collection_info: { collection_info: collectionInfo },
},
funds: [],
}
}
return { return {
transferNft, transferNft,
@ -731,6 +779,7 @@ export const SG721 = (client: SigningCosmWasmClient, txSigner: string): SG721Con
burn, burn,
batchBurn, batchBurn,
batchTransfer, batchTransfer,
updateCollectionInfo,
} }
} }

View File

@ -37,7 +37,7 @@ const MinterExecutePage: NextPage = () => {
const type = comboboxState.value?.id const type = comboboxState.value?.id
const limitState = useNumberInputState({ const limitState = useNumberInputState({
id: 'per-address-limi', id: 'per-address-limit',
name: 'perAddressLimit', name: 'perAddressLimit',
title: 'Per Address Limit', title: 'Per Address Limit',
subtitle: 'Enter the per address limit', subtitle: 'Enter the per address limit',