diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/BatchMetadata.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/BatchMetadata.tsx index 910d2a3af27..d2d24c6da12 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/BatchMetadata.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/BatchMetadata.tsx @@ -66,8 +66,6 @@ export type UploadMetadataFormValues = z.infer; function BatchMetadataModule(props: ModuleInstanceProps) { const { contract, ownerAccount } = props; - const isErc721 = props.contractInfo.name === "BatchMetadataERC721"; - const uploadMetadata = useCallback( async (values: UploadMetadataFormValues) => { if (!ownerAccount) { @@ -75,9 +73,10 @@ function BatchMetadataModule(props: ModuleInstanceProps) { } const nft = parseAttributes(values); - const uploadMetadata = isErc721 - ? BatchMetadataERC721.uploadMetadata - : BatchMetadataERC1155.uploadMetadata; + const uploadMetadata = + props.contractInfo.name === "BatchMetadataERC721" + ? BatchMetadataERC721.uploadMetadata + : BatchMetadataERC1155.uploadMetadata; const uploadMetadataTx = uploadMetadata({ contract, metadatas: [nft], @@ -88,7 +87,7 @@ function BatchMetadataModule(props: ModuleInstanceProps) { transaction: uploadMetadataTx, }); }, - [contract, ownerAccount, isErc721], + [contract, ownerAccount, props.contractInfo.name], ); return ( diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Claimable.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Claimable.tsx index 92e1a50c008..3fae3031aec 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Claimable.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Claimable.tsx @@ -46,7 +46,11 @@ import { toTokens, } from "thirdweb"; import { decimals } from "thirdweb/extensions/erc20"; -import { ClaimableERC721, ClaimableERC1155 } from "thirdweb/modules"; +import { + ClaimableERC20, + ClaimableERC721, + ClaimableERC1155, +} from "thirdweb/modules"; import { useActiveAccount, useReadContract } from "thirdweb/react"; import { z } from "zod"; import { addressSchema } from "../zod-schemas"; @@ -72,26 +76,34 @@ function ClaimableModule(props: ModuleInstanceProps) { const account = useActiveAccount(); const [tokenId, setTokenId] = useState(""); - const isErc721 = props.contractInfo.name === "ClaimableERC721"; - const isErc20 = props.contractInfo.name === "ClaimableERC20"; const isValidTokenId = positiveIntegerRegex.test(tokenId); const primarySaleRecipientQuery = useReadContract( - isErc721 ? ClaimableERC721.getSaleConfig : ClaimableERC1155.getSaleConfig, + props.contractInfo.name === "ClaimableERC721" + ? ClaimableERC721.getSaleConfig + : props.contractInfo.name === "ClaimableERC20" + ? ClaimableERC20.getSaleConfig + : ClaimableERC1155.getSaleConfig, { contract: contract, }, ); const claimConditionQuery = useReadContract( - isErc721 + props.contractInfo.name === "ClaimableERC721" ? ClaimableERC721.getClaimCondition - : ClaimableERC1155.getClaimCondition, + : props.contractInfo.name === "ClaimableERC20" + ? ClaimableERC20.getClaimCondition + : ClaimableERC1155.getClaimCondition, { tokenId: positiveIntegerRegex.test(tokenId) ? BigInt(tokenId) : 0n, contract: contract, queryOptions: { - enabled: isErc721 || (!!tokenId && isValidTokenId), + enabled: + ["ClaimableERC721", "ClaimableERC20"].includes( + props.contractInfo.name, + ) || + (!!tokenId && isValidTokenId), }, }, ); @@ -116,12 +128,18 @@ function ClaimableModule(props: ModuleInstanceProps) { claimConditionQuery.data && claimConditionQuery.data?.currency !== ZERO_ADDRESS; - const tokenDecimalsQuery = useReadContract(decimals, { + const currencyDecimalsQuery = useReadContract(decimals, { contract: currencyContract, queryOptions: { enabled: shouldFetchTokenDecimals, }, }); + const tokenDecimalsQuery = useReadContract(decimals, { + contract: contract, + queryOptions: { + enabled: props.contractInfo.name === "ClaimableERC20", + }, + }); const mint = useCallback( async (values: MintFormValues) => { @@ -130,12 +148,18 @@ function ClaimableModule(props: ModuleInstanceProps) { } let mintTx: PreparedTransaction; - if (isErc721) { + if (props.contractInfo.name === "ClaimableERC721") { mintTx = ClaimableERC721.mint({ contract, to: values.recipient, quantity: values.quantity, }); + } else if (props.contractInfo.name === "ClaimableERC20") { + mintTx = ClaimableERC20.mint({ + contract, + to: values.recipient, + quantity: values.quantity, + }); } else if (values.tokenId) { mintTx = ClaimableERC1155.mint({ contract, @@ -152,7 +176,7 @@ function ClaimableModule(props: ModuleInstanceProps) { transaction: mintTx, }); }, - [contract, account, isErc721], + [contract, account, props.contractInfo.name], ); const setClaimCondition = useCallback( @@ -162,7 +186,7 @@ function ClaimableModule(props: ModuleInstanceProps) { } let setClaimConditionTx: PreparedTransaction; - if (isErc721) { + if (props.contractInfo.name === "ClaimableERC721") { setClaimConditionTx = ClaimableERC721.setClaimCondition({ ...values, contract, @@ -171,6 +195,15 @@ function ClaimableModule(props: ModuleInstanceProps) { ? values.allowList.map(({ address }) => address) : undefined, }); + } else if (props.contractInfo.name === "ClaimableERC20") { + setClaimConditionTx = ClaimableERC20.setClaimCondition({ + ...values, + contract, + allowList: + values.allowList && values.allowList.length > 0 + ? values.allowList.map(({ address }) => address) + : undefined, + }); } else if (values.tokenId) { setClaimConditionTx = ClaimableERC1155.setClaimCondition({ ...values, @@ -190,7 +223,7 @@ function ClaimableModule(props: ModuleInstanceProps) { transaction: setClaimConditionTx, }); }, - [contract, ownerAccount, isErc721], + [contract, ownerAccount, props.contractInfo.name], ); const setPrimarySaleRecipient = useCallback( @@ -198,9 +231,12 @@ function ClaimableModule(props: ModuleInstanceProps) { if (!ownerAccount) { throw new Error("Not an owner account"); } - const setSaleConfig = isErc721 - ? ClaimableERC721.setSaleConfig - : ClaimableERC1155.setSaleConfig; + const setSaleConfig = + props.contractInfo.name === "ClaimableERC721" + ? ClaimableERC721.setSaleConfig + : props.contractInfo.name === "ClaimableERC20" + ? ClaimableERC20.setSaleConfig + : ClaimableERC1155.setSaleConfig; const setSaleConfigTx = setSaleConfig({ contract: contract, primarySaleRecipient: values.primarySaleRecipient, @@ -211,7 +247,7 @@ function ClaimableModule(props: ModuleInstanceProps) { transaction: setSaleConfigTx, }); }, - [contract, ownerAccount, isErc721], + [contract, ownerAccount, props.contractInfo.name], ); return ( @@ -228,9 +264,10 @@ function ClaimableModule(props: ModuleInstanceProps) { // claim conditions data is present claimConditionQuery.data && // token decimals is fetched if it should be fetched - (shouldFetchTokenDecimals ? tokenDecimalsQuery.isFetched : true) + (shouldFetchTokenDecimals ? currencyDecimalsQuery.isFetched : true) ? { claimCondition: claimConditionQuery.data, + currencyDecimals: currencyDecimalsQuery.data, tokenDecimals: tokenDecimalsQuery.data, } : undefined, @@ -238,11 +275,10 @@ function ClaimableModule(props: ModuleInstanceProps) { tokenId, isLoading: claimConditionQuery.isLoading || - (!!shouldFetchTokenDecimals && tokenDecimalsQuery.isLoading), + (!!shouldFetchTokenDecimals && currencyDecimalsQuery.isLoading), }} isOwnerAccount={!!ownerAccount} - isErc721={isErc721} - isErc20={isErc20} + name={props.contractInfo.name} contractChainId={props.contract.chain.id} setTokenId={setTokenId} isValidTokenId={isValidTokenId} @@ -257,8 +293,7 @@ function ClaimableModule(props: ModuleInstanceProps) { export function ClaimableModuleUI( props: Omit & { isOwnerAccount: boolean; - isErc721: boolean; - isErc20: boolean; + name: string; contractChainId: number; setTokenId: Dispatch>; isValidTokenId: boolean; @@ -282,6 +317,7 @@ export function ClaimableModuleUI( data: | { claimCondition: ClaimConditionValue; + currencyDecimals: number | undefined; tokenDecimals: number | undefined; } | undefined; @@ -298,12 +334,12 @@ export function ClaimableModuleUI( - Mint {props.isErc20 ? "Token" : "NFT"} + Mint {props.name === "ClaimableERC20" ? "Token" : "NFT"} @@ -314,7 +350,7 @@ export function ClaimableModuleUI( Claim Conditions - {!props.isErc721 && ( + {props.name === "ClaimableERC1155" && (
@@ -330,28 +366,29 @@ export function ClaimableModuleUI(
- {props.isValidTokenId && - props.claimConditionSection.data && - !props.claimConditionSection.isLoading && ( + {props.name !== "ClaimableERC1155" || props.isValidTokenId ? ( + props.claimConditionSection.data ? ( - )} - {props.isValidTokenId && - props.claimConditionSection.isLoading && ( + ) : ( - )} + ) + ) : null} @@ -423,9 +460,10 @@ function ClaimConditionSection(props: { claimCondition: ClaimConditionValue; update: (values: ClaimConditionFormValues) => Promise; isOwnerAccount: boolean; - isErc721: boolean; + name: string; chainId: number; - tokenDecimals?: number; + currencyDecimals?: number; + tokenDecimals?: number | undefined; tokenId: string; noClaimConditionSet: boolean; }) { @@ -445,18 +483,28 @@ function ClaimConditionSection(props: { : claimCondition?.currency, // default case is zero state, so 0 // 10 ** 18 still results in 0 pricePerToken: Number( - toTokens(claimCondition?.pricePerUnit, props.tokenDecimals || 18), + toTokens(claimCondition?.pricePerUnit, props.currencyDecimals || 18), ), maxClaimableSupply: claimCondition?.availableSupply.toString() === "0" || claimCondition?.availableSupply.toString() === MAX_UINT_256 ? "" - : claimCondition?.availableSupply.toString() || "", + : props.name === "ClaimableERC20" + ? toTokens( + claimCondition?.availableSupply, + props.tokenDecimals || 18, + ) + : claimCondition?.availableSupply.toString() || "", maxClaimablePerWallet: claimCondition?.maxMintPerWallet.toString() === "0" || claimCondition?.maxMintPerWallet.toString() === MAX_UINT_256 ? "" - : claimCondition?.maxMintPerWallet.toString() || "", + : props.name === "ClaimableERC20" + ? toTokens( + claimCondition?.maxMintPerWallet, + props.tokenDecimals || 18, + ) + : claimCondition?.maxMintPerWallet.toString() || "", startTime: claimCondition?.startTimestamp ? fromUnixTime(claimCondition?.startTimestamp) : defaultStartDate, @@ -487,7 +535,7 @@ function ClaimConditionSection(props: { const onSubmit = async () => { const values = form.getValues(); - if (!props.isErc721 && !values.tokenId) { + if (props.name === "ClaimableERC1155" && !values.tokenId) { form.setError("tokenId", { message: "Token ID is required" }); return; } @@ -782,7 +830,7 @@ export type MintFormValues = z.infer; function MintNFTSection(props: { mint: (values: MintFormValues) => Promise; - isErc721: boolean; + name: string; contractChainId: number; }) { const form = useForm({ @@ -808,7 +856,7 @@ function MintNFTSection(props: { const onSubmit = async () => { const values = form.getValues(); - if (!props.isErc721 && !values.tokenId) { + if (props.name === "ClaimableERC1155" && !values.tokenId) { form.setError("tokenId", { message: "Token ID is required" }); return; } @@ -847,7 +895,7 @@ function MintNFTSection(props: { )} /> - {!props.isErc721 && ( + {props.name === "ClaimableERC1155" && ( @@ -177,7 +191,7 @@ export function MintableModuleUI( isOwnerAccount: boolean; updatePrimaryRecipient: (values: UpdateFormValues) => Promise; mint: (values: MintFormValues) => Promise; - isErc721: boolean; + name: string; isBatchMetadataInstalled: boolean; contractChainId: number; }, @@ -199,7 +213,7 @@ export function MintableModuleUI( {props.isOwnerAccount && ( @@ -323,7 +337,7 @@ const mintFormSchema = z.object({ function MintNFTSection(props: { mint: (values: MintFormValues) => Promise; - isErc721: boolean; + name: string; isBatchMetadataInstalled: boolean; contractChainId: number; }) { @@ -353,7 +367,11 @@ function MintNFTSection(props: { const onSubmit = async () => { const values = form.getValues(); - if (!props.isErc721 && !values.useNextTokenId && !values.tokenId) { + if ( + props.name === "MintableERC1155" && + !values.useNextTokenId && + !values.tokenId + ) { form.setError("tokenId", { message: "Token ID is required" }); return; } @@ -453,7 +471,7 @@ function MintNFTSection(props: { )} /> - {!props.isErc721 && ( + {props.name !== "MintableERC721" && ( )} - {!props.isErc721 && ( + {props.name === "MintableERC1155" && (
; function TransferableModule(props: ModuleInstanceProps) { const { contract, ownerAccount } = props; - const isErc721 = props.contractInfo.name === "TransferableERC721"; - const isTransferEnabledQuery = useReadContract( - isErc721 + props.contractInfo.name === "TransferableERC721" ? TransferableERC721.isTransferEnabled - : TransferableERC1155.isTransferEnabled, + : props.contractInfo.name === "TransferableERC20" + ? TransferableERC20.isTransferEnabled + : TransferableERC1155.isTransferEnabled, { contract, }, @@ -64,9 +68,12 @@ function TransferableModule(props: ModuleInstanceProps) { const transferEnabled = !values.isRestricted; if (isTransferEnabledQuery.data !== transferEnabled) { - const setTransferable = isErc721 - ? TransferableERC721.setTransferable - : TransferableERC1155.setTransferable; + const setTransferable = + props.contractInfo.name === "TransferableERC721" + ? TransferableERC721.setTransferable + : props.contractInfo.name === "TransferableERC20" + ? TransferableERC20.setTransferable + : TransferableERC1155.setTransferable; const setTransferableTx = setTransferable({ contract, enableTransfer: transferEnabled, @@ -80,9 +87,12 @@ function TransferableModule(props: ModuleInstanceProps) { await Promise.all( values.allowList.map(async ({ address }) => { - const setTransferableFor = isErc721 - ? TransferableERC721.setTransferableFor - : TransferableERC1155.setTransferableFor; + const setTransferableFor = + props.contractInfo.name === "TransferableERC721" + ? TransferableERC721.setTransferableFor + : props.contractInfo.name === "TransferableERC20" + ? TransferableERC20.setTransferableFor + : TransferableERC1155.setTransferableFor; const setTransferableForTx = setTransferableFor({ contract, enableTransfer: true, @@ -96,7 +106,12 @@ function TransferableModule(props: ModuleInstanceProps) { }), ); }, - [contract, ownerAccount, isTransferEnabledQuery.data, isErc721], + [ + contract, + ownerAccount, + isTransferEnabledQuery.data, + props.contractInfo.name, + ], ); return ( diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/claimable.stories.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/claimable.stories.tsx index 069bb78c90a..e26f9a8b851 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/claimable.stories.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/claimable.stories.tsx @@ -1,5 +1,12 @@ import { ChakraProviderSetup } from "@/components/ChakraProviderSetup"; import { Checkbox } from "@/components/ui/checkbox"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import type { Meta, StoryObj } from "@storybook/react"; import { useMutation } from "@tanstack/react-query"; import { subDays } from "date-fns"; @@ -60,8 +67,7 @@ const claimCondition = { function Component() { const [isOwner, setIsOwner] = useState(true); - const [isErc721, setIsErc721] = useState(false); - const [isErc20, setIsErc20] = useState(false); + const [name, setName] = useState("ClaimableERC721"); const [isClaimConditionLoading, setIsClaimConditionLoading] = useState(false); const [isPrimarySaleRecipientLoading, setIsPrimarySaleRecipientLoading] = useState(false); @@ -119,19 +125,16 @@ function Component() { label="Is Owner" /> - - - + - - + +
@@ -118,7 +131,7 @@ function Component() { isPending: removeMutation.isPending, }} isOwnerAccount={isOwner} - isErc721={isErc721} + name={name} isBatchMetadataInstalled={isBatchMetadataInstalled} contractChainId={1} /> @@ -137,7 +150,7 @@ function Component() { isPending: removeMutation.isPending, }} isOwnerAccount={isOwner} - isErc721={isErc721} + name={name} isBatchMetadataInstalled={isBatchMetadataInstalled} contractChainId={1} /> @@ -156,7 +169,7 @@ function Component() { isPending: removeMutation.isPending, }} isOwnerAccount={isOwner} - isErc721={isErc721} + name={name} isBatchMetadataInstalled={isBatchMetadataInstalled} contractChainId={1} />