[Update] Refactor NFT reveal functionality and batch lazy minting (#4413)
This commit is contained in:
@@ -1,11 +1,3 @@
|
||||
import {
|
||||
type Abi,
|
||||
type AddContractInput,
|
||||
type FeatureName,
|
||||
type ValidContractInstance,
|
||||
detectFeatures as detectFeaturesFromSdk,
|
||||
isExtensionEnabled,
|
||||
} from "@thirdweb-dev/sdk";
|
||||
import { MULTICHAIN_REGISTRY_CONTRACT } from "constants/contracts";
|
||||
import {
|
||||
DASHBOARD_ENGINE_RELAYER_URL,
|
||||
@@ -14,30 +6,14 @@ import {
|
||||
import { prepareContractCall, sendAndConfirmTransaction } from "thirdweb";
|
||||
import type { Account } from "thirdweb/wallets";
|
||||
|
||||
export function detectFeatures<TContract extends ValidContractInstance | null>(
|
||||
contract: ValidContractInstance | null | undefined,
|
||||
features: FeatureName[],
|
||||
strategy: "any" | "all" = "any",
|
||||
): contract is TContract {
|
||||
if (!contract) {
|
||||
return false;
|
||||
}
|
||||
if (!("abi" in contract)) {
|
||||
return false;
|
||||
}
|
||||
type ContractInput = {
|
||||
address: string;
|
||||
chainId: number;
|
||||
};
|
||||
|
||||
const extensions = detectFeaturesFromSdk(contract.abi as Abi);
|
||||
|
||||
if (strategy === "any") {
|
||||
return features.some((feature) =>
|
||||
isExtensionEnabled(contract.abi as Abi, feature, extensions),
|
||||
);
|
||||
}
|
||||
|
||||
return features.every((feature) =>
|
||||
isExtensionEnabled(contract.abi as Abi, feature, extensions),
|
||||
);
|
||||
}
|
||||
type AddContractInput = ContractInput & {
|
||||
metadataURI?: string;
|
||||
};
|
||||
|
||||
export async function addContractToMultiChainRegistry(
|
||||
contractData: AddContractInput,
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Tooltip } from "@chakra-ui/react";
|
||||
import {
|
||||
useAccountsForAddress,
|
||||
useContract,
|
||||
useCreateAccount,
|
||||
useIsAccountDeployed,
|
||||
} from "@thirdweb-dev/react";
|
||||
import { TransactionButton } from "components/buttons/TransactionButton";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import { useActiveAccount } from "thirdweb/react";
|
||||
import * as ERC4337Ext from "thirdweb/extensions/erc4337";
|
||||
import {
|
||||
useActiveAccount,
|
||||
useReadContract,
|
||||
useSendAndConfirmTransaction,
|
||||
} from "thirdweb/react";
|
||||
import { Button, Card, Text } from "tw-components";
|
||||
|
||||
interface CreateAccountButtonProps {
|
||||
@@ -18,26 +17,35 @@ export const CreateAccountButton: React.FC<CreateAccountButtonProps> = ({
|
||||
contract,
|
||||
...restButtonProps
|
||||
}) => {
|
||||
const contractQuery = useContract(contract.address);
|
||||
const { mutate: createAccount, isLoading } = useCreateAccount(
|
||||
contractQuery?.contract,
|
||||
);
|
||||
const sendTxMutation = useSendAndConfirmTransaction();
|
||||
|
||||
const address = useActiveAccount()?.address;
|
||||
const { data: isAccountDeployed } = useIsAccountDeployed(
|
||||
contractQuery.contract,
|
||||
address,
|
||||
);
|
||||
const { data: accountsForAddress } = useAccountsForAddress(
|
||||
contractQuery.contract,
|
||||
address,
|
||||
|
||||
const isAccountDeployedQuery = useReadContract(ERC4337Ext.isAccountDeployed, {
|
||||
contract,
|
||||
adminSigner: address || "",
|
||||
data: "0x",
|
||||
queryOptions: {
|
||||
enabled: !!address,
|
||||
},
|
||||
});
|
||||
|
||||
const accountsForAddressQuery = useReadContract(
|
||||
ERC4337Ext.getAccountsOfSigner,
|
||||
{
|
||||
contract,
|
||||
signer: address || "",
|
||||
queryOptions: {
|
||||
enabled: !!address,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!contractQuery.contract || !address) {
|
||||
if (!address) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isAccountDeployed && accountsForAddress?.length) {
|
||||
if (isAccountDeployedQuery.data && accountsForAddressQuery.data?.length) {
|
||||
return (
|
||||
<Tooltip
|
||||
label={
|
||||
@@ -62,10 +70,17 @@ export const CreateAccountButton: React.FC<CreateAccountButtonProps> = ({
|
||||
return (
|
||||
<TransactionButton
|
||||
colorScheme="primary"
|
||||
onClick={() => createAccount(address)}
|
||||
isLoading={isLoading}
|
||||
onClick={() => {
|
||||
const tx = ERC4337Ext.createAccount({
|
||||
contract,
|
||||
admin: address,
|
||||
data: "0x",
|
||||
});
|
||||
sendTxMutation.mutate(tx);
|
||||
}}
|
||||
isLoading={sendTxMutation.isPending}
|
||||
transactionCount={1}
|
||||
isDisabled={isAccountDeployed}
|
||||
isDisabled={isAccountDeployedQuery.data}
|
||||
{...restButtonProps}
|
||||
>
|
||||
Create Account
|
||||
|
||||
+1
-2
@@ -621,8 +621,7 @@ export const ClaimConditionsForm: React.FC<ClaimConditionsFormProps> = ({
|
||||
{controlledFields.some((field) => field.fromSdk) && (
|
||||
<ResetClaimEligibility
|
||||
isErc20={isErc20}
|
||||
contract={contract}
|
||||
isColumn={isColumn}
|
||||
contract={contractV5}
|
||||
tokenId={tokenId}
|
||||
/>
|
||||
)}
|
||||
|
||||
+33
-23
@@ -1,23 +1,19 @@
|
||||
import { ToolTipLabel } from "@/components/ui/tooltip";
|
||||
import { thirdwebClient } from "@/constants/client";
|
||||
import { AdminOnly } from "@3rdweb-sdk/react/components/roles/admin-only";
|
||||
import { Box } from "@chakra-ui/react";
|
||||
import {
|
||||
type DropContract,
|
||||
useResetClaimConditions,
|
||||
} from "@thirdweb-dev/react";
|
||||
import { TransactionButton } from "components/buttons/TransactionButton";
|
||||
import { useTrack } from "hooks/analytics/useTrack";
|
||||
import { useTxNotifications } from "hooks/useTxNotifications";
|
||||
import { useV5DashboardChain } from "lib/v5-adapter";
|
||||
import { CircleHelpIcon } from "lucide-react";
|
||||
import { getContract } from "thirdweb";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import * as ERC20Ext from "thirdweb/extensions/erc20";
|
||||
import * as ERC721Ext from "thirdweb/extensions/erc721";
|
||||
import * as ERC1155Ext from "thirdweb/extensions/erc1155";
|
||||
import { useSendAndConfirmTransaction } from "thirdweb/react";
|
||||
|
||||
interface ResetClaimEligibilityProps {
|
||||
isErc20: boolean;
|
||||
contract: DropContract;
|
||||
contract: ThirdwebContract;
|
||||
tokenId?: string;
|
||||
isColumn?: true;
|
||||
}
|
||||
|
||||
export const ResetClaimEligibility: React.FC<ResetClaimEligibilityProps> = ({
|
||||
@@ -26,7 +22,9 @@ export const ResetClaimEligibility: React.FC<ResetClaimEligibilityProps> = ({
|
||||
isErc20,
|
||||
}) => {
|
||||
const trackEvent = useTrack();
|
||||
const resetClaimConditions = useResetClaimConditions(contract, tokenId);
|
||||
|
||||
const sendTxMutation = useSendAndConfirmTransaction();
|
||||
|
||||
const txNotification = useTxNotifications(
|
||||
"Successfully reset claim eligibility",
|
||||
"Failed to reset claim eligibility",
|
||||
@@ -41,7 +39,27 @@ export const ResetClaimEligibility: React.FC<ResetClaimEligibilityProps> = ({
|
||||
label: "attempt",
|
||||
});
|
||||
|
||||
resetClaimConditions.mutate(undefined, {
|
||||
const tx = (() => {
|
||||
switch (true) {
|
||||
// erc 20
|
||||
case isErc20: {
|
||||
return ERC20Ext.resetClaimEligibility({ contract });
|
||||
}
|
||||
// erc 1155
|
||||
case tokenId !== undefined: {
|
||||
return ERC1155Ext.resetClaimEligibility({
|
||||
contract,
|
||||
tokenId: BigInt(tokenId),
|
||||
});
|
||||
}
|
||||
// assume erc 721
|
||||
default: {
|
||||
return ERC721Ext.resetClaimEligibility({ contract });
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
sendTxMutation.mutate(tx, {
|
||||
onSuccess: () => {
|
||||
txNotification.onSuccess();
|
||||
trackEvent({
|
||||
@@ -62,27 +80,19 @@ export const ResetClaimEligibility: React.FC<ResetClaimEligibilityProps> = ({
|
||||
});
|
||||
};
|
||||
|
||||
const chain = useV5DashboardChain(contract?.chainId);
|
||||
|
||||
if (!contract || !chain) {
|
||||
if (!contract) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const contractV5 = getContract({
|
||||
address: contract.getAddress(),
|
||||
chain,
|
||||
client: thirdwebClient,
|
||||
});
|
||||
|
||||
return (
|
||||
<AdminOnly contract={contractV5} fallback={<Box pb={5} />}>
|
||||
<AdminOnly contract={contract} fallback={<div className="pb-5" />}>
|
||||
<TransactionButton
|
||||
colorScheme="secondary"
|
||||
bg="bgBlack"
|
||||
color="bgWhite"
|
||||
transactionCount={1}
|
||||
type="button"
|
||||
isLoading={resetClaimConditions.isLoading}
|
||||
isLoading={sendTxMutation.isPending}
|
||||
onClick={handleResetClaimEligibility}
|
||||
loadingText="Resetting..."
|
||||
size="sm"
|
||||
|
||||
@@ -1,63 +1,37 @@
|
||||
import { MinterOnly } from "@3rdweb-sdk/react/components/roles/minter-only";
|
||||
import { Icon, useDisclosure } from "@chakra-ui/react";
|
||||
import {
|
||||
type RevealableContract,
|
||||
type useContract,
|
||||
useDelayedRevealLazyMint,
|
||||
useLazyMint,
|
||||
} from "@thirdweb-dev/react";
|
||||
import { BatchLazyMint } from "core-ui/batch-upload/batch-lazy-mint";
|
||||
import {
|
||||
ProgressBox,
|
||||
type UploadProgressEvent,
|
||||
} from "core-ui/batch-upload/progress-box";
|
||||
import { useTrack } from "hooks/analytics/useTrack";
|
||||
import { useTxNotifications } from "hooks/useTxNotifications";
|
||||
import { useState } from "react";
|
||||
import { RiCheckboxMultipleBlankLine } from "react-icons/ri";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import { nextTokenIdToMint } from "thirdweb/extensions/erc721";
|
||||
import { useReadContract } from "thirdweb/react";
|
||||
import * as ERC721Ext from "thirdweb/extensions/erc721";
|
||||
import * as ERC1155Ext from "thirdweb/extensions/erc1155";
|
||||
import { useReadContract, useSendAndConfirmTransaction } from "thirdweb/react";
|
||||
import { Button, Drawer } from "tw-components";
|
||||
|
||||
interface BatchLazyMintButtonProps {
|
||||
contractQuery: ReturnType<typeof useContract>;
|
||||
isRevealable: boolean;
|
||||
contract: ThirdwebContract;
|
||||
isErc721: boolean;
|
||||
}
|
||||
|
||||
export const BatchLazyMintButton: React.FC<BatchLazyMintButtonProps> = ({
|
||||
contractQuery,
|
||||
contract,
|
||||
isRevealable,
|
||||
isErc721,
|
||||
}) => {
|
||||
const contractV4 = contractQuery.contract;
|
||||
const trackEvent = useTrack();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
// Both ERC721 and ERC1155 have the same function signature for nextTokenIdToMint,
|
||||
// so we can just use either one.
|
||||
const nextTokenIdToMintQuery = useReadContract(nextTokenIdToMint, {
|
||||
contract,
|
||||
});
|
||||
const [progress, setProgress] = useState<UploadProgressEvent>({
|
||||
progress: 0,
|
||||
total: 100,
|
||||
});
|
||||
|
||||
const mintBatchMutation = useLazyMint(
|
||||
contractV4,
|
||||
(event: UploadProgressEvent) => {
|
||||
setProgress(event);
|
||||
const nextTokenIdToMintQuery = useReadContract(
|
||||
isErc721 ? ERC721Ext.nextTokenIdToMint : ERC1155Ext.nextTokenIdToMint,
|
||||
{
|
||||
contract,
|
||||
},
|
||||
);
|
||||
|
||||
const mintDelayedRevealBatchMutation = useDelayedRevealLazyMint(
|
||||
contractV4 as RevealableContract,
|
||||
(event: UploadProgressEvent) => {
|
||||
setProgress(event);
|
||||
},
|
||||
);
|
||||
const sendTxMutation = useSendAndConfirmTransaction();
|
||||
|
||||
const txNotifications = useTxNotifications(
|
||||
"Batch uploaded successfully",
|
||||
@@ -87,17 +61,38 @@ export const BatchLazyMintButton: React.FC<BatchLazyMintButtonProps> = ({
|
||||
label: "attempt",
|
||||
});
|
||||
try {
|
||||
if (revealType === "instant") {
|
||||
// instant reveal
|
||||
await mintBatchMutation.mutateAsync(data);
|
||||
} else {
|
||||
// otherwise it's delayed reveal
|
||||
await mintDelayedRevealBatchMutation.mutateAsync({
|
||||
metadatas: data.metadata,
|
||||
placeholder: data.placeholderMetadata,
|
||||
password: data.password,
|
||||
});
|
||||
}
|
||||
const tx = (() => {
|
||||
switch (true) {
|
||||
// lazy mint erc721
|
||||
case revealType === "instant" && isErc721: {
|
||||
return ERC721Ext.lazyMint({
|
||||
contract,
|
||||
nfts: data.metadatas,
|
||||
});
|
||||
}
|
||||
// lazy mint erc1155
|
||||
case revealType === "instant" && !isErc721: {
|
||||
return ERC1155Ext.lazyMint({
|
||||
contract,
|
||||
nfts: data.metadatas,
|
||||
});
|
||||
}
|
||||
// delayed reveal erc721
|
||||
case revealType === "delayed": {
|
||||
return ERC721Ext.createDelayedRevealBatch({
|
||||
contract,
|
||||
metadata: data.metadata,
|
||||
password: data.password,
|
||||
placeholderMetadata: data.placeholderMetadata,
|
||||
});
|
||||
}
|
||||
default: {
|
||||
throw new Error("Invalid reveal type");
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
await sendTxMutation.mutateAsync(tx);
|
||||
|
||||
trackEvent({
|
||||
category: "nft",
|
||||
@@ -114,20 +109,11 @@ export const BatchLazyMintButton: React.FC<BatchLazyMintButtonProps> = ({
|
||||
error,
|
||||
});
|
||||
txNotifications.onError(error);
|
||||
} finally {
|
||||
setProgress({
|
||||
progress: 0,
|
||||
total: 100,
|
||||
});
|
||||
}
|
||||
}}
|
||||
nextTokenIdToMint={nextTokenIdToMintQuery.data || 0n}
|
||||
isRevealable={isRevealable}
|
||||
>
|
||||
{mintBatchMutation.isLoading ? (
|
||||
<ProgressBox progress={progress} />
|
||||
) : null}
|
||||
</BatchLazyMint>
|
||||
/>
|
||||
</Drawer>
|
||||
<Button
|
||||
colorScheme="primary"
|
||||
|
||||
@@ -1,37 +1,24 @@
|
||||
import { thirdwebClient } from "@/constants/client";
|
||||
import { MinterOnly } from "@3rdweb-sdk/react/components/roles/minter-only";
|
||||
import { Icon, useDisclosure } from "@chakra-ui/react";
|
||||
import { useBatchesToReveal, type useContract } from "@thirdweb-dev/react";
|
||||
import { useV5DashboardChain } from "lib/v5-adapter";
|
||||
import { FiEye } from "react-icons/fi";
|
||||
import { getContract } from "thirdweb";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import { getBatchesToReveal } from "thirdweb/extensions/erc721";
|
||||
import { useReadContract } from "thirdweb/react";
|
||||
import { Button, Drawer } from "tw-components";
|
||||
import { NFTRevealForm } from "./reveal-form";
|
||||
|
||||
interface NFTRevealButtonProps {
|
||||
contractQuery: ReturnType<typeof useContract>;
|
||||
contract: ThirdwebContract;
|
||||
}
|
||||
|
||||
export const NFTRevealButton: React.FC<NFTRevealButtonProps> = ({
|
||||
contractQuery,
|
||||
...restButtonProps
|
||||
contract,
|
||||
}) => {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const contractV4 = contractQuery.contract;
|
||||
const chain = useV5DashboardChain(contractV4?.chainId);
|
||||
const contract =
|
||||
contractV4 && chain
|
||||
? getContract({
|
||||
address: contractV4.getAddress(),
|
||||
chain: chain,
|
||||
client: thirdwebClient,
|
||||
})
|
||||
: null;
|
||||
const { data: batchesToReveal } = useBatchesToReveal(contractV4);
|
||||
if (!contract || !contractV4) {
|
||||
return null;
|
||||
}
|
||||
return batchesToReveal?.length ? (
|
||||
const batchesQuery = useReadContract(getBatchesToReveal, {
|
||||
contract,
|
||||
});
|
||||
return batchesQuery.data?.length ? (
|
||||
<MinterOnly contract={contract}>
|
||||
<Drawer
|
||||
allowPinchZoom
|
||||
@@ -40,12 +27,14 @@ export const NFTRevealButton: React.FC<NFTRevealButtonProps> = ({
|
||||
onClose={onClose}
|
||||
isOpen={isOpen}
|
||||
>
|
||||
<NFTRevealForm contract={contractV4} />
|
||||
<NFTRevealForm
|
||||
contract={contract}
|
||||
batchesToReveal={batchesQuery.data}
|
||||
/>
|
||||
</Drawer>
|
||||
<Button
|
||||
colorScheme="primary"
|
||||
leftIcon={<Icon as={FiEye} />}
|
||||
{...restButtonProps}
|
||||
onClick={onOpen}
|
||||
>
|
||||
Reveal NFTs
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { thirdwebClient } from "@/constants/client";
|
||||
import {
|
||||
DrawerBody,
|
||||
DrawerFooter,
|
||||
@@ -9,51 +8,41 @@ import {
|
||||
Stack,
|
||||
useModalContext,
|
||||
} from "@chakra-ui/react";
|
||||
import {
|
||||
type NFTContract,
|
||||
type RevealableContract,
|
||||
useBatchesToReveal,
|
||||
useRevealLazyMint,
|
||||
} from "@thirdweb-dev/react";
|
||||
import { TransactionButton } from "components/buttons/TransactionButton";
|
||||
import { useTrack } from "hooks/analytics/useTrack";
|
||||
import { useTxNotifications } from "hooks/useTxNotifications";
|
||||
import { useV5DashboardChain } from "lib/v5-adapter";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { getContract } from "thirdweb";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import { type BatchToReveal, reveal } from "thirdweb/extensions/erc721";
|
||||
import { useSendAndConfirmTransaction } from "thirdweb/react";
|
||||
import { FormErrorMessage, FormLabel, Heading } from "tw-components";
|
||||
|
||||
const REVEAL_FORM_ID = "reveal-form";
|
||||
|
||||
interface NFTRevealFormProps {
|
||||
contract: NFTContract;
|
||||
contract: ThirdwebContract;
|
||||
batchesToReveal: BatchToReveal[];
|
||||
}
|
||||
|
||||
export const NFTRevealForm: React.FC<NFTRevealFormProps> = ({ contract }) => {
|
||||
export const NFTRevealForm: React.FC<NFTRevealFormProps> = ({
|
||||
contract,
|
||||
batchesToReveal,
|
||||
}) => {
|
||||
const trackEvent = useTrack();
|
||||
const reveal = useRevealLazyMint(contract as RevealableContract);
|
||||
const { data: batchesToReveal } = useBatchesToReveal(
|
||||
contract as RevealableContract,
|
||||
);
|
||||
|
||||
const sendTxMutation = useSendAndConfirmTransaction();
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
} = useForm<{ batchId: string; password: string }>();
|
||||
const modalContext = useModalContext();
|
||||
const chain = useV5DashboardChain(contract?.chainId);
|
||||
const contractV5 =
|
||||
contract && chain
|
||||
? getContract({
|
||||
address: contract.getAddress(),
|
||||
chain: chain,
|
||||
client: thirdwebClient,
|
||||
})
|
||||
: null;
|
||||
|
||||
const { onSuccess, onError } = useTxNotifications(
|
||||
"Batch revealed successfully",
|
||||
"Error revealing batch upload",
|
||||
contractV5,
|
||||
contract,
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -72,37 +61,38 @@ export const NFTRevealForm: React.FC<NFTRevealFormProps> = ({ contract }) => {
|
||||
action: "batch-upload-reveal",
|
||||
label: "attempt",
|
||||
});
|
||||
reveal.mutate(
|
||||
{
|
||||
batchId: data.batchId,
|
||||
password: data.password,
|
||||
|
||||
const tx = reveal({
|
||||
contract,
|
||||
batchId: BigInt(data.batchId),
|
||||
password: data.password,
|
||||
});
|
||||
|
||||
sendTxMutation.mutate(tx, {
|
||||
onSuccess: () => {
|
||||
trackEvent({
|
||||
category: "nft",
|
||||
action: "batch-upload-reveal",
|
||||
label: "success",
|
||||
});
|
||||
onSuccess();
|
||||
modalContext.onClose();
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
trackEvent({
|
||||
category: "nft",
|
||||
action: "batch-upload-reveal",
|
||||
label: "success",
|
||||
});
|
||||
onSuccess();
|
||||
modalContext.onClose();
|
||||
},
|
||||
onError: (error) => {
|
||||
trackEvent({
|
||||
category: "nft",
|
||||
action: "batch-upload-reveal",
|
||||
label: "error",
|
||||
});
|
||||
onError(error);
|
||||
},
|
||||
onError: (error) => {
|
||||
trackEvent({
|
||||
category: "nft",
|
||||
action: "batch-upload-reveal",
|
||||
label: "error",
|
||||
});
|
||||
onError(error);
|
||||
},
|
||||
);
|
||||
});
|
||||
})}
|
||||
>
|
||||
<FormControl isRequired isInvalid={!!errors.password} mr={4}>
|
||||
<FormLabel>Select a batch</FormLabel>
|
||||
<Select {...register("batchId")} autoFocus>
|
||||
{batchesToReveal?.map((batch) => (
|
||||
{batchesToReveal.map((batch) => (
|
||||
<option
|
||||
key={batch.batchId.toString()}
|
||||
value={batch.batchId.toString()}
|
||||
@@ -128,7 +118,7 @@ export const NFTRevealForm: React.FC<NFTRevealFormProps> = ({ contract }) => {
|
||||
<DrawerFooter>
|
||||
<TransactionButton
|
||||
transactionCount={1}
|
||||
isLoading={reveal.isLoading}
|
||||
isLoading={sendTxMutation.isPending}
|
||||
form={REVEAL_FORM_ID}
|
||||
type="submit"
|
||||
colorScheme="primary"
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import { Box, Flex } from "@chakra-ui/react";
|
||||
import { useContract } from "@thirdweb-dev/react";
|
||||
import { extensionDetectedState } from "components/buttons/ExtensionDetectButton";
|
||||
import { detectFeatures } from "components/contract-components/utils";
|
||||
import { useRouter } from "next/router";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import * as ERC721Ext from "thirdweb/extensions/erc721";
|
||||
@@ -31,7 +28,6 @@ export const ContractNFTPage: React.FC<NftOverviewPageProps> = ({
|
||||
contract,
|
||||
isErc721,
|
||||
}) => {
|
||||
const contractQuery = useContract(contract.address);
|
||||
const router = useRouter();
|
||||
const tokenId = router.query?.paths?.[2];
|
||||
const functionSelectorQuery = useContractFunctionSelectors(contract);
|
||||
@@ -82,23 +78,16 @@ export const ContractNFTPage: React.FC<NftOverviewPageProps> = ({
|
||||
);
|
||||
})();
|
||||
|
||||
const isRevealable = detectFeatures(contractQuery.contract, [
|
||||
"ERC721Revealable",
|
||||
]);
|
||||
|
||||
const detectedRevealableState = extensionDetectedState({
|
||||
contractQuery,
|
||||
feature: ["ERC721Revealable"],
|
||||
});
|
||||
const isRevealable = ERC721Ext.isGetBaseURICountSupported(
|
||||
functionSelectorQuery.data,
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap={6}>
|
||||
<Flex direction="row" justify="space-between" align="center">
|
||||
<Heading size="title.sm">Contract NFTs</Heading>
|
||||
<Flex gap={2} flexDir={{ base: "column", md: "row" }}>
|
||||
{detectedRevealableState === "enabled" && contractQuery?.contract && (
|
||||
<NFTRevealButton contractQuery={contractQuery} />
|
||||
)}
|
||||
{isRevealable && <NFTRevealButton contract={contract} />}
|
||||
{isERC721ClaimToSupported && (
|
||||
/**
|
||||
* This button is used for claiming NFT Drop contract (erc721) only!
|
||||
@@ -117,8 +106,8 @@ export const ContractNFTPage: React.FC<NftOverviewPageProps> = ({
|
||||
)}
|
||||
{isLazyMintable && (
|
||||
<BatchLazyMintButton
|
||||
contractQuery={contractQuery}
|
||||
isRevealable={isRevealable}
|
||||
isErc721={isErc721}
|
||||
contract={contract}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -2,8 +2,6 @@ import {
|
||||
useIsAdmin,
|
||||
useIsMinter,
|
||||
} from "@3rdweb-sdk/react/hooks/useContractRoles";
|
||||
import { useBatchesToReveal, useContract } from "@thirdweb-dev/react";
|
||||
import { detectFeatures } from "components/contract-components/utils";
|
||||
import { StepsCard } from "components/dashboard/StepsCard";
|
||||
import { useTabHref } from "contract-ui/utils";
|
||||
import { useMemo } from "react";
|
||||
@@ -37,7 +35,6 @@ export const ContractChecklist: React.FC<ContractChecklistProps> = ({
|
||||
isErc721,
|
||||
}) => {
|
||||
const functionSelectorQuery = useContractFunctionSelectors(contract);
|
||||
const contractQuery = useContract(contract.address);
|
||||
const nftHref = useTabHref("nfts");
|
||||
const tokenHref = useTabHref("tokens");
|
||||
const accountsHref = useTabHref("accounts");
|
||||
@@ -80,8 +77,10 @@ export const ContractChecklist: React.FC<ContractChecklistProps> = ({
|
||||
},
|
||||
);
|
||||
|
||||
// need to replace!
|
||||
const batchesToReveal = useBatchesToReveal(contractQuery.contract);
|
||||
const batchesToReveal = useReadContract(ERC721Ext.getBatchesToReveal, {
|
||||
contract,
|
||||
queryOptions: { enabled: isErc721 },
|
||||
});
|
||||
|
||||
const steps: Step[] = [
|
||||
{
|
||||
@@ -278,10 +277,9 @@ export const ContractChecklist: React.FC<ContractChecklistProps> = ({
|
||||
});
|
||||
}
|
||||
|
||||
const isRevealable = detectFeatures(contractQuery.contract, [
|
||||
"ERC721Revealable",
|
||||
"ERC1155Revealable",
|
||||
]);
|
||||
const isRevealable = ERC721Ext.isGetBaseURICountSupported(
|
||||
functionSelectorQuery.data,
|
||||
);
|
||||
const needsReveal = batchesToReveal.data?.length;
|
||||
if (isRevealable && needsReveal) {
|
||||
steps.push({
|
||||
|
||||
@@ -23,6 +23,18 @@ export {
|
||||
isRemoveSessionKeySupported,
|
||||
} from "../../extensions/erc4337/account/removeSessionKey.js";
|
||||
|
||||
export {
|
||||
isAccountDeployed,
|
||||
type IsAccountDeployedParams,
|
||||
isIsAccountDeployedSupported,
|
||||
} from "../../extensions/erc4337/account/isAccountDeployed.js";
|
||||
|
||||
export {
|
||||
createAccount,
|
||||
type CreateAccountParams,
|
||||
isCreateAccountSupported,
|
||||
} from "../../extensions/erc4337/__generated__/IAccountFactory/write/createAccount.js";
|
||||
|
||||
export {
|
||||
getAllActiveSigners,
|
||||
isGetAllActiveSignersSupported,
|
||||
|
||||
@@ -199,6 +199,7 @@ export {
|
||||
export {
|
||||
type BatchToReveal,
|
||||
getBatchesToReveal,
|
||||
isGetBaseURICountSupported,
|
||||
} from "../../extensions/erc721/lazyMinting/read/getBatchesToReveal.js";
|
||||
export {
|
||||
updateMetadata,
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { getBytecode } from "../../../contract/actions/get-bytecode.js";
|
||||
import type { BaseTransactionOptions } from "../../../transaction/types.js";
|
||||
import * as PredictAddress from "../__generated__/IAccountFactory/read/getAddress.js";
|
||||
|
||||
export {
|
||||
isGetAddressSupported as isIsAccountDeployedSupported,
|
||||
type GetAddressParams as IsAccountDeployedParams,
|
||||
} from "../__generated__/IAccountFactory/read/getAddress.js";
|
||||
|
||||
/**
|
||||
* Adds admin permissions for a specified address.
|
||||
* @param options - The options for the addAdmin function.
|
||||
* @returns The transaction object to be sent.
|
||||
* @example
|
||||
* ```ts
|
||||
* import { isAccountDeployed } from 'thirdweb/extensions/erc4337';
|
||||
*
|
||||
* const transaction = addAdmin({
|
||||
* contract,
|
||||
* account,
|
||||
* adminAddress: '0x...'
|
||||
* });
|
||||
*
|
||||
* await isAccountDeployed({ contract, adminSigner });
|
||||
* ```
|
||||
* @extension ERC4337
|
||||
*/
|
||||
export async function isAccountDeployed(
|
||||
options: BaseTransactionOptions<PredictAddress.GetAddressParams>,
|
||||
): Promise<boolean> {
|
||||
const predictedAddress = await PredictAddress.getAddress(options);
|
||||
const bytecode = await getBytecode({
|
||||
...options.contract,
|
||||
address: predictedAddress,
|
||||
});
|
||||
return bytecode !== "0x";
|
||||
}
|
||||
@@ -3,11 +3,11 @@ import type { BaseTransactionOptions } from "../../../../transaction/types.js";
|
||||
import type { Hex } from "../../../../utils/encoding/hex.js";
|
||||
import { fetchTokenMetadata } from "../../../../utils/nft/fetchTokenMetadata.js";
|
||||
import type { NFTMetadata } from "../../../../utils/nft/parseNft.js";
|
||||
import { getBaseURICount } from "../../__generated__/IBatchMintMetadata/read/getBaseURICount.js";
|
||||
import { getBatchIdAtIndex } from "../../__generated__/IBatchMintMetadata/read/getBatchIdAtIndex.js";
|
||||
import { encryptedData } from "../../__generated__/IDelayedReveal/read/encryptedData.js";
|
||||
import { baseURIIndices } from "../../__generated__/IDrop/read/baseURIIndices.js";
|
||||
import { tokenURI } from "../../__generated__/IERC721A/read/tokenURI.js";
|
||||
import * as GetBaseURICount from "../../__generated__/IBatchMintMetadata/read/getBaseURICount.js";
|
||||
import * as GetBatchIdAtIndex from "../../__generated__/IBatchMintMetadata/read/getBatchIdAtIndex.js";
|
||||
import * as EncryptedData from "../../__generated__/IDelayedReveal/read/encryptedData.js";
|
||||
import * as BaseURIIndicies from "../../__generated__/IDrop/read/baseURIIndices.js";
|
||||
import * as TokenURI from "../../__generated__/IERC721A/read/tokenURI.js";
|
||||
|
||||
/**
|
||||
* @extension ERC721
|
||||
@@ -39,7 +39,7 @@ export interface BatchToReveal {
|
||||
export async function getBatchesToReveal(
|
||||
options: BaseTransactionOptions,
|
||||
): Promise<BatchToReveal[]> {
|
||||
const count = await getBaseURICount({
|
||||
const count = await GetBaseURICount.getBaseURICount({
|
||||
contract: options.contract,
|
||||
});
|
||||
if (count === 0n) {
|
||||
@@ -50,13 +50,13 @@ export async function getBatchesToReveal(
|
||||
const uriIndices = await Promise.all(
|
||||
countRangeArray.map(async (batchId) => {
|
||||
try {
|
||||
return await getBatchIdAtIndex({
|
||||
return await GetBatchIdAtIndex.getBatchIdAtIndex({
|
||||
contract: options.contract,
|
||||
index: BigInt(batchId),
|
||||
});
|
||||
} catch {
|
||||
try {
|
||||
return await baseURIIndices({
|
||||
return await BaseURIIndicies.baseURIIndices({
|
||||
contract: options.contract,
|
||||
index: BigInt(batchId),
|
||||
});
|
||||
@@ -74,7 +74,7 @@ export async function getBatchesToReveal(
|
||||
|
||||
const tokenMetadatas = await Promise.all(
|
||||
Array.from([0, ...uriIndicesWithZeroStart]).map(async (i) => {
|
||||
const uri = await tokenURI({
|
||||
const uri = await TokenURI.tokenURI({
|
||||
contract: options.contract,
|
||||
tokenId: BigInt(i),
|
||||
});
|
||||
@@ -88,7 +88,7 @@ export async function getBatchesToReveal(
|
||||
|
||||
const encryptedUriData = await Promise.all(
|
||||
Array.from([...uriIndices]).map((i) =>
|
||||
encryptedData({
|
||||
EncryptedData.encryptedData({
|
||||
contract: options.contract,
|
||||
index: BigInt(i),
|
||||
}),
|
||||
@@ -118,3 +118,25 @@ export async function getBatchesToReveal(
|
||||
}))
|
||||
.filter((_, index) => (encryptedBaseUris[index]?.length || 0) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the `getBatchesToReveal` method is supported by the given contract.
|
||||
* @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors.
|
||||
* @returns A boolean indicating if the `getBatchesToReveal` method is supported.
|
||||
* @extension ERC721
|
||||
* @example
|
||||
* ```ts
|
||||
* import { isGetBatchesToRevealSupported } from "thirdweb/extensions/erc721";
|
||||
*
|
||||
* const supported = isGetBatchesToRevealSupported(["0x..."]);
|
||||
* ```
|
||||
*/
|
||||
export function isGetBaseURICountSupported(availableSelectors: string[]) {
|
||||
return [
|
||||
GetBaseURICount.isGetBaseURICountSupported(availableSelectors),
|
||||
GetBatchIdAtIndex.isGetBatchIdAtIndexSupported(availableSelectors),
|
||||
EncryptedData.isEncryptedDataSupported(availableSelectors),
|
||||
BaseURIIndicies.isBaseURIIndicesSupported(availableSelectors),
|
||||
TokenURI.isTokenURISupported(availableSelectors),
|
||||
].every(Boolean);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user