[Update] Refactor NFT reveal functionality and batch lazy minting (#4413)

This commit is contained in:
Jonas Daniels
2024-09-05 01:33:31 -07:00
committed by GitHub
parent 4ad441e2f7
commit 60c092f87b
13 changed files with 269 additions and 245 deletions
@@ -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
@@ -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}
/>
)}
@@ -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);
}