Skip to content

Commit 60c092f

Browse files
authored
[Update] Refactor NFT reveal functionality and batch lazy minting (#4413)
1 parent 4ad441e commit 60c092f

File tree

13 files changed

+269
-245
lines changed

13 files changed

+269
-245
lines changed

apps/dashboard/src/components/contract-components/utils.ts

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
import {
2-
type Abi,
3-
type AddContractInput,
4-
type FeatureName,
5-
type ValidContractInstance,
6-
detectFeatures as detectFeaturesFromSdk,
7-
isExtensionEnabled,
8-
} from "@thirdweb-dev/sdk";
91
import { MULTICHAIN_REGISTRY_CONTRACT } from "constants/contracts";
102
import {
113
DASHBOARD_ENGINE_RELAYER_URL,
@@ -14,30 +6,14 @@ import {
146
import { prepareContractCall, sendAndConfirmTransaction } from "thirdweb";
157
import type { Account } from "thirdweb/wallets";
168

17-
export function detectFeatures<TContract extends ValidContractInstance | null>(
18-
contract: ValidContractInstance | null | undefined,
19-
features: FeatureName[],
20-
strategy: "any" | "all" = "any",
21-
): contract is TContract {
22-
if (!contract) {
23-
return false;
24-
}
25-
if (!("abi" in contract)) {
26-
return false;
27-
}
28-
29-
const extensions = detectFeaturesFromSdk(contract.abi as Abi);
9+
type ContractInput = {
10+
address: string;
11+
chainId: number;
12+
};
3013

31-
if (strategy === "any") {
32-
return features.some((feature) =>
33-
isExtensionEnabled(contract.abi as Abi, feature, extensions),
34-
);
35-
}
36-
37-
return features.every((feature) =>
38-
isExtensionEnabled(contract.abi as Abi, feature, extensions),
39-
);
40-
}
14+
type AddContractInput = ContractInput & {
15+
metadataURI?: string;
16+
};
4117

4218
export async function addContractToMultiChainRegistry(
4319
contractData: AddContractInput,

apps/dashboard/src/contract-ui/tabs/accounts/components/create-account-button.tsx

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import { Tooltip } from "@chakra-ui/react";
2-
import {
3-
useAccountsForAddress,
4-
useContract,
5-
useCreateAccount,
6-
useIsAccountDeployed,
7-
} from "@thirdweb-dev/react";
82
import { TransactionButton } from "components/buttons/TransactionButton";
93
import type { ThirdwebContract } from "thirdweb";
10-
import { useActiveAccount } from "thirdweb/react";
4+
import * as ERC4337Ext from "thirdweb/extensions/erc4337";
5+
import {
6+
useActiveAccount,
7+
useReadContract,
8+
useSendAndConfirmTransaction,
9+
} from "thirdweb/react";
1110
import { Button, Card, Text } from "tw-components";
1211

1312
interface CreateAccountButtonProps {
@@ -18,26 +17,35 @@ export const CreateAccountButton: React.FC<CreateAccountButtonProps> = ({
1817
contract,
1918
...restButtonProps
2019
}) => {
21-
const contractQuery = useContract(contract.address);
22-
const { mutate: createAccount, isLoading } = useCreateAccount(
23-
contractQuery?.contract,
24-
);
20+
const sendTxMutation = useSendAndConfirmTransaction();
2521

2622
const address = useActiveAccount()?.address;
27-
const { data: isAccountDeployed } = useIsAccountDeployed(
28-
contractQuery.contract,
29-
address,
30-
);
31-
const { data: accountsForAddress } = useAccountsForAddress(
32-
contractQuery.contract,
33-
address,
23+
24+
const isAccountDeployedQuery = useReadContract(ERC4337Ext.isAccountDeployed, {
25+
contract,
26+
adminSigner: address || "",
27+
data: "0x",
28+
queryOptions: {
29+
enabled: !!address,
30+
},
31+
});
32+
33+
const accountsForAddressQuery = useReadContract(
34+
ERC4337Ext.getAccountsOfSigner,
35+
{
36+
contract,
37+
signer: address || "",
38+
queryOptions: {
39+
enabled: !!address,
40+
},
41+
},
3442
);
3543

36-
if (!contractQuery.contract || !address) {
44+
if (!address) {
3745
return null;
3846
}
3947

40-
if (isAccountDeployed && accountsForAddress?.length) {
48+
if (isAccountDeployedQuery.data && accountsForAddressQuery.data?.length) {
4149
return (
4250
<Tooltip
4351
label={
@@ -62,10 +70,17 @@ export const CreateAccountButton: React.FC<CreateAccountButtonProps> = ({
6270
return (
6371
<TransactionButton
6472
colorScheme="primary"
65-
onClick={() => createAccount(address)}
66-
isLoading={isLoading}
73+
onClick={() => {
74+
const tx = ERC4337Ext.createAccount({
75+
contract,
76+
admin: address,
77+
data: "0x",
78+
});
79+
sendTxMutation.mutate(tx);
80+
}}
81+
isLoading={sendTxMutation.isPending}
6782
transactionCount={1}
68-
isDisabled={isAccountDeployed}
83+
isDisabled={isAccountDeployedQuery.data}
6984
{...restButtonProps}
7085
>
7186
Create Account

apps/dashboard/src/contract-ui/tabs/claim-conditions/components/claim-conditions-form/index.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -621,8 +621,7 @@ export const ClaimConditionsForm: React.FC<ClaimConditionsFormProps> = ({
621621
{controlledFields.some((field) => field.fromSdk) && (
622622
<ResetClaimEligibility
623623
isErc20={isErc20}
624-
contract={contract}
625-
isColumn={isColumn}
624+
contract={contractV5}
626625
tokenId={tokenId}
627626
/>
628627
)}

apps/dashboard/src/contract-ui/tabs/claim-conditions/components/reset-claim-eligibility.tsx

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
11
import { ToolTipLabel } from "@/components/ui/tooltip";
2-
import { thirdwebClient } from "@/constants/client";
32
import { AdminOnly } from "@3rdweb-sdk/react/components/roles/admin-only";
4-
import { Box } from "@chakra-ui/react";
5-
import {
6-
type DropContract,
7-
useResetClaimConditions,
8-
} from "@thirdweb-dev/react";
93
import { TransactionButton } from "components/buttons/TransactionButton";
104
import { useTrack } from "hooks/analytics/useTrack";
115
import { useTxNotifications } from "hooks/useTxNotifications";
12-
import { useV5DashboardChain } from "lib/v5-adapter";
136
import { CircleHelpIcon } from "lucide-react";
14-
import { getContract } from "thirdweb";
7+
import type { ThirdwebContract } from "thirdweb";
8+
import * as ERC20Ext from "thirdweb/extensions/erc20";
9+
import * as ERC721Ext from "thirdweb/extensions/erc721";
10+
import * as ERC1155Ext from "thirdweb/extensions/erc1155";
11+
import { useSendAndConfirmTransaction } from "thirdweb/react";
1512

1613
interface ResetClaimEligibilityProps {
1714
isErc20: boolean;
18-
contract: DropContract;
15+
contract: ThirdwebContract;
1916
tokenId?: string;
20-
isColumn?: true;
2117
}
2218

2319
export const ResetClaimEligibility: React.FC<ResetClaimEligibilityProps> = ({
@@ -26,7 +22,9 @@ export const ResetClaimEligibility: React.FC<ResetClaimEligibilityProps> = ({
2622
isErc20,
2723
}) => {
2824
const trackEvent = useTrack();
29-
const resetClaimConditions = useResetClaimConditions(contract, tokenId);
25+
26+
const sendTxMutation = useSendAndConfirmTransaction();
27+
3028
const txNotification = useTxNotifications(
3129
"Successfully reset claim eligibility",
3230
"Failed to reset claim eligibility",
@@ -41,7 +39,27 @@ export const ResetClaimEligibility: React.FC<ResetClaimEligibilityProps> = ({
4139
label: "attempt",
4240
});
4341

44-
resetClaimConditions.mutate(undefined, {
42+
const tx = (() => {
43+
switch (true) {
44+
// erc 20
45+
case isErc20: {
46+
return ERC20Ext.resetClaimEligibility({ contract });
47+
}
48+
// erc 1155
49+
case tokenId !== undefined: {
50+
return ERC1155Ext.resetClaimEligibility({
51+
contract,
52+
tokenId: BigInt(tokenId),
53+
});
54+
}
55+
// assume erc 721
56+
default: {
57+
return ERC721Ext.resetClaimEligibility({ contract });
58+
}
59+
}
60+
})();
61+
62+
sendTxMutation.mutate(tx, {
4563
onSuccess: () => {
4664
txNotification.onSuccess();
4765
trackEvent({
@@ -62,27 +80,19 @@ export const ResetClaimEligibility: React.FC<ResetClaimEligibilityProps> = ({
6280
});
6381
};
6482

65-
const chain = useV5DashboardChain(contract?.chainId);
66-
67-
if (!contract || !chain) {
83+
if (!contract) {
6884
return null;
6985
}
7086

71-
const contractV5 = getContract({
72-
address: contract.getAddress(),
73-
chain,
74-
client: thirdwebClient,
75-
});
76-
7787
return (
78-
<AdminOnly contract={contractV5} fallback={<Box pb={5} />}>
88+
<AdminOnly contract={contract} fallback={<div className="pb-5" />}>
7989
<TransactionButton
8090
colorScheme="secondary"
8191
bg="bgBlack"
8292
color="bgWhite"
8393
transactionCount={1}
8494
type="button"
85-
isLoading={resetClaimConditions.isLoading}
95+
isLoading={sendTxMutation.isPending}
8696
onClick={handleResetClaimEligibility}
8797
loadingText="Resetting..."
8898
size="sm"

apps/dashboard/src/contract-ui/tabs/nfts/components/batch-lazy-mint-button.tsx

Lines changed: 43 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,37 @@
11
import { MinterOnly } from "@3rdweb-sdk/react/components/roles/minter-only";
22
import { Icon, useDisclosure } from "@chakra-ui/react";
3-
import {
4-
type RevealableContract,
5-
type useContract,
6-
useDelayedRevealLazyMint,
7-
useLazyMint,
8-
} from "@thirdweb-dev/react";
93
import { BatchLazyMint } from "core-ui/batch-upload/batch-lazy-mint";
10-
import {
11-
ProgressBox,
12-
type UploadProgressEvent,
13-
} from "core-ui/batch-upload/progress-box";
144
import { useTrack } from "hooks/analytics/useTrack";
155
import { useTxNotifications } from "hooks/useTxNotifications";
16-
import { useState } from "react";
176
import { RiCheckboxMultipleBlankLine } from "react-icons/ri";
187
import type { ThirdwebContract } from "thirdweb";
19-
import { nextTokenIdToMint } from "thirdweb/extensions/erc721";
20-
import { useReadContract } from "thirdweb/react";
8+
import * as ERC721Ext from "thirdweb/extensions/erc721";
9+
import * as ERC1155Ext from "thirdweb/extensions/erc1155";
10+
import { useReadContract, useSendAndConfirmTransaction } from "thirdweb/react";
2111
import { Button, Drawer } from "tw-components";
2212

2313
interface BatchLazyMintButtonProps {
24-
contractQuery: ReturnType<typeof useContract>;
2514
isRevealable: boolean;
2615
contract: ThirdwebContract;
16+
isErc721: boolean;
2717
}
2818

2919
export const BatchLazyMintButton: React.FC<BatchLazyMintButtonProps> = ({
30-
contractQuery,
3120
contract,
3221
isRevealable,
22+
isErc721,
3323
}) => {
34-
const contractV4 = contractQuery.contract;
3524
const trackEvent = useTrack();
3625
const { isOpen, onOpen, onClose } = useDisclosure();
3726

38-
// Both ERC721 and ERC1155 have the same function signature for nextTokenIdToMint,
39-
// so we can just use either one.
40-
const nextTokenIdToMintQuery = useReadContract(nextTokenIdToMint, {
41-
contract,
42-
});
43-
const [progress, setProgress] = useState<UploadProgressEvent>({
44-
progress: 0,
45-
total: 100,
46-
});
47-
48-
const mintBatchMutation = useLazyMint(
49-
contractV4,
50-
(event: UploadProgressEvent) => {
51-
setProgress(event);
27+
const nextTokenIdToMintQuery = useReadContract(
28+
isErc721 ? ERC721Ext.nextTokenIdToMint : ERC1155Ext.nextTokenIdToMint,
29+
{
30+
contract,
5231
},
5332
);
5433

55-
const mintDelayedRevealBatchMutation = useDelayedRevealLazyMint(
56-
contractV4 as RevealableContract,
57-
(event: UploadProgressEvent) => {
58-
setProgress(event);
59-
},
60-
);
34+
const sendTxMutation = useSendAndConfirmTransaction();
6135

6236
const txNotifications = useTxNotifications(
6337
"Batch uploaded successfully",
@@ -87,17 +61,38 @@ export const BatchLazyMintButton: React.FC<BatchLazyMintButtonProps> = ({
8761
label: "attempt",
8862
});
8963
try {
90-
if (revealType === "instant") {
91-
// instant reveal
92-
await mintBatchMutation.mutateAsync(data);
93-
} else {
94-
// otherwise it's delayed reveal
95-
await mintDelayedRevealBatchMutation.mutateAsync({
96-
metadatas: data.metadata,
97-
placeholder: data.placeholderMetadata,
98-
password: data.password,
99-
});
100-
}
64+
const tx = (() => {
65+
switch (true) {
66+
// lazy mint erc721
67+
case revealType === "instant" && isErc721: {
68+
return ERC721Ext.lazyMint({
69+
contract,
70+
nfts: data.metadatas,
71+
});
72+
}
73+
// lazy mint erc1155
74+
case revealType === "instant" && !isErc721: {
75+
return ERC1155Ext.lazyMint({
76+
contract,
77+
nfts: data.metadatas,
78+
});
79+
}
80+
// delayed reveal erc721
81+
case revealType === "delayed": {
82+
return ERC721Ext.createDelayedRevealBatch({
83+
contract,
84+
metadata: data.metadata,
85+
password: data.password,
86+
placeholderMetadata: data.placeholderMetadata,
87+
});
88+
}
89+
default: {
90+
throw new Error("Invalid reveal type");
91+
}
92+
}
93+
})();
94+
95+
await sendTxMutation.mutateAsync(tx);
10196

10297
trackEvent({
10398
category: "nft",
@@ -114,20 +109,11 @@ export const BatchLazyMintButton: React.FC<BatchLazyMintButtonProps> = ({
114109
error,
115110
});
116111
txNotifications.onError(error);
117-
} finally {
118-
setProgress({
119-
progress: 0,
120-
total: 100,
121-
});
122112
}
123113
}}
124114
nextTokenIdToMint={nextTokenIdToMintQuery.data || 0n}
125115
isRevealable={isRevealable}
126-
>
127-
{mintBatchMutation.isLoading ? (
128-
<ProgressBox progress={progress} />
129-
) : null}
130-
</BatchLazyMint>
116+
/>
131117
</Drawer>
132118
<Button
133119
colorScheme="primary"

0 commit comments

Comments
 (0)