Skip to content

Commit 4ad441e

Browse files
committed
[Update] Refactor ContractChecklist and add feature detection (#4400)
<!-- start pr-codex --> ## PR-Codex overview The focus of this PR is to add support for additional ERC721 metadata and claim conditions, optimize contract checklist components, and improve feature detection. ### Detailed summary - Added support for `isSharedMetadataSupported` and `getClaimConditions` in ERC721. - Optimized contract checklist component for ERC721, ERC1155, and ERC20 contracts. - Improved feature detection for lazy minting, claim conditions, and account factory creation. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent 0f8679a commit 4ad441e

File tree

3 files changed

+122
-74
lines changed

3 files changed

+122
-74
lines changed

apps/dashboard/src/contract-ui/tabs/overview/components/ContractChecklist.tsx

Lines changed: 112 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,26 @@ import {
22
useIsAdmin,
33
useIsMinter,
44
} from "@3rdweb-sdk/react/hooks/useContractRoles";
5-
import {
6-
useBatchesToReveal,
7-
useClaimConditions,
8-
useContract,
9-
} from "@thirdweb-dev/react";
5+
import { useBatchesToReveal, useContract } from "@thirdweb-dev/react";
106
import { detectFeatures } from "components/contract-components/utils";
117
import { StepsCard } from "components/dashboard/StepsCard";
128
import { useTabHref } from "contract-ui/utils";
13-
import { totalSupply } from "thirdweb/extensions/erc20";
14-
import {
15-
getNFTs as erc721GetNfts,
16-
getTotalClaimedSupply,
17-
sharedMetadata,
18-
} from "thirdweb/extensions/erc721";
19-
import { getNFTs as erc1155Nfts } from "thirdweb/extensions/erc1155";
9+
import { useMemo } from "react";
10+
import type { ThirdwebContract } from "thirdweb";
11+
import * as ERC20Ext from "thirdweb/extensions/erc20";
12+
import * as ERC721Ext from "thirdweb/extensions/erc721";
13+
import * as ERC1155Ext from "thirdweb/extensions/erc1155";
14+
import * as ERC4337Ext from "thirdweb/extensions/erc4337";
2015
import { getAccounts } from "thirdweb/extensions/erc4337";
2116
import { useReadContract } from "thirdweb/react";
22-
23-
import type { ThirdwebContract } from "thirdweb";
2417
import { Link, Text } from "tw-components";
18+
import { useContractFunctionSelectors } from "../../../hooks/useContractFunctionSelectors";
2519

2620
interface ContractChecklistProps {
2721
contract: ThirdwebContract;
22+
isErc721: boolean;
23+
isErc1155: boolean;
24+
isErc20: boolean;
2825
}
2926

3027
type Step = {
@@ -35,37 +32,55 @@ type Step = {
3532

3633
export const ContractChecklist: React.FC<ContractChecklistProps> = ({
3734
contract,
35+
isErc1155,
36+
isErc20,
37+
isErc721,
3838
}) => {
39+
const functionSelectorQuery = useContractFunctionSelectors(contract);
3940
const contractQuery = useContract(contract.address);
4041
const nftHref = useTabHref("nfts");
4142
const tokenHref = useTabHref("tokens");
4243
const accountsHref = useTabHref("accounts");
4344
const claimConditionsHref = useTabHref("claim-conditions");
4445

45-
const erc721Claimed = useReadContract(getTotalClaimedSupply, {
46+
const erc721Claimed = useReadContract(ERC721Ext.getTotalClaimedSupply, {
4647
contract: contract,
48+
queryOptions: { enabled: isErc721 },
49+
});
50+
const erc20Supply = useReadContract(ERC20Ext.totalSupply, {
51+
contract,
52+
queryOptions: { enabled: isErc20 },
4753
});
48-
const erc20Supply = useReadContract(totalSupply, { contract });
4954
const accounts = useReadContract(getAccounts, {
5055
contract,
5156
start: 0n,
5257
end: 1n,
5358
});
54-
const erc721NftQuery = useReadContract(erc721GetNfts, {
55-
contract,
56-
start: 0,
57-
count: 1,
58-
});
59-
const erc1155NftQuery = useReadContract(erc1155Nfts, {
60-
contract,
61-
start: 0,
62-
count: 1,
63-
});
64-
const sharedMetadataQuery = useReadContract(sharedMetadata, {
59+
const nftsQuery = useReadContract(
60+
isErc721 ? ERC721Ext.getNFTs : ERC721Ext.getNFTs,
61+
{
62+
contract,
63+
start: 0,
64+
count: 1,
65+
queryOptions: { enabled: isErc721 || isErc1155 },
66+
},
67+
);
68+
69+
const sharedMetadataQuery = useReadContract(ERC721Ext.sharedMetadata, {
6570
contract,
71+
queryOptions: { enabled: isErc721 },
6672
});
6773

68-
const claimConditions = useClaimConditions(contractQuery.contract);
74+
const claimConditions = useReadContract(
75+
isErc721 ? ERC721Ext.getClaimConditions : ERC1155Ext.getClaimConditions,
76+
{
77+
contract,
78+
tokenId: 0n,
79+
queryOptions: { enabled: isErc721 || isErc1155 },
80+
},
81+
);
82+
83+
// need to replace!
6984
const batchesToReveal = useBatchesToReveal(contractQuery.contract);
7085

7186
const steps: Step[] = [
@@ -79,15 +94,58 @@ export const ContractChecklist: React.FC<ContractChecklistProps> = ({
7994
const isAdmin = useIsAdmin(contract);
8095
const isMinter = useIsMinter(contract);
8196

97+
const isLazyMintable = useMemo(() => {
98+
if (isErc721) {
99+
return ERC721Ext.isLazyMintSupported(functionSelectorQuery.data);
100+
}
101+
if (isErc1155) {
102+
return ERC1155Ext.isLazyMintSupported(functionSelectorQuery.data);
103+
}
104+
return false;
105+
}, [functionSelectorQuery.data, isErc721, isErc1155]);
106+
107+
const hasERC721ClaimConditions = useMemo(() => {
108+
return [
109+
// reads
110+
ERC721Ext.isGetClaimConditionByIdSupported(functionSelectorQuery.data),
111+
ERC721Ext.isGetActiveClaimConditionIdSupported(
112+
functionSelectorQuery.data,
113+
),
114+
ERC721Ext.isGetClaimConditionsSupported(functionSelectorQuery.data),
115+
ERC721Ext.isGetActiveClaimConditionSupported(functionSelectorQuery.data),
116+
// writes
117+
ERC721Ext.isSetClaimConditionsSupported(functionSelectorQuery.data),
118+
ERC721Ext.isResetClaimEligibilitySupported(functionSelectorQuery.data),
119+
].every(Boolean);
120+
}, [functionSelectorQuery.data]);
121+
122+
const hasERC20ClaimConditions = useMemo(() => {
123+
return [
124+
// reads
125+
ERC20Ext.isGetClaimConditionByIdSupported(functionSelectorQuery.data),
126+
ERC20Ext.isGetActiveClaimConditionIdSupported(functionSelectorQuery.data),
127+
ERC20Ext.isGetClaimConditionsSupported(functionSelectorQuery.data),
128+
ERC20Ext.isGetActiveClaimConditionSupported(functionSelectorQuery.data),
129+
// writes
130+
ERC20Ext.isSetClaimConditionsSupported(functionSelectorQuery.data),
131+
ERC20Ext.isResetClaimEligibilitySupported(functionSelectorQuery.data),
132+
].every(Boolean);
133+
}, [functionSelectorQuery.data]);
134+
135+
const accountFactory = useMemo(() => {
136+
return [
137+
ERC4337Ext.isGetAllAccountsSupported(functionSelectorQuery.data),
138+
ERC4337Ext.isGetAccountsSupported(functionSelectorQuery.data),
139+
ERC4337Ext.isTotalAccountsSupported(functionSelectorQuery.data),
140+
ERC4337Ext.isGetAccountsOfSignerSupported(functionSelectorQuery.data),
141+
ERC4337Ext.isPredictAccountAddressSupported(functionSelectorQuery.data),
142+
].every(Boolean);
143+
}, [functionSelectorQuery.data]);
144+
82145
if (!isAdmin) {
83146
return null;
84147
}
85148

86-
const isLazyMintable = detectFeatures(contractQuery.contract, [
87-
"ERC721LazyMintable",
88-
"ERC1155LazyMintableV2",
89-
"ERC1155LazyMintableV1",
90-
]);
91149
if (isLazyMintable && isMinter) {
92150
steps.push({
93151
title: "First NFT uploaded",
@@ -101,15 +159,11 @@ export const ContractChecklist: React.FC<ContractChecklistProps> = ({
101159
</Text>
102160
),
103161
// can be either 721 or 1155
104-
completed:
105-
(erc721NftQuery.data?.length || erc1155NftQuery.data?.length || 0) > 0,
162+
completed: (nftsQuery.data?.length || 0) > 0,
106163
});
107164
}
108165

109-
const isErc721SharedMetadadata = detectFeatures(contractQuery.contract, [
110-
"ERC721SharedMetadata",
111-
]);
112-
if (isErc721SharedMetadadata) {
166+
if (ERC721Ext.isSharedMetadataSupported(functionSelectorQuery.data)) {
113167
steps.push({
114168
title: "Set NFT Metadata",
115169
children: (
@@ -125,20 +179,7 @@ export const ContractChecklist: React.FC<ContractChecklistProps> = ({
125179
});
126180
}
127181

128-
const erc721hasClaimConditions = detectFeatures(contractQuery.contract, [
129-
"ERC721ClaimPhasesV1",
130-
"ERC721ClaimPhasesV2",
131-
"ERC721ClaimConditionsV1",
132-
"ERC721ClaimConditionsV2",
133-
"ERC721ClaimCustom",
134-
]);
135-
const erc20HasClaimConditions = detectFeatures(contractQuery.contract, [
136-
"ERC20ClaimPhasesV1",
137-
"ERC20ClaimPhasesV2",
138-
"ERC20ClaimConditionsV1",
139-
"ERC20ClaimConditionsV2",
140-
]);
141-
if (erc721hasClaimConditions || erc20HasClaimConditions) {
182+
if (hasERC721ClaimConditions || hasERC20ClaimConditions) {
142183
steps.push({
143184
title: "Set Claim Conditions",
144185
children: (
@@ -157,15 +198,15 @@ export const ContractChecklist: React.FC<ContractChecklistProps> = ({
157198
(erc20Supply.data || 0n) > 0n,
158199
});
159200
}
160-
if (erc721hasClaimConditions) {
201+
if (hasERC721ClaimConditions) {
161202
steps.push({
162203
title: "First NFT claimed",
163204
children: <Text size="label.sm">No NFTs have been claimed so far.</Text>,
164205
completed: (erc721Claimed.data || 0n) > 0n,
165206
});
166207
}
167208

168-
if (erc20HasClaimConditions) {
209+
if (hasERC20ClaimConditions) {
169210
steps.push({
170211
title: "First token claimed",
171212
children: (
@@ -175,10 +216,11 @@ export const ContractChecklist: React.FC<ContractChecklistProps> = ({
175216
});
176217
}
177218

178-
const tokenIsMintable = detectFeatures(contractQuery.contract, [
179-
"ERC20Mintable",
180-
]);
181-
if (tokenIsMintable && isMinter) {
219+
if (
220+
isErc20 &&
221+
ERC20Ext.isMintToSupported(functionSelectorQuery.data) &&
222+
isMinter
223+
) {
182224
steps.push({
183225
title: "First token minted",
184226
children: (
@@ -194,10 +236,15 @@ export const ContractChecklist: React.FC<ContractChecklistProps> = ({
194236
});
195237
}
196238

197-
const nftIsMintable = detectFeatures(contractQuery.contract, [
198-
"ERC721Mintable",
199-
"ERC1155Mintable",
200-
]);
239+
const nftIsMintable = (() => {
240+
if (isErc721) {
241+
return ERC721Ext.isMintToSupported(functionSelectorQuery.data);
242+
}
243+
if (isErc1155) {
244+
return ERC1155Ext.isMintToSupported(functionSelectorQuery.data);
245+
}
246+
return false;
247+
})();
201248
if (nftIsMintable && isMinter) {
202249
steps.push({
203250
title: "First NFT minted",
@@ -211,15 +258,11 @@ export const ContractChecklist: React.FC<ContractChecklistProps> = ({
211258
</Text>
212259
),
213260
// can be either 721 or 1155
214-
completed:
215-
(erc721NftQuery.data?.length || erc1155NftQuery.data?.length || 0) > 0,
261+
completed: (nftsQuery.data?.length || 0) > 0,
216262
});
217263
}
218264

219-
const isAccountFactory = detectFeatures(contractQuery.contract, [
220-
"AccountFactory",
221-
]);
222-
if (isAccountFactory) {
265+
if (accountFactory) {
223266
steps.push({
224267
title: "First account created",
225268
children: (

apps/dashboard/src/contract-ui/tabs/overview/page.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ const TRACKING_CATEGORY = "contract_overview";
2424

2525
export const ContractOverviewPage: React.FC<ContractOverviewPageProps> = ({
2626
contract,
27-
2827
isErc1155,
2928
isErc20,
3029
isErc721,
@@ -35,7 +34,12 @@ export const ContractOverviewPage: React.FC<ContractOverviewPageProps> = ({
3534
return (
3635
<SimpleGrid columns={{ base: 1, xl: 10 }} gap={20}>
3736
<GridItem as={Flex} colSpan={{ xl: 7 }} direction="column" gap={16}>
38-
<ContractChecklist contract={contract} />
37+
<ContractChecklist
38+
isErc721={isErc721}
39+
isErc1155={isErc1155}
40+
isErc20={isErc20}
41+
contract={contract}
42+
/>
3943

4044
<AnalyticsOverview
4145
contractAddress={contract.address}
@@ -73,8 +77,6 @@ export const ContractOverviewPage: React.FC<ContractOverviewPageProps> = ({
7377
</GridItem>
7478
<GridItem colSpan={{ xl: 3 }} as={Flex} direction="column" gap={6}>
7579
<PublishedBy contractAddress={contract.address} />
76-
{/* TODO: this is broken because it relies on something that no longer exists on contract instance */}
77-
{/* {contract?.abi && <Extensions abi={contract?.abi} />} */}
7880
</GridItem>
7981
</SimpleGrid>
8082
);

packages/thirdweb/src/exports/extensions/erc721.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,10 @@ export {
143143
/**
144144
* SHARED METADATA extension for ERC721
145145
*/
146-
export { sharedMetadata } from "../../extensions/erc721/__generated__/ISharedMetadata/read/sharedMetadata.js";
146+
export {
147+
sharedMetadata,
148+
isSharedMetadataSupported,
149+
} from "../../extensions/erc721/__generated__/ISharedMetadata/read/sharedMetadata.js";
147150
export {
148151
setSharedMetadata,
149152
type SetSharedMetadataParams,

0 commit comments

Comments
 (0)