import { AirdropUpload } from 'components/AirdropUpload' import { Button } from 'components/Button' import type { DispatchExecuteArgs } from 'components/collections/actions/actions' import { dispatchExecute, isEitherType, previewExecutePayload } from 'components/collections/actions/actions' import { ActionsCombobox } from 'components/collections/actions/Combobox' import { useActionsComboboxState } from 'components/collections/actions/Combobox.hooks' import { Conditional } from 'components/Conditional' import { FormControl } from 'components/FormControl' import { FormGroup } from 'components/FormGroup' import { AddressInput, NumberInput } from 'components/forms/FormInput' import { useInputState, useNumberInputState } from 'components/forms/FormInput.hooks' import { InputDateTime } from 'components/InputDateTime' import { JsonPreview } from 'components/JsonPreview' import { TransactionHash } from 'components/TransactionHash' import { useWallet } from 'contexts/wallet' import type { SG721Instance } from 'contracts/sg721' import type { VendingMinterInstance } from 'contracts/vendingMinter' import type { FormEvent } from 'react' import { useEffect, useState } from 'react' import { toast } from 'react-hot-toast' import { FaArrowRight } from 'react-icons/fa' import { useMutation } from 'react-query' import type { AirdropAllocation } from 'utils/isValidAccountsFile' import type { CollectionInfo } from '../../../contracts/sg721/contract' import { TextInput } from '../../forms/FormInput' interface CollectionActionsProps { minterContractAddress: string sg721ContractAddress: string sg721Messages: SG721Instance | undefined vendingMinterMessages: VendingMinterInstance | undefined } type ExplicitContentType = true | false | undefined export const CollectionActions = ({ sg721ContractAddress, sg721Messages, minterContractAddress, vendingMinterMessages, }: CollectionActionsProps) => { const wallet = useWallet() const [lastTx, setLastTx] = useState('') const [timestamp, setTimestamp] = useState(undefined) const [airdropAllocationArray, setAirdropAllocationArray] = useState([]) const [airdropArray, setAirdropArray] = useState([]) const [collectionInfo, setCollectionInfo] = useState() const [explicitContent, setExplicitContent] = useState(undefined) const actionComboboxState = useActionsComboboxState() const type = actionComboboxState.value?.id const limitState = useNumberInputState({ id: 'per-address-limit', name: 'perAddressLimit', title: 'Per Address Limit', subtitle: 'Enter the per address limit', }) const tokenIdState = useNumberInputState({ id: 'token-id', name: 'tokenId', title: 'Token ID', subtitle: 'Enter the token ID', }) const batchNumberState = useNumberInputState({ id: 'batch-number', name: 'batchNumber', title: 'Number of Tokens', subtitle: 'Enter the number of tokens to mint', }) const tokenIdListState = useInputState({ id: 'token-id-list', name: 'tokenIdList', title: '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', title: 'Recipient Address', subtitle: 'Address of the recipient', }) const whitelistState = useInputState({ id: 'whitelist-address', name: 'whitelistAddress', title: 'Whitelist Address', 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 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.', placeholder: 'stars1234567890abcdefghijklmnopqrstuvwxyz...', }) 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 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', 'batch_mint_for']) const showRecipientField = isEitherType(type, [ 'transfer', 'mint_to', 'mint_for', 'batch_mint', 'batch_transfer', 'batch_mint_for', ]) const showAirdropFileField = type === 'airdrop' 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 = { whitelist: whitelistState.value, startTime: timestamp ? (timestamp.getTime() * 1_000_000).toString() : '', limit: limitState.value, minterContract: minterContractAddress, sg721Contract: sg721ContractAddress, tokenId: tokenIdState.value, tokenIds: tokenIdListState.value, batchNumber: batchNumberState.value, vendingMinterMessages, sg721Messages, recipient: recipientState.value, recipients: airdropArray, txSigner: wallet.address, type, 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(() => { const addresses: string[] = [] airdropAllocationArray.forEach((allocation) => { for (let i = 0; i < Number(allocation.amount); i++) { addresses.push(allocation.address) } }) //shuffle the addresses array for (let i = addresses.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)) ;[addresses[i], addresses[j]] = [addresses[j], addresses[i]] } setAirdropArray(addresses) }, [airdropAllocationArray]) const { isLoading, mutate } = useMutation( async (event: FormEvent) => { event.preventDefault() if (!type) { throw new Error('Please select an action!') } if (minterContractAddress === '' && sg721ContractAddress === '') { throw new Error('Please enter minter and sg721 contract addresses!') } if (type === 'update_mint_price' && priceState.value < 50) { throw new Error('Mint price must be at least 50 STARS') } if ( type === 'update_collection_info' && (royaltyShareState.value ? !royaltyPaymentAddressState.value : royaltyPaymentAddressState.value) ) { throw new Error('Royalty payment address and share percentage are both required') } const txHash = await toast.promise(dispatchExecute(payload), { error: `${type.charAt(0).toUpperCase() + type.slice(1)} execute failed!`, loading: 'Executing message...', success: (tx) => `Transaction ${tx} success!`, }) if (txHash) { setLastTx(txHash) } }, { onError: (error) => { toast.error(String(error)) }, }, ) const airdropFileOnChange = (data: AirdropAllocation[]) => { setAirdropAllocationArray(data) } return (
{showRecipientField && } {showWhitelistField && } {showLimitField && } {showTokenIdField && } {showTokenIdListField && } {showNumberOfTokensField && } {showPriceField && } {showDescriptionField && } {showImageField && } {showExternalLinkField && } {showRoyaltyRelatedFields && (
)} {showExplicitContentField && (
Does the collection contain explicit content?
{ setExplicitContent(true) }} type="radio" />
{ setExplicitContent(false) }} type="radio" />
)} {showAirdropFileField && ( )} setTimestamp(date)} value={timestamp} />
) }