[Update] Migrate contract analytics to v5 (#4366)
<!-- start pr-codex -->
## PR-Codex overview
This PR focuses on updating imports and types related to ABI parameters in the codebase.
### Detailed summary
- Updated imports from `"@thirdweb-dev/sdk"` to `"abitype"`
- Changed types to use `AbiParameter` instead of previous types like `Abi`, `AbiFunction`, `AbiEvent`
- Fixed type errors and updated type annotations throughout the codebase
> The following files were skipped due to too many changes: `apps/dashboard/src/components/contract-functions/interactive-abi-function.tsx`, `apps/dashboard/src/contract-ui/tabs/overview/components/NFTDetails.tsx`, `packages/thirdweb/src/extensions/thirdweb/read/contractType.ts`, `apps/dashboard/src/contract-ui/tabs/code/components/code-overview.tsx`, `apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx`, `apps/dashboard/src/components/contract-components/contract-publish-form/index.tsx`, `apps/dashboard/src/contract-ui/tabs/analytics/page.tsx`, `apps/dashboard/src/contract-ui/tabs/overview/page.tsx`, `apps/dashboard/src/components/contract-components/hooks.ts`, `apps/dashboard/src/components/contract-functions/contract-function.tsx`, `apps/dashboard/src/contract-ui/hooks/useRouteConfig.tsx`
> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`
<!-- end pr-codex -->
This commit is contained in:
+12
-11
@@ -12,6 +12,7 @@ import {
|
||||
Flex,
|
||||
FormControl,
|
||||
} from "@chakra-ui/react";
|
||||
import type { Abi } from "abitype";
|
||||
import { TransactionButton } from "components/buttons/TransactionButton";
|
||||
import { NetworkSelectorButton } from "components/selects/NetworkSelectorButton";
|
||||
import { DeprecatedAlert } from "components/shared/DeprecatedAlert";
|
||||
@@ -32,7 +33,6 @@ import { encodeAbiParameters } from "thirdweb/utils";
|
||||
import invariant from "tiny-invariant";
|
||||
import { FormHelperText, FormLabel, Heading, Text } from "tw-components";
|
||||
import {
|
||||
useConstructorParamsFromABI,
|
||||
useContractEnabledExtensions,
|
||||
useContractFullPublishMetadata,
|
||||
useContractPublishMetadataFromURI,
|
||||
@@ -123,9 +123,9 @@ const CustomContractForm: React.FC<CustomContractFormProps> = ({
|
||||
|
||||
const compilerMetadata = useContractPublishMetadataFromURI(ipfsHash);
|
||||
const fullPublishMetadata = useContractFullPublishMetadata(ipfsHash);
|
||||
const constructorParams = useConstructorParamsFromABI(
|
||||
compilerMetadata.data?.abi,
|
||||
);
|
||||
const constructorParams =
|
||||
compilerMetadata.data?.abi?.find((c) => c.type === "constructor")?.inputs ||
|
||||
[];
|
||||
|
||||
const [customFactoryNetwork, customFactoryAddress] = Object.entries(
|
||||
fullPublishMetadata.data?.factoryDeploymentData?.customFactoryInput
|
||||
@@ -141,10 +141,10 @@ const CustomContractForm: React.FC<CustomContractFormProps> = ({
|
||||
fullPublishMetadata.data?.publisher === "deployer.thirdweb.eth";
|
||||
|
||||
const initializerParams = useFunctionParamsFromABI(
|
||||
fullPublishMetadata.data?.deployType === "customFactory" &&
|
||||
customFactoryAbi?.data
|
||||
(fullPublishMetadata.data?.deployType === "customFactory" &&
|
||||
customFactoryAbi?.data
|
||||
? customFactoryAbi.data
|
||||
: compilerMetadata.data?.abi,
|
||||
: compilerMetadata.data?.abi) as Abi | undefined,
|
||||
fullPublishMetadata.data?.deployType === "customFactory"
|
||||
? fullPublishMetadata.data?.factoryDeploymentData?.customFactoryInput
|
||||
?.factoryFunction || "deployProxyByImplementation"
|
||||
@@ -168,9 +168,8 @@ const CustomContractForm: React.FC<CustomContractFormProps> = ({
|
||||
isQueryEnabled: isModular,
|
||||
});
|
||||
|
||||
const deployParams = isFactoryDeployment
|
||||
? initializerParams
|
||||
: constructorParams;
|
||||
const deployParams =
|
||||
(isFactoryDeployment ? initializerParams : constructorParams) || [];
|
||||
|
||||
const isAccountFactory =
|
||||
!isFactoryDeployment &&
|
||||
@@ -319,7 +318,9 @@ const CustomContractForm: React.FC<CustomContractFormProps> = ({
|
||||
return false;
|
||||
};
|
||||
|
||||
const extensions = useContractEnabledExtensions(compilerMetadata.data?.abi);
|
||||
const extensions = useContractEnabledExtensions(
|
||||
compilerMetadata.data?.abi as Abi | undefined,
|
||||
);
|
||||
const isErc721SharedMetadadata = extensions.some(
|
||||
(extension) => extension.name === "ERC721SharedMetadata",
|
||||
);
|
||||
|
||||
@@ -17,7 +17,7 @@ interface ParamProps {
|
||||
};
|
||||
deployParam?: {
|
||||
type: string;
|
||||
name: string;
|
||||
name?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+9
-10
@@ -1,5 +1,5 @@
|
||||
import { Flex } from "@chakra-ui/react";
|
||||
import type { Abi } from "@thirdweb-dev/sdk";
|
||||
import type { Abi } from "abitype";
|
||||
import { Select } from "chakra-react-select";
|
||||
import { useMemo } from "react";
|
||||
|
||||
@@ -16,16 +16,15 @@ export const AbiSelector: React.FC<AbiSelectorProps> = ({
|
||||
defaultValue,
|
||||
onChange,
|
||||
}) => {
|
||||
const writeFunctions = abi.filter(
|
||||
(f) => f.type === "function" && f.stateMutability !== "view",
|
||||
);
|
||||
|
||||
const options = useMemo(() => {
|
||||
return writeFunctions.map((f) => ({
|
||||
label: f.name,
|
||||
value: f.name,
|
||||
}));
|
||||
}, [writeFunctions]);
|
||||
return abi
|
||||
.filter((f) => f.type === "function")
|
||||
.filter((f) => f.stateMutability !== "view")
|
||||
.map((f) => ({
|
||||
label: f.name,
|
||||
value: f.name,
|
||||
}));
|
||||
}, [abi]);
|
||||
|
||||
return (
|
||||
<Flex gap={2} alignItems="center" w="full">
|
||||
|
||||
+2
-7
@@ -9,6 +9,7 @@ import {
|
||||
Tooltip,
|
||||
useBreakpointValue,
|
||||
} from "@chakra-ui/react";
|
||||
import type { AbiParameter } from "abitype";
|
||||
import { SolidityInput } from "contract-ui/components/solidity-inputs";
|
||||
import { camelToTitle } from "contract-ui/components/solidity-inputs/helpers";
|
||||
import { getTemplateValuesForType } from "lib/deployment/template-values";
|
||||
@@ -23,15 +24,9 @@ import {
|
||||
Heading,
|
||||
Text,
|
||||
} from "tw-components";
|
||||
import type {
|
||||
useConstructorParamsFromABI,
|
||||
useFunctionParamsFromABI,
|
||||
} from "../hooks";
|
||||
|
||||
interface ContractParamsFieldsetProps {
|
||||
deployParams:
|
||||
| ReturnType<typeof useFunctionParamsFromABI>
|
||||
| ReturnType<typeof useConstructorParamsFromABI>;
|
||||
deployParams: readonly AbiParameter[];
|
||||
}
|
||||
export const ContractParamsFieldset: React.FC<ContractParamsFieldsetProps> = ({
|
||||
deployParams,
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import {
|
||||
ListItem,
|
||||
UnorderedList,
|
||||
} from "@chakra-ui/react";
|
||||
import type { Abi } from "@thirdweb-dev/sdk";
|
||||
import type { Abi } from "abitype";
|
||||
import { type Dispatch, type SetStateAction, useEffect } from "react";
|
||||
import { Controller, useFieldArray, useFormContext } from "react-hook-form";
|
||||
import { FiPlus, FiTrash } from "react-icons/fi";
|
||||
|
||||
+2
-8
@@ -1,11 +1,7 @@
|
||||
import { Flex, FormControl, ListItem, UnorderedList } from "@chakra-ui/react";
|
||||
import type { Abi } from "@thirdweb-dev/sdk";
|
||||
import type { Abi, AbiParameter } from "abitype";
|
||||
import { useFormContext } from "react-hook-form";
|
||||
import { Heading, Link, Text } from "tw-components";
|
||||
import type {
|
||||
useConstructorParamsFromABI,
|
||||
useFunctionParamsFromABI,
|
||||
} from "../hooks";
|
||||
import { AbiSelector } from "./abi-selector";
|
||||
import { DynamicContractsFieldset } from "./dynamic-contract-fieldset";
|
||||
import { ModularContractsFieldset } from "./modular-contract-fieldset";
|
||||
@@ -17,9 +13,7 @@ interface DefaultFactoryProps {
|
||||
shouldShowDynamicFactoryInput: boolean;
|
||||
shouldShowModularFactoryInput: boolean;
|
||||
shouldShowModulesParamInput: boolean;
|
||||
deployParams:
|
||||
| ReturnType<typeof useFunctionParamsFromABI>
|
||||
| ReturnType<typeof useConstructorParamsFromABI>;
|
||||
deployParams: readonly AbiParameter[];
|
||||
}
|
||||
|
||||
export const DefaultFactory: React.FC<DefaultFactoryProps> = ({
|
||||
|
||||
+2
-8
@@ -1,12 +1,8 @@
|
||||
import { ButtonGroup, Flex } from "@chakra-ui/react";
|
||||
import type { Abi } from "@thirdweb-dev/sdk";
|
||||
import type { Abi, AbiParameter } from "abitype";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import { useFormContext } from "react-hook-form";
|
||||
import { Button, Heading } from "tw-components";
|
||||
import type {
|
||||
useConstructorParamsFromABI,
|
||||
useFunctionParamsFromABI,
|
||||
} from "../hooks";
|
||||
import { CustomFactory } from "./custom-factory";
|
||||
import { DefaultFactory } from "./default-factory";
|
||||
|
||||
@@ -16,9 +12,7 @@ interface FactoryFieldsetProps {
|
||||
shouldShowDynamicFactoryInput: boolean;
|
||||
shouldShowModularFactoryInput: boolean;
|
||||
shouldShowModulesParamInput: boolean;
|
||||
deployParams:
|
||||
| ReturnType<typeof useFunctionParamsFromABI>
|
||||
| ReturnType<typeof useConstructorParamsFromABI>;
|
||||
deployParams: readonly AbiParameter[];
|
||||
}
|
||||
|
||||
export const FactoryFieldset: React.FC<FactoryFieldsetProps> = ({
|
||||
|
||||
@@ -4,12 +4,12 @@ import { useIsomorphicLayoutEffect } from "@/lib/useIsomorphicLayoutEffect";
|
||||
import { CustomConnectWallet } from "@3rdweb-sdk/react/components/connect-wallet";
|
||||
import { Box, Divider, Flex, Icon, IconButton } from "@chakra-ui/react";
|
||||
import {
|
||||
type Abi,
|
||||
CONTRACT_ADDRESSES,
|
||||
ExtraPublishMetadataSchemaInput,
|
||||
detectFeatures,
|
||||
isExtensionEnabled,
|
||||
} from "@thirdweb-dev/sdk";
|
||||
import type { Abi } from "abitype";
|
||||
import { defaultChains } from "constants/chains";
|
||||
import { useTrack } from "hooks/analytics/useTrack";
|
||||
import { useTxNotifications } from "hooks/useTxNotifications";
|
||||
@@ -21,7 +21,6 @@ import { useActiveAccount } from "thirdweb/react";
|
||||
import { Button, Text } from "tw-components";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
useConstructorParamsFromABI,
|
||||
useContractFullPublishMetadata,
|
||||
useContractPrePublishMetadata,
|
||||
useContractPublishMetadataFromURI,
|
||||
@@ -212,14 +211,14 @@ export const ContractPublishForm: React.FC<ContractPublishFormProps> = ({
|
||||
);
|
||||
|
||||
const fullPublishMetadata = useContractFullPublishMetadata(contractId);
|
||||
const constructorParams = useConstructorParamsFromABI(
|
||||
publishMetadata.data?.abi,
|
||||
);
|
||||
const constructorParams =
|
||||
publishMetadata.data?.abi?.find((c) => c.type === "constructor")?.inputs ||
|
||||
[];
|
||||
|
||||
const initializerParams = useFunctionParamsFromABI(
|
||||
form.watch("deployType") === "customFactory"
|
||||
(form.watch("deployType") === "customFactory"
|
||||
? customFactoryAbi
|
||||
: publishMetadata.data?.abi,
|
||||
: publishMetadata.data?.abi) as Abi,
|
||||
form.watch("deployType") === "customFactory"
|
||||
? form.watch(
|
||||
"factoryDeploymentData.customFactoryInput.factoryFunction",
|
||||
@@ -238,12 +237,12 @@ export const ContractPublishForm: React.FC<ContractPublishFormProps> = ({
|
||||
? constructorParams
|
||||
: initializerParams;
|
||||
|
||||
const extensions = detectFeatures(publishMetadata.data?.abi as Abi);
|
||||
const extensions = detectFeatures(publishMetadata.data?.abi || []);
|
||||
|
||||
const isPluginRouter = useMemo(
|
||||
() =>
|
||||
isExtensionEnabled(
|
||||
publishMetadata.data?.abi as Abi,
|
||||
publishMetadata.data?.abi || [],
|
||||
"PluginRouter",
|
||||
extensions,
|
||||
),
|
||||
@@ -253,7 +252,7 @@ export const ContractPublishForm: React.FC<ContractPublishFormProps> = ({
|
||||
const isDynamicContract = useMemo(
|
||||
() =>
|
||||
isExtensionEnabled(
|
||||
publishMetadata.data?.abi as Abi,
|
||||
publishMetadata.data?.abi || [],
|
||||
"DynamicContract",
|
||||
extensions,
|
||||
),
|
||||
@@ -263,7 +262,7 @@ export const ContractPublishForm: React.FC<ContractPublishFormProps> = ({
|
||||
const isModularContract = useMemo(
|
||||
() =>
|
||||
isExtensionEnabled(
|
||||
publishMetadata.data?.abi as Abi,
|
||||
publishMetadata.data?.abi || [],
|
||||
"ModularCore",
|
||||
extensions,
|
||||
),
|
||||
@@ -445,7 +444,7 @@ export const ContractPublishForm: React.FC<ContractPublishFormProps> = ({
|
||||
{fieldsetToShow === "factory" && (
|
||||
<Flex flexDir="column" gap={24}>
|
||||
<FactoryFieldset
|
||||
abi={publishMetadata.data?.abi || []}
|
||||
abi={(publishMetadata.data?.abi || []) as Abi}
|
||||
setCustomFactoryAbi={setCustomFactoryAbi}
|
||||
shouldShowDynamicFactoryInput={shouldShowDynamicFactoryInput}
|
||||
shouldShowModularFactoryInput={shouldShowModularFactoryInput}
|
||||
|
||||
+3
-8
@@ -6,16 +6,11 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import type { AbiParameter } from "abitype";
|
||||
import type React from "react";
|
||||
import type {
|
||||
useConstructorParamsFromABI,
|
||||
useFunctionParamsFromABI,
|
||||
} from "../hooks";
|
||||
|
||||
interface ModulesParamSelectorProps {
|
||||
deployParams:
|
||||
| ReturnType<typeof useFunctionParamsFromABI>
|
||||
| ReturnType<typeof useConstructorParamsFromABI>;
|
||||
deployParams: readonly AbiParameter[];
|
||||
value: string;
|
||||
onChange: (fn: string) => void;
|
||||
}
|
||||
@@ -34,7 +29,7 @@ export const ModulesParamSelector: React.FC<ModulesParamSelectorProps> = ({
|
||||
<SelectGroup>
|
||||
{deployParams.map((param) => {
|
||||
return (
|
||||
<SelectItem key={param.name} value={param.name}>
|
||||
<SelectItem key={param.name} value={param.name || ""}>
|
||||
{param.name}
|
||||
</SelectItem>
|
||||
);
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
} from "@tanstack/react-query";
|
||||
import { useSDK, useSigner } from "@thirdweb-dev/react";
|
||||
import {
|
||||
type Abi,
|
||||
type DeploymentTransaction,
|
||||
type ExtraPublishMetadata,
|
||||
type FeatureName,
|
||||
@@ -20,10 +19,6 @@ import {
|
||||
type PublishedContract,
|
||||
type ThirdwebSDK,
|
||||
detectFeatures,
|
||||
extractConstructorParamsFromAbi,
|
||||
extractEventsFromAbi,
|
||||
extractFunctionParamsFromAbi,
|
||||
extractFunctionsFromAbi,
|
||||
fetchAndCacheDeployMetadata,
|
||||
fetchContractMetadata,
|
||||
fetchPreDeployMetadata,
|
||||
@@ -35,6 +30,7 @@ import {
|
||||
getZkTransactionsForDeploy,
|
||||
zkDeployContractFromUri,
|
||||
} from "@thirdweb-dev/sdk/evm/zksync";
|
||||
import type { Abi } from "abitype";
|
||||
import type { ProfileMetadata, ProfileMetadataInput } from "constants/schemas";
|
||||
import type { providers } from "ethers";
|
||||
import { useSupportedChain } from "hooks/chains/configureChains";
|
||||
@@ -43,6 +39,7 @@ import { getDashboardChainRpc } from "lib/rpc";
|
||||
import { StorageSingleton, getThirdwebSDK } from "lib/sdk";
|
||||
import { useRouter } from "next/router";
|
||||
import { useMemo } from "react";
|
||||
import { getContract } from "thirdweb";
|
||||
import {
|
||||
abstractTestnet,
|
||||
polygon,
|
||||
@@ -50,6 +47,7 @@ import {
|
||||
zkSync,
|
||||
zkSyncSepolia,
|
||||
} from "thirdweb/chains";
|
||||
import { resolveContractAbi } from "thirdweb/contract";
|
||||
import {
|
||||
useActiveAccount,
|
||||
useActiveWallet,
|
||||
@@ -58,6 +56,8 @@ import {
|
||||
import { isAddress } from "thirdweb/utils";
|
||||
import invariant from "tiny-invariant";
|
||||
import { Web3Provider } from "zksync-ethers";
|
||||
import { thirdwebClient } from "../../@/constants/client";
|
||||
import { useV5DashboardChain } from "../../lib/v5-adapter";
|
||||
import type { CustomContractDeploymentFormData } from "./contract-deploy-form/custom-contract";
|
||||
import type {
|
||||
DeployModalStep,
|
||||
@@ -439,12 +439,10 @@ export function usePublishedContractFunctions(contract: PublishedContract) {
|
||||
"MarketplaceV3",
|
||||
))
|
||||
) {
|
||||
return extractFunctionsFromAbi(compositeAbi);
|
||||
return compositeAbi.filter((f) => f.type === "function");
|
||||
}
|
||||
|
||||
return meta
|
||||
? extractFunctionsFromAbi(meta.abi as Abi, meta?.compilerMetadata)
|
||||
: undefined;
|
||||
return meta?.abi?.filter((f) => f.type === "function") || [];
|
||||
}
|
||||
export function usePublishedContractEvents(contract: PublishedContract) {
|
||||
const publishedContractInfo = usePublishedContractInfo(contract);
|
||||
@@ -466,12 +464,10 @@ export function usePublishedContractEvents(contract: PublishedContract) {
|
||||
"MarketplaceV3",
|
||||
))
|
||||
) {
|
||||
return extractEventsFromAbi(compositeAbi);
|
||||
return compositeAbi.filter((f) => f.type === "event");
|
||||
}
|
||||
|
||||
return meta
|
||||
? extractEventsFromAbi(meta.abi as Abi, meta?.compilerMetadata)
|
||||
: undefined;
|
||||
return meta?.abi?.filter((f) => f.type === "event") || [];
|
||||
}
|
||||
|
||||
export function usePublishedContractCompilerMetadata(
|
||||
@@ -480,17 +476,12 @@ export function usePublishedContractCompilerMetadata(
|
||||
return useContractPublishMetadataFromURI(contract.metadataUri);
|
||||
}
|
||||
|
||||
export function useConstructorParamsFromABI(abi?: Abi) {
|
||||
return useMemo(() => {
|
||||
return abi ? extractConstructorParamsFromAbi(abi) : [];
|
||||
}, [abi]);
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: FIXME
|
||||
export function useFunctionParamsFromABI(abi?: any, functionName?: string) {
|
||||
export function useFunctionParamsFromABI(abi?: Abi, functionName?: string) {
|
||||
return useMemo(() => {
|
||||
return abi && functionName
|
||||
? extractFunctionParamsFromAbi(abi, functionName)
|
||||
? abi
|
||||
.filter((a) => a.type === "function")
|
||||
.find((a) => a.name === functionName)?.inputs || []
|
||||
: [];
|
||||
}, [abi, functionName]);
|
||||
}
|
||||
@@ -991,7 +982,9 @@ export async function fetchPublishedContractsWithFeature(
|
||||
);
|
||||
|
||||
return resultWithFeature.filter((c) => {
|
||||
// @ts-expect-error - this is the "wrong" abi type, but it works fine
|
||||
const extensions = detectFeatures(c.deployMetadata.abi as Abi);
|
||||
// @ts-expect-error - this is the "wrong" abi type, but it works fine
|
||||
return isExtensionEnabled(c.deployMetadata.abi as Abi, feature, extensions);
|
||||
});
|
||||
}
|
||||
@@ -1063,10 +1056,10 @@ function extractExtensions(
|
||||
};
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: FIXME
|
||||
function useContractDetectedExtensions(abi?: any) {
|
||||
function useContractDetectedExtensions(abi?: Abi) {
|
||||
const features = useMemo(() => {
|
||||
if (abi) {
|
||||
// @ts-expect-error - this is the "wrong" abi type, but it works fine
|
||||
return extractExtensions(detectFeatures(abi));
|
||||
}
|
||||
return undefined;
|
||||
@@ -1074,8 +1067,7 @@ function useContractDetectedExtensions(abi?: any) {
|
||||
return features;
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: FIXME
|
||||
export function useContractEnabledExtensions(abi?: any) {
|
||||
export function useContractEnabledExtensions(abi?: Abi) {
|
||||
const extensions = useContractDetectedExtensions(abi);
|
||||
return extensions ? extensions.enabledExtensions : [];
|
||||
}
|
||||
@@ -1139,22 +1131,31 @@ export function useEns(addressOrEnsName?: string) {
|
||||
}
|
||||
|
||||
export function useContractFunctions(abi: Abi) {
|
||||
return abi ? extractFunctionsFromAbi(abi) : undefined;
|
||||
return abi
|
||||
.filter((a) => a.type === "function")
|
||||
.map((f) => ({
|
||||
...f,
|
||||
// fake "field" for the "signature"
|
||||
signature: `${f.name}(${f.inputs.map((i) => i.type).join(",")})`,
|
||||
}));
|
||||
}
|
||||
|
||||
export function useContractEvents(abi: Abi) {
|
||||
return abi ? extractEventsFromAbi(abi) : undefined;
|
||||
return abi.filter((a) => a.type === "event");
|
||||
}
|
||||
|
||||
export function useCustomFactoryAbi(contractAddress: string, chainId: number) {
|
||||
const chain = useSupportedChain(chainId);
|
||||
const chain = useV5DashboardChain(chainId);
|
||||
const contract = useMemo(() => {
|
||||
return getContract({
|
||||
client: thirdwebClient,
|
||||
address: contractAddress,
|
||||
chain,
|
||||
});
|
||||
}, [contractAddress, chain]);
|
||||
return useQuery({
|
||||
queryKey: ["custom-factory-abi", { contractAddress, chainId }],
|
||||
queryFn: async () => {
|
||||
const sdk = getThirdwebSDK(chainId, getDashboardChainRpc(chainId, chain));
|
||||
return (await sdk.getContract(contractAddress)).abi;
|
||||
},
|
||||
|
||||
enabled: !!contractAddress && !!chainId,
|
||||
queryKey: ["custom-factory-abi", contract],
|
||||
queryFn: () => resolveContractAbi<Abi>(contract),
|
||||
enabled: !!contract,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
type PublishedMetadata,
|
||||
fetchSourceFilesFromMetadata,
|
||||
} from "@thirdweb-dev/sdk";
|
||||
import type { Abi } from "abitype";
|
||||
import { ContractFunctionsOverview } from "components/contract-functions/contract-functions";
|
||||
import { replaceDeployerAddress } from "components/explore/publisher";
|
||||
import { ShareButton } from "components/share-buttom";
|
||||
@@ -101,7 +102,9 @@ export const PublishedContract: React.FC<PublishedContractProps> = ({
|
||||
? compositeAbi
|
||||
: contractPublishMetadata.data?.abi;
|
||||
|
||||
const enabledExtensions = useContractEnabledExtensions(abi);
|
||||
const enabledExtensions = useContractEnabledExtensions(
|
||||
abi as Abi | undefined,
|
||||
);
|
||||
|
||||
const publisherProfile = usePublisherProfile(contract.publisher);
|
||||
|
||||
@@ -333,9 +336,12 @@ Deploy it in one click`,
|
||||
{contractFunctions && (
|
||||
<Card p={0}>
|
||||
<ContractFunctionsOverview
|
||||
// @ts-expect-error - wrong function type
|
||||
functions={contractFunctions}
|
||||
// @ts-expect-error - wrong event type
|
||||
events={contractEvents}
|
||||
sources={sources.data}
|
||||
// @ts-expect-error - wrong contract type
|
||||
abi={contractPublishMetadata.data?.abi}
|
||||
/>
|
||||
</Card>
|
||||
@@ -457,6 +463,7 @@ Deploy it in one click`,
|
||||
<Divider />
|
||||
{contractPublishMetadata.data?.abi && (
|
||||
<Extensions
|
||||
// @ts-expect-error - wrong abi type
|
||||
abi={
|
||||
compositeAbi &&
|
||||
(dynamicContractType === "plugin" ||
|
||||
|
||||
@@ -20,14 +20,8 @@ import {
|
||||
Thead,
|
||||
Tr,
|
||||
} from "@chakra-ui/react";
|
||||
import {
|
||||
type AbiEvent,
|
||||
type AbiFunction,
|
||||
extractFunctionsFromAbi,
|
||||
joinABIs,
|
||||
} from "@thirdweb-dev/sdk";
|
||||
import type { Abi, AbiEvent, AbiFunction } from "abitype";
|
||||
import { useContractEnabledExtensions } from "components/contract-components/hooks";
|
||||
import { MarkdownRenderer } from "components/contract-components/published-contract/markdown-renderer";
|
||||
import { camelToTitle } from "contract-ui/components/solidity-inputs/helpers";
|
||||
import { useRouter } from "next/router";
|
||||
import { type Dispatch, type SetStateAction, useMemo, useState } from "react";
|
||||
@@ -41,6 +35,48 @@ import { CodeSegment } from "../contract-tabs/code/CodeSegment";
|
||||
import type { CodeEnvironment } from "../contract-tabs/code/types";
|
||||
import { InteractiveAbiFunction } from "./interactive-abi-function";
|
||||
|
||||
// internal helpers
|
||||
export function unique<T>(a: T[], fn: (a: T, b: T) => boolean): T[] {
|
||||
if (a.length === 0 || a.length === 1) {
|
||||
return a;
|
||||
}
|
||||
if (!fn) {
|
||||
return a;
|
||||
}
|
||||
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
for (let j = i + 1; j < a.length; j++) {
|
||||
if (fn(a[i], a[j])) {
|
||||
a.splice(j, 1);
|
||||
j--;
|
||||
}
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
function joinABIs(abis: Abi[], abiWithConstructor?: Abi): Abi {
|
||||
const filteredABIs: Abi[] = abis.map((abi) =>
|
||||
abi.filter((item) => item.type !== "constructor"),
|
||||
);
|
||||
|
||||
if (abiWithConstructor) {
|
||||
filteredABIs.push(abiWithConstructor);
|
||||
}
|
||||
const flattenedAbis = filteredABIs.flat();
|
||||
|
||||
const finalABIs = unique(flattenedAbis, (a, b): boolean => {
|
||||
return (
|
||||
"name" in a &&
|
||||
"name" in b &&
|
||||
a.name === b.name &&
|
||||
a.type === b.type &&
|
||||
a.inputs.length === b.inputs.length
|
||||
);
|
||||
});
|
||||
|
||||
return finalABIs;
|
||||
}
|
||||
|
||||
interface ContractFunctionProps {
|
||||
fn?: AbiFunction | AbiEvent;
|
||||
contract?: ThirdwebContract;
|
||||
@@ -96,7 +132,8 @@ const ContractFunction: React.FC<ContractFunctionProps> = ({
|
||||
</Badge>
|
||||
)}
|
||||
</Flex>
|
||||
{fn.comment && (
|
||||
{/* TODO: bring this back eventually */}
|
||||
{/* {fn.comment && (
|
||||
<MarkdownRenderer
|
||||
markdownText={fn.comment
|
||||
?.replaceAll(/See \{(.+)\}(\.)?/gm, "")
|
||||
@@ -104,7 +141,7 @@ const ContractFunction: React.FC<ContractFunctionProps> = ({
|
||||
.replaceAll("}", '"')
|
||||
.replaceAll("'", '"')}
|
||||
/>
|
||||
)}
|
||||
)} */}
|
||||
{fn.inputs?.length && !contract ? (
|
||||
<>
|
||||
<Divider my={2} />
|
||||
@@ -176,28 +213,30 @@ const ContractFunction: React.FC<ContractFunctionProps> = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
<Heading size="subtitle.md" mt={6}>
|
||||
Use this function in your app
|
||||
</Heading>
|
||||
<Divider mb={2} />
|
||||
{contract && (
|
||||
<CodeSegment
|
||||
environment={environment}
|
||||
setEnvironment={setEnvironment}
|
||||
snippet={formatSnippet(
|
||||
COMMANDS[
|
||||
isFunction ? (isRead ? "read" : "write") : "events"
|
||||
// biome-ignore lint/suspicious/noExplicitAny: FIXME
|
||||
] as any,
|
||||
{
|
||||
contractAddress: contract.address,
|
||||
fn,
|
||||
args: fn.inputs?.map((i) => i.name),
|
||||
chainId: contract.chain.id,
|
||||
extensionNamespace,
|
||||
},
|
||||
)}
|
||||
/>
|
||||
<>
|
||||
<Heading size="subtitle.md" mt={6}>
|
||||
Use this function in your app
|
||||
</Heading>
|
||||
<Divider mb={2} />
|
||||
<CodeSegment
|
||||
environment={environment}
|
||||
setEnvironment={setEnvironment}
|
||||
snippet={formatSnippet(
|
||||
COMMANDS[
|
||||
isFunction ? (isRead ? "read" : "write") : "events"
|
||||
// biome-ignore lint/suspicious/noExplicitAny: FIXME
|
||||
] as any,
|
||||
{
|
||||
contractAddress: contract.address,
|
||||
fn,
|
||||
args: fn.inputs?.map((i) => i.name || ""),
|
||||
chainId: contract.chain.id,
|
||||
extensionNamespace,
|
||||
},
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
@@ -219,15 +258,14 @@ export const ContractFunctionsPanel: React.FC<ContractFunctionsPanelProps> = ({
|
||||
}) => {
|
||||
const abiQuery = useResolveContractAbi(contract);
|
||||
const extensions = useContractEnabledExtensions(abiQuery.data);
|
||||
const isFunction = "stateMutability" in fnsOrEvents[0];
|
||||
const functionsWithExtension = useMemo(() => {
|
||||
let allFunctions = fnsOrEvents as AbiFunction[];
|
||||
let allFunctions = fnsOrEvents.filter((f) => f.type === "function");
|
||||
const results: ExtensionFunctions[] = [];
|
||||
const processedFunctions: string[] = [];
|
||||
// biome-ignore lint/complexity/noForEach: FIXME
|
||||
extensions.forEach((ext) => {
|
||||
// biome-ignore lint/suspicious/noExplicitAny: FIXME
|
||||
let functions = extractFunctionsFromAbi(joinABIs(ext.abis as any));
|
||||
// @ts-expect-error - this is the "wrong" abi type
|
||||
let functions = joinABIs(ext.abis).filter((f) => f.type === "function");
|
||||
allFunctions = allFunctions.filter(
|
||||
(fn) => !functions.map((f) => f.name).includes(fn.name),
|
||||
);
|
||||
@@ -251,8 +289,7 @@ export const ContractFunctionsPanel: React.FC<ContractFunctionsPanelProps> = ({
|
||||
.map((e) => {
|
||||
const filteredFunctions = e.functions.filter(
|
||||
(fn) =>
|
||||
(fn as AbiFunction).stateMutability !== "pure" &&
|
||||
(fn as AbiFunction).stateMutability !== "view",
|
||||
fn.stateMutability !== "pure" && fn.stateMutability !== "view",
|
||||
);
|
||||
if (filteredFunctions.length === 0) {
|
||||
return undefined;
|
||||
@@ -332,9 +369,8 @@ export const ContractFunctionsPanel: React.FC<ContractFunctionsPanelProps> = ({
|
||||
)}
|
||||
{e.functions.map((fn) => (
|
||||
<FunctionsOrEventsListItem
|
||||
key={fn.signature}
|
||||
key={`${fn.name}_${fn.type}_${fn.inputs.length}`}
|
||||
fn={fn}
|
||||
isFunction={isFunction}
|
||||
selectedFunction={selectedFunction}
|
||||
setSelectedFunction={setSelectedFunction}
|
||||
/>
|
||||
@@ -398,9 +434,8 @@ export const ContractFunctionsPanel: React.FC<ContractFunctionsPanelProps> = ({
|
||||
<Box px={4} pt={2} overflowX="hidden">
|
||||
{events.map((fn) => (
|
||||
<FunctionsOrEventsListItem
|
||||
key={isFunction ? (fn as AbiFunction).signature : fn.name}
|
||||
key={`${fn.name}_${fn.type}_${fn.inputs.length}`}
|
||||
fn={fn}
|
||||
isFunction={isFunction}
|
||||
selectedFunction={selectedFunction}
|
||||
setSelectedFunction={setSelectedFunction}
|
||||
/>
|
||||
@@ -423,40 +458,26 @@ export const ContractFunctionsPanel: React.FC<ContractFunctionsPanelProps> = ({
|
||||
|
||||
interface FunctionsOrEventsListItemProps {
|
||||
fn: AbiFunction | AbiEvent;
|
||||
isFunction: boolean;
|
||||
selectedFunction: AbiFunction | AbiEvent;
|
||||
setSelectedFunction: Dispatch<SetStateAction<AbiFunction | AbiEvent>>;
|
||||
}
|
||||
|
||||
const FunctionsOrEventsListItem: React.FC<FunctionsOrEventsListItemProps> = ({
|
||||
fn,
|
||||
isFunction,
|
||||
|
||||
selectedFunction,
|
||||
setSelectedFunction,
|
||||
}) => {
|
||||
const isActive =
|
||||
selectedFunction?.name === fn.name &&
|
||||
selectedFunction.inputs?.length === fn.inputs?.length;
|
||||
const router = useRouter();
|
||||
return (
|
||||
<ListItem my={0.5}>
|
||||
<Button
|
||||
size="sm"
|
||||
fontWeight={
|
||||
(isFunction &&
|
||||
(selectedFunction as AbiFunction).signature ===
|
||||
(fn as AbiFunction).signature) ||
|
||||
(!isFunction &&
|
||||
(selectedFunction as AbiEvent).name === (fn as AbiEvent).name)
|
||||
? 600
|
||||
: 400
|
||||
}
|
||||
opacity={
|
||||
(isFunction &&
|
||||
(selectedFunction as AbiFunction).signature ===
|
||||
(fn as AbiFunction).signature) ||
|
||||
(!isFunction &&
|
||||
(selectedFunction as AbiEvent).name === (fn as AbiEvent).name)
|
||||
? 1
|
||||
: 0.65
|
||||
}
|
||||
fontWeight={isActive ? 600 : 400}
|
||||
opacity={isActive ? 1 : 0.65}
|
||||
onClick={() => {
|
||||
setSelectedFunction(fn);
|
||||
const { name } = fn;
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
TabPanels,
|
||||
Tabs,
|
||||
} from "@chakra-ui/react";
|
||||
import type { Abi, AbiEvent, AbiFunction } from "@thirdweb-dev/sdk";
|
||||
import type { Abi, AbiEvent, AbiFunction } from "abitype";
|
||||
import { SourcesPanel } from "components/contract-components/shared/sources-panel";
|
||||
import type { SourceFile } from "components/contract-components/types";
|
||||
import { CodeOverview } from "contract-ui/tabs/code/components/code-overview";
|
||||
@@ -100,7 +100,6 @@ export const ContractFunctionsOverview: React.FC<ContractFunctionsOverview> = ({
|
||||
)}
|
||||
{(sources || abi) && (
|
||||
<TabPanel>
|
||||
{/* @ts-expect-error This won't cause any error and will auto-resolve itself once we have fully migrated to v5 */}
|
||||
<SourcesPanel sources={sources} abi={abi} />
|
||||
</TabPanel>
|
||||
)}
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Input,
|
||||
} from "@chakra-ui/react";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import type { AbiFunction } from "@thirdweb-dev/sdk";
|
||||
import type { AbiFunction, AbiParameter } from "abitype";
|
||||
import { TransactionButton } from "components/buttons/TransactionButton";
|
||||
import { SolidityInput } from "contract-ui/components/solidity-inputs";
|
||||
import { camelToTitle } from "contract-ui/components/solidity-inputs/helpers";
|
||||
@@ -84,14 +84,7 @@ function formatContractCall(
|
||||
key: string;
|
||||
value: string;
|
||||
type: string;
|
||||
components:
|
||||
| {
|
||||
// biome-ignore lint/suspicious/noExplicitAny: FIXME
|
||||
[x: string]: any;
|
||||
type: string;
|
||||
name?: string;
|
||||
}[]
|
||||
| undefined;
|
||||
components: Readonly<AbiParameter[]> | undefined;
|
||||
}[],
|
||||
) {
|
||||
const parsedParams = params
|
||||
@@ -142,7 +135,7 @@ export const InteractiveAbiFunction: React.FC<InteractiveAbiFunctionProps> = ({
|
||||
key: i.name || "key",
|
||||
value: "",
|
||||
type: i.type,
|
||||
components: i.components,
|
||||
components: "components" in i ? i.components : undefined,
|
||||
})) || [],
|
||||
value: "0",
|
||||
},
|
||||
@@ -266,7 +259,7 @@ export const InteractiveAbiFunction: React.FC<InteractiveAbiFunctionProps> = ({
|
||||
<SolidityInput
|
||||
solidityName={item.key}
|
||||
solidityType={item.type}
|
||||
solidityComponents={item.components}
|
||||
// solidityComponents={item.components}
|
||||
{...form.register(`params.${index}.value`)}
|
||||
functionName={abiFunction.name}
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
contractType,
|
||||
getAllDetectedFeatureNames,
|
||||
useContract,
|
||||
} from "@thirdweb-dev/react";
|
||||
import { useContract } from "@thirdweb-dev/react";
|
||||
import { extensionDetectedState } from "components/buttons/ExtensionDetectButton";
|
||||
import { useEns } from "components/contract-components/hooks";
|
||||
import { detectFeatures } from "components/contract-components/utils";
|
||||
@@ -28,6 +24,7 @@ import {
|
||||
} from "thirdweb/extensions/erc20";
|
||||
import { isERC721 } from "thirdweb/extensions/erc721";
|
||||
import { isERC1155 } from "thirdweb/extensions/erc1155";
|
||||
import { contractType } from "thirdweb/extensions/thirdweb";
|
||||
import { useReadContract } from "thirdweb/react";
|
||||
import { useAnalyticsSupportedForChain } from "../../data/analytics/hooks";
|
||||
import { useContractFunctionSelectors } from "./useContractFunctionSelectors";
|
||||
@@ -124,19 +121,19 @@ export function useContractRouteConfig(
|
||||
contractAddress: string,
|
||||
contract: ThirdwebContract,
|
||||
): EnhancedRoute[] {
|
||||
const ensQuery = useEns(contractAddress);
|
||||
const functinSelectorQuery = useContractFunctionSelectors(contract);
|
||||
const contractQuery = useContract(ensQuery.data?.address);
|
||||
const contractTypeQuery = contractType.useQuery(contractAddress);
|
||||
|
||||
const analyticsSupported = useAnalyticsSupportedForChain(
|
||||
contractQuery.contract?.chainId,
|
||||
);
|
||||
|
||||
// ContractTokensPage
|
||||
// new
|
||||
const functionSelectorQuery = useContractFunctionSelectors(contract);
|
||||
const contractTypeQuery = useReadContract(contractType, { contract });
|
||||
const analyticsSupported = useAnalyticsSupportedForChain(contract.chain.id);
|
||||
const isERC721Query = useReadContract(isERC721, { contract });
|
||||
const isERC1155Query = useReadContract(isERC1155, { contract });
|
||||
|
||||
// old
|
||||
const ensQuery = useEns(contractAddress);
|
||||
|
||||
// TODO: remove
|
||||
const contractQuery = useContract(ensQuery.data?.address);
|
||||
// TODO: remove all below
|
||||
const contractData = useMemo(() => {
|
||||
const claimconditionExtensionDetection = extensionDetectedState({
|
||||
contractQuery,
|
||||
@@ -211,10 +208,6 @@ export function useContractRouteConfig(
|
||||
["PermissionsEnumerable"],
|
||||
);
|
||||
|
||||
const detectedFeatureNames = contractQuery.contract?.abi
|
||||
? getAllDetectedFeatureNames(contractQuery.contract.abi)
|
||||
: [];
|
||||
|
||||
return {
|
||||
claimconditionExtensionDetection,
|
||||
detectedAccountFeature,
|
||||
@@ -226,7 +219,6 @@ export function useContractRouteConfig(
|
||||
detectedModularExtension,
|
||||
hasNewClaimConditions,
|
||||
detectedPermissionEnumerable,
|
||||
detectedFeatureNames,
|
||||
};
|
||||
}, [contractQuery]);
|
||||
|
||||
@@ -266,8 +258,18 @@ export function useContractRouteConfig(
|
||||
{contract && (
|
||||
<ContractOverviewPage
|
||||
contract={contract}
|
||||
contractType={contractTypeQuery.data || "custom"}
|
||||
detectedFeatureNames={contractData.detectedFeatureNames}
|
||||
hasDirectListings={
|
||||
contractData.detectedDirectListings === "enabled"
|
||||
}
|
||||
hasEnglishAuctions={
|
||||
contractData.detectedEnglishAuctions === "enabled"
|
||||
}
|
||||
isErc1155={isERC1155Query.data || false}
|
||||
isErc20={isERC20(functionSelectorQuery.data)}
|
||||
isErc721={isERC721Query.data || false}
|
||||
isPermissionsEnumerable={
|
||||
contractData.detectedPermissionEnumerable
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@@ -285,32 +287,22 @@ export function useContractRouteConfig(
|
||||
{
|
||||
title: "Code Snippets",
|
||||
path: "code",
|
||||
component: () => (
|
||||
<>{contract && <LazyContractCodePage contract={contract} />}</>
|
||||
),
|
||||
component: () =>
|
||||
contract ? <LazyContractCodePage contract={contract} /> : null,
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
title: "Explorer",
|
||||
path: "explorer",
|
||||
component: () => (
|
||||
<>
|
||||
{contract && contractQuery.contract?.abi && (
|
||||
<LazyContractExplorerPage
|
||||
contract={contract}
|
||||
abi={contractQuery.contract.abi}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
component: () =>
|
||||
contract ? <LazyContractExplorerPage contract={contract} /> : null,
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
title: "Events",
|
||||
path: "events",
|
||||
component: () => (
|
||||
<>{contract && <LazyContractEventsPage contract={contract} />}</>
|
||||
),
|
||||
component: () =>
|
||||
contract ? <LazyContractEventsPage contract={contract} /> : null,
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
@@ -324,22 +316,19 @@ export function useContractRouteConfig(
|
||||
? "loading"
|
||||
: "disabled",
|
||||
component: () => {
|
||||
return (
|
||||
<>
|
||||
{contract && (
|
||||
<LazyContractEmbedPage
|
||||
contract={contract}
|
||||
ercOrMarketplace={embedType}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
return contract ? (
|
||||
<LazyContractEmbedPage
|
||||
contract={contract}
|
||||
ercOrMarketplace={embedType}
|
||||
/>
|
||||
) : null;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Analytics",
|
||||
path: "analytics",
|
||||
component: LazyContractAnalyticsPage,
|
||||
component: () =>
|
||||
contract ? <LazyContractAnalyticsPage contract={contract} /> : null,
|
||||
isDefault: true,
|
||||
isBeta: true,
|
||||
isEnabled: analyticsSupported.isLoading
|
||||
@@ -364,9 +353,9 @@ export function useContractRouteConfig(
|
||||
{
|
||||
title: "Tokens",
|
||||
path: "tokens",
|
||||
isEnabled: functinSelectorQuery.isLoading
|
||||
isEnabled: functionSelectorQuery.isLoading
|
||||
? "loading"
|
||||
: isERC20(functinSelectorQuery.data)
|
||||
: isERC20(functionSelectorQuery.data)
|
||||
? "enabled"
|
||||
: "disabled",
|
||||
component: () => (
|
||||
@@ -374,9 +363,11 @@ export function useContractRouteConfig(
|
||||
{contract && (
|
||||
<LazyContractTokensPage
|
||||
contract={contract}
|
||||
isERC20={isERC20(functinSelectorQuery.data)}
|
||||
isMintToSupported={isMintToSupported(functinSelectorQuery.data)}
|
||||
isClaimToSupported={isClaimToSupported(functinSelectorQuery.data)}
|
||||
isERC20={isERC20(functionSelectorQuery.data)}
|
||||
isMintToSupported={isMintToSupported(functionSelectorQuery.data)}
|
||||
isClaimToSupported={isClaimToSupported(
|
||||
functionSelectorQuery.data,
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@@ -407,7 +398,7 @@ export function useContractRouteConfig(
|
||||
path: "split",
|
||||
isEnabled: contractTypeQuery.isLoading
|
||||
? "loading"
|
||||
: contractTypeQuery.data === "split"
|
||||
: contractTypeQuery.data === "Split"
|
||||
? "enabled"
|
||||
: "disabled",
|
||||
component: () => (
|
||||
@@ -419,10 +410,12 @@ export function useContractRouteConfig(
|
||||
path: "proposals",
|
||||
isEnabled: contractTypeQuery.isLoading
|
||||
? "loading"
|
||||
: contractTypeQuery.data === "vote"
|
||||
: contractTypeQuery.data === "Vote"
|
||||
? "enabled"
|
||||
: "disabled",
|
||||
component: LazyContractProposalsPage,
|
||||
component: () => (
|
||||
<>{contract && <LazyContractProposalsPage contract={contract} />}</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Claim Conditions",
|
||||
@@ -436,7 +429,7 @@ export function useContractRouteConfig(
|
||||
claimconditionExtensionDetection={
|
||||
contractData.claimconditionExtensionDetection
|
||||
}
|
||||
isERC20={isERC20(functinSelectorQuery.data)}
|
||||
isERC20={isERC20(functionSelectorQuery.data)}
|
||||
hasNewClaimConditions={contractData.hasNewClaimConditions}
|
||||
/>
|
||||
)}
|
||||
@@ -513,22 +506,22 @@ export function useContractRouteConfig(
|
||||
{contract && (
|
||||
<LazyContractSettingsPage
|
||||
contract={contract}
|
||||
isLoading={functinSelectorQuery.isLoading}
|
||||
isLoading={functionSelectorQuery.isLoading}
|
||||
isContractMetadataSupported={[
|
||||
isGetContractMetadataSupported(functinSelectorQuery.data),
|
||||
isSetContractMetadataSupported(functinSelectorQuery.data),
|
||||
isGetContractMetadataSupported(functionSelectorQuery.data),
|
||||
isSetContractMetadataSupported(functionSelectorQuery.data),
|
||||
].every(Boolean)}
|
||||
isPrimarySaleSupported={[
|
||||
isPrimarySaleRecipientSupported(functinSelectorQuery.data),
|
||||
isSetPrimarySaleRecipientSupported(functinSelectorQuery.data),
|
||||
isPrimarySaleRecipientSupported(functionSelectorQuery.data),
|
||||
isSetPrimarySaleRecipientSupported(functionSelectorQuery.data),
|
||||
].every(Boolean)}
|
||||
isRoyaltiesSupported={[
|
||||
isGetDefaultRoyaltyInfoSupported(functinSelectorQuery.data),
|
||||
isSetDefaultRoyaltyInfoSupported(functinSelectorQuery.data),
|
||||
isGetDefaultRoyaltyInfoSupported(functionSelectorQuery.data),
|
||||
isSetDefaultRoyaltyInfoSupported(functionSelectorQuery.data),
|
||||
].every(Boolean)}
|
||||
isPlatformFeesSupported={[
|
||||
isGetPlatformFeeInfoSupported(functinSelectorQuery.data),
|
||||
isSetPlatformFeeInfoSupported(functinSelectorQuery.data),
|
||||
isGetPlatformFeeInfoSupported(functionSelectorQuery.data),
|
||||
isSetPlatformFeeInfoSupported(functionSelectorQuery.data),
|
||||
].every(Boolean)}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
"use client";
|
||||
|
||||
import { useEVMContractInfo } from "@3rdweb-sdk/react/hooks/useActiveChainId";
|
||||
import {
|
||||
Alert,
|
||||
AlertDescription,
|
||||
@@ -28,16 +26,17 @@ import {
|
||||
useUniqueWalletsAnalytics,
|
||||
} from "data/analytics/hooks";
|
||||
import { Suspense, useMemo, useState } from "react";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import { Card, Heading } from "tw-components";
|
||||
import { ThirdwebBarChart } from "../../../@/components/blocks/charts/bar-chart";
|
||||
import { useIsomorphicLayoutEffect } from "../../../@/lib/useIsomorphicLayoutEffect";
|
||||
|
||||
interface ContractAnalyticsPageProps {
|
||||
contractAddress?: string;
|
||||
contract: ThirdwebContract;
|
||||
}
|
||||
|
||||
export const ContractAnalyticsPage: React.FC<ContractAnalyticsPageProps> = ({
|
||||
contractAddress,
|
||||
contract,
|
||||
}) => {
|
||||
const [startDate] = useState(
|
||||
(() => {
|
||||
@@ -52,17 +51,9 @@ export const ContractAnalyticsPage: React.FC<ContractAnalyticsPageProps> = ({
|
||||
window?.scrollTo({ top: 0, behavior: "smooth" });
|
||||
}, []);
|
||||
|
||||
const evmContractInfo = useEVMContractInfo();
|
||||
const analyticsSupported = useAnalyticsSupportedForChain(contract.chain.id);
|
||||
|
||||
const analyticsSupported = useAnalyticsSupportedForChain(
|
||||
evmContractInfo?.chain?.chainId,
|
||||
);
|
||||
|
||||
if (
|
||||
!contractAddress ||
|
||||
!evmContractInfo?.chain?.chainId ||
|
||||
analyticsSupported.isLoading
|
||||
) {
|
||||
if (analyticsSupported.isLoading) {
|
||||
// TODO build a skeleton for this
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
@@ -82,7 +73,7 @@ export const ContractAnalyticsPage: React.FC<ContractAnalyticsPageProps> = ({
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap={6}>
|
||||
{contractAddress && evmContractInfo?.chain && (
|
||||
{contract && (
|
||||
<>
|
||||
<Flex gap={10} direction="column">
|
||||
<Flex direction="column" gap={2}>
|
||||
@@ -98,24 +89,24 @@ export const ContractAnalyticsPage: React.FC<ContractAnalyticsPageProps> = ({
|
||||
</Heading>
|
||||
<Flex gap={4}>
|
||||
<AnalyticsStat
|
||||
chainId={evmContractInfo.chain.chainId}
|
||||
contractAddress={contractAddress}
|
||||
chainId={contract.chain.id}
|
||||
contractAddress={contract.address}
|
||||
// FIXME
|
||||
// eslint-disable-next-line react-compiler/react-compiler
|
||||
useTotal={useTotalWalletsAnalytics}
|
||||
label="Unique Wallets"
|
||||
/>
|
||||
<AnalyticsStat
|
||||
chainId={evmContractInfo.chain.chainId}
|
||||
contractAddress={contractAddress}
|
||||
chainId={contract.chain.id}
|
||||
contractAddress={contract.address}
|
||||
// FIXME
|
||||
// eslint-disable-next-line react-compiler/react-compiler
|
||||
useTotal={useTotalTransactionAnalytics}
|
||||
label="Total Transactions"
|
||||
/>
|
||||
<AnalyticsStat
|
||||
chainId={evmContractInfo.chain.chainId}
|
||||
contractAddress={contractAddress}
|
||||
chainId={contract.chain.id}
|
||||
contractAddress={contract.address}
|
||||
// FIXME
|
||||
// eslint-disable-next-line react-compiler/react-compiler
|
||||
useTotal={useTotalLogsAnalytics}
|
||||
@@ -126,36 +117,36 @@ export const ContractAnalyticsPage: React.FC<ContractAnalyticsPageProps> = ({
|
||||
</Flex>
|
||||
<SimpleGrid columns={{ base: 1, md: 1 }} gap={4}>
|
||||
<UniqueWalletsChart
|
||||
chainId={evmContractInfo.chain.chainId}
|
||||
contractAddress={contractAddress}
|
||||
chainId={contract.chain.id}
|
||||
contractAddress={contract.address}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
/>
|
||||
|
||||
<TotalTransactionsChart
|
||||
chainId={evmContractInfo.chain.chainId}
|
||||
contractAddress={contractAddress}
|
||||
chainId={contract.chain.id}
|
||||
contractAddress={contract.address}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
/>
|
||||
|
||||
<TotalEventsChart
|
||||
chainId={evmContractInfo.chain.chainId}
|
||||
contractAddress={contractAddress}
|
||||
chainId={contract.chain.id}
|
||||
contractAddress={contract.address}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
/>
|
||||
|
||||
<FunctionBreakdownChart
|
||||
chainId={evmContractInfo.chain.chainId}
|
||||
contractAddress={contractAddress}
|
||||
chainId={contract.chain.id}
|
||||
contractAddress={contract.address}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
/>
|
||||
|
||||
<EventBreakdownChart
|
||||
chainId={evmContractInfo.chain.chainId}
|
||||
contractAddress={contractAddress}
|
||||
chainId={contract.chain.id}
|
||||
contractAddress={contract.address}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
/>
|
||||
|
||||
@@ -16,9 +16,12 @@ import {
|
||||
Tabs,
|
||||
useBreakpointValue,
|
||||
} from "@chakra-ui/react";
|
||||
import type { Abi, AbiEvent, AbiFunction } from "@thirdweb-dev/sdk";
|
||||
import { formatAbiItem } from "abitype";
|
||||
import type { Abi as AbiType } from "abitype";
|
||||
import {
|
||||
type Abi,
|
||||
type AbiEvent,
|
||||
type AbiFunction,
|
||||
formatAbiItem,
|
||||
} from "abitype";
|
||||
import {
|
||||
useContractEnabledExtensions,
|
||||
useContractEvents,
|
||||
@@ -33,8 +36,7 @@ import { useActiveAccount } from "thirdweb/react";
|
||||
import { Button, Card, Heading, Link, Text, TrackedLink } from "tw-components";
|
||||
|
||||
interface CodeOverviewProps {
|
||||
// TODO: Remove v4's `Abi` type
|
||||
abi?: Abi | AbiType;
|
||||
abi?: Abi;
|
||||
contractAddress?: string;
|
||||
onlyInstall?: boolean;
|
||||
chainId?: number;
|
||||
@@ -562,7 +564,7 @@ export const CodeOverview: React.FC<CodeOverviewProps> = ({
|
||||
const chainId = useDashboardEVMChainId() || chainIdProp || 1;
|
||||
const chainInfo = useSupportedChain(chainId || -1);
|
||||
|
||||
const functions = useContractFunctions(abi as Abi);
|
||||
const functions = useContractFunctions(abi || []);
|
||||
const events = useContractEvents(abi as Abi);
|
||||
const { readFunctions, writeFunctions } = useMemo(() => {
|
||||
return {
|
||||
@@ -778,15 +780,13 @@ export const CodeOverview: React.FC<CodeOverviewProps> = ({
|
||||
size="sm"
|
||||
fontWeight={
|
||||
tab === "write" &&
|
||||
(write as AbiFunction).signature ===
|
||||
(fn as AbiFunction).signature
|
||||
write?.signature === fn.signature
|
||||
? 600
|
||||
: 400
|
||||
}
|
||||
opacity={
|
||||
tab === "write" &&
|
||||
(write as AbiFunction).signature ===
|
||||
(fn as AbiFunction).signature
|
||||
write?.signature === fn.signature
|
||||
? 1
|
||||
: 0.65
|
||||
}
|
||||
@@ -814,15 +814,13 @@ export const CodeOverview: React.FC<CodeOverviewProps> = ({
|
||||
size="sm"
|
||||
fontWeight={
|
||||
tab === "read" &&
|
||||
(read as AbiFunction).signature ===
|
||||
(fn as AbiFunction).signature
|
||||
read?.signature === fn.signature
|
||||
? 600
|
||||
: 400
|
||||
}
|
||||
opacity={
|
||||
tab === "read" &&
|
||||
(read as AbiFunction).signature ===
|
||||
(fn as AbiFunction).signature
|
||||
read?.signature === fn.signature
|
||||
? 1
|
||||
: 0.65
|
||||
}
|
||||
@@ -900,22 +898,18 @@ export const CodeOverview: React.FC<CodeOverviewProps> = ({
|
||||
contractAddress,
|
||||
fn:
|
||||
tab === "read" ? read : tab === "write" ? write : event,
|
||||
args: (tab === "read"
|
||||
? readFunctions
|
||||
: tab === "write"
|
||||
? writeFunctions
|
||||
: events
|
||||
)
|
||||
?.find(
|
||||
(f) =>
|
||||
f.name ===
|
||||
(tab === "read"
|
||||
? read?.name
|
||||
: tab === "write"
|
||||
? write?.name
|
||||
: event?.name),
|
||||
)
|
||||
?.inputs?.map((i) => i.name),
|
||||
args:
|
||||
abi
|
||||
?.filter(
|
||||
(f) => f.type === "function" || f.type === "event",
|
||||
)
|
||||
?.find((f) =>
|
||||
f.type === "function"
|
||||
? f.name ===
|
||||
(tab === "read" ? read?.name : write?.name)
|
||||
: f.name === event?.name,
|
||||
)
|
||||
?.inputs.map((i) => i.name || "") || [],
|
||||
|
||||
chainId,
|
||||
extensionNamespace,
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
import { Center, Flex, Skeleton } from "@chakra-ui/react";
|
||||
import type { Abi } from "@thirdweb-dev/sdk";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useContractFunctions } from "components/contract-components/hooks";
|
||||
import { ContractFunctionsOverview } from "components/contract-functions/contract-functions";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import { resolveContractAbi } from "thirdweb/contract";
|
||||
import { Heading, Text } from "tw-components";
|
||||
|
||||
interface ContractCodePageProps {
|
||||
contract: ThirdwebContract;
|
||||
abi: Abi;
|
||||
}
|
||||
|
||||
export const ContractExplorerPage: React.FC<ContractCodePageProps> = ({
|
||||
contract,
|
||||
abi,
|
||||
}) => {
|
||||
const functions = useContractFunctions(abi);
|
||||
const contractAbiQuery = useQuery({
|
||||
queryKey: ["contractAbi", contract],
|
||||
queryFn: () => resolveContractAbi(contract),
|
||||
enabled: !!contract,
|
||||
});
|
||||
const functions = useContractFunctions(contractAbiQuery.data || []);
|
||||
return (
|
||||
<Flex direction="column" h="70vh">
|
||||
<Skeleton height="100%" isLoaded={!!contract}>
|
||||
<Skeleton
|
||||
height="100%"
|
||||
isLoaded={!!contract && contractAbiQuery.isSuccess}
|
||||
>
|
||||
{functions && functions.length > 0 ? (
|
||||
<ContractFunctionsOverview
|
||||
onlyFunctions
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Flex, Image, List, ListItem } from "@chakra-ui/react";
|
||||
import type { Abi } from "abitype";
|
||||
import { useContractEnabledExtensions } from "components/contract-components/hooks";
|
||||
import type { ContractInterface } from "ethers";
|
||||
import { Heading, Text, TrackedLink } from "tw-components";
|
||||
|
||||
type ExtensionsProps = {
|
||||
abi: ContractInterface;
|
||||
abi: Abi;
|
||||
};
|
||||
|
||||
export const Extensions: React.FC<ExtensionsProps> = ({ abi }) => {
|
||||
|
||||
@@ -41,26 +41,6 @@ type ListingData =
|
||||
currencyValue: DirectListing["currencyValuePerToken"];
|
||||
});
|
||||
|
||||
type MarketplaceDetailsProps = {
|
||||
contract: ThirdwebContract;
|
||||
features: string[];
|
||||
trackingCategory: string;
|
||||
};
|
||||
|
||||
export const MarketplaceDetails: React.FC<MarketplaceDetailsProps> = ({
|
||||
contract,
|
||||
trackingCategory,
|
||||
features,
|
||||
}) => {
|
||||
return (
|
||||
<MarketplaceV3Details
|
||||
contract={contract}
|
||||
trackingCategory={trackingCategory}
|
||||
features={features}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
type ListingCardsSectionProps = {
|
||||
contract: ThirdwebContract;
|
||||
trackingCategory: string;
|
||||
@@ -193,17 +173,16 @@ const EnglishAuctionCards: React.FC<ListingCardsSectionProps> = ({
|
||||
interface MarketplaceDetailsVersionProps {
|
||||
contract: ThirdwebContract;
|
||||
trackingCategory: string;
|
||||
features: MarketplaceDetailsProps["features"];
|
||||
hasEnglishAuctions: boolean;
|
||||
hasDirectListings: boolean;
|
||||
}
|
||||
|
||||
const MarketplaceV3Details: React.FC<MarketplaceDetailsVersionProps> = ({
|
||||
export const MarketplaceDetails: React.FC<MarketplaceDetailsVersionProps> = ({
|
||||
contract,
|
||||
trackingCategory,
|
||||
features,
|
||||
hasDirectListings,
|
||||
hasEnglishAuctions,
|
||||
}) => {
|
||||
const hasDirectListings = features.includes("DirectListings");
|
||||
const hasEnglishAuctions = features.includes("EnglishAuctions");
|
||||
|
||||
return (
|
||||
<Flex gap={6} flexDirection="column">
|
||||
<Heading size="title.sm">Listings</Heading>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Flex, useBreakpointValue } from "@chakra-ui/react";
|
||||
import { SupplyCards } from "contract-ui/tabs/nfts/components/supply-cards";
|
||||
import { useTabHref } from "contract-ui/utils";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import { getNFTs } from "thirdweb/extensions/erc721";
|
||||
@@ -10,13 +9,11 @@ import { NFTCards } from "./NFTCards";
|
||||
interface NFTDetailsProps {
|
||||
contract: ThirdwebContract;
|
||||
trackingCategory: TrackedLinkProps["category"];
|
||||
features: string[];
|
||||
}
|
||||
|
||||
export const NFTDetails: React.FC<NFTDetailsProps> = ({
|
||||
contract,
|
||||
trackingCategory,
|
||||
features,
|
||||
}) => {
|
||||
const isMobile = useBreakpointValue({ base: true, md: false });
|
||||
const nftsHref = useTabHref("nfts");
|
||||
@@ -32,17 +29,7 @@ export const NFTDetails: React.FC<NFTDetailsProps> = ({
|
||||
?.filter((token) => token.metadata.image || token.metadata.animation_url)
|
||||
.slice(0, isMobile ? 2 : 3) || [];
|
||||
|
||||
const showSupplyCards = [
|
||||
"ERC721ClaimPhasesV1",
|
||||
"ERC721ClaimPhasesV2",
|
||||
"ERC721ClaimConditionsV1",
|
||||
"ERC721ClaimConditionsV2",
|
||||
"ERC721ClaimCustom",
|
||||
].some((type) => features.includes(type));
|
||||
|
||||
return displayableNFTs.length === 0 &&
|
||||
!showSupplyCards &&
|
||||
!nftQuery.isLoading ? null : (
|
||||
return displayableNFTs.length === 0 && !nftQuery.isLoading ? null : (
|
||||
<Flex direction="column" gap={{ base: 3, md: 6 }}>
|
||||
<Flex align="center" justify="space-between" w="full">
|
||||
<Heading size="title.sm">NFT Details</Heading>
|
||||
@@ -59,7 +46,6 @@ export const NFTDetails: React.FC<NFTDetailsProps> = ({
|
||||
View all ->
|
||||
</TrackedLink>
|
||||
</Flex>
|
||||
{showSupplyCards && contract && <SupplyCards contract={contract} />}
|
||||
<NFTCards
|
||||
contractAddress={contract.address}
|
||||
nfts={displayableNFTs}
|
||||
|
||||
@@ -1,488 +0,0 @@
|
||||
import type { ContractType } from "constants/contracts";
|
||||
|
||||
type Item = {
|
||||
title: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
const ALL_GUIDES: Record<string, Item> = {
|
||||
claimErc20TokenNextjs: {
|
||||
title: "Build An ERC20 Token Claim App in React",
|
||||
url: "https://blog.thirdweb.com/guides/claim-erc20-token-nextjs/",
|
||||
},
|
||||
sellYourInGameCurrencyForCryptoInUnity: {
|
||||
title: "Sell An ERC20 Token As In-Game Currency In Unity",
|
||||
url: "https://blog.thirdweb.com/guides/sell-your-in-game-currency-for-crypto-in-unity/",
|
||||
},
|
||||
tokenButtonReact: {
|
||||
title: "Build a Token Minting Button with React",
|
||||
url: "https://blog.thirdweb.com/guides/token-button-react/",
|
||||
},
|
||||
howToSellNftsInACustomErc20Token: {
|
||||
title: "How To Sell NFTs In A Custom ERC20 token",
|
||||
url: "https://blog.thirdweb.com/guides/how-to-sell-nfts-in-a-custom-erc20-token/",
|
||||
},
|
||||
nftDropWithDelayedReveal: {
|
||||
title: "Release an NFT drop with Delayed Reveal",
|
||||
url: "https://blog.thirdweb.com/guides/nft-drop-with-delayed-reveal/",
|
||||
},
|
||||
realeaseAnNftDropWithNoCode: {
|
||||
title: "Release an NFT Drop on your own site without writing any code",
|
||||
url: "https://blog.thirdweb.com/guides/release-an-nft-drop-with-no-code/",
|
||||
},
|
||||
biconomyGaslessGuide: {
|
||||
title: "Create an NFT Drop with Gasless Transactions using Biconomy",
|
||||
url: "https://blog.thirdweb.com/guides/biconomy-gasless-guide/",
|
||||
},
|
||||
acceptCreditCardPayments: {
|
||||
title: "How to accept credit card payments for your NFT drop",
|
||||
url: "https://blog.thirdweb.com/guides/accept-credit-card-payments/",
|
||||
},
|
||||
createGaslessNftDrop: {
|
||||
title: "Create a gasless NFT drop with Next.js and OpenZeppelin",
|
||||
url: "https://blog.thirdweb.com/guides/create-gasless-nft-drop/",
|
||||
},
|
||||
createNftDropWithClaimPhases: {
|
||||
title: "Release an NFT Drop with an Allowlist and Multiple Claim Phases",
|
||||
url: "https://blog.thirdweb.com/guides/create-nft-drop-with-claim-phases/",
|
||||
},
|
||||
createAnMaycCollectionClone: {
|
||||
title: "Build A Mutant Ape Yacht Club (MAYC) NFT Collection Clone",
|
||||
url: "https://blog.thirdweb.com/guides/create-an-mayc-collection-clone/",
|
||||
},
|
||||
howToBatchUpload: {
|
||||
title: "How to use batch upload with an NFT Drop",
|
||||
url: "https://blog.thirdweb.com/guides/how-to-batch-upload/",
|
||||
},
|
||||
releaseAnErc721DropWithCentralizedMetadata: {
|
||||
title: "Create an NFT Drop with Centralized Metadata",
|
||||
url: "https://blog.thirdweb.com/guides/release-an-erc721-drop-with-centralized-metadata/",
|
||||
},
|
||||
createNftWithNoCode: {
|
||||
title: "Mint an NFT with no code",
|
||||
url: "https://blog.thirdweb.com/guides/create-nft-with-no-code/",
|
||||
},
|
||||
howToRenderNftMetadataInAReactAppUsingThirdwebNftMedia: {
|
||||
title: "How to Render NFT Metadata In a React App",
|
||||
url: "https://blog.thirdweb.com/guides/how-to-render-nft-metadata-in-a-react-app-using-thirdwebnftmedia/",
|
||||
},
|
||||
onDemandPass: {
|
||||
title: "Create ERC721 Access Pass NFTs Using TypeScript",
|
||||
url: "https://blog.thirdweb.com/guides/on-demand-pass/",
|
||||
},
|
||||
mintSpecificNft: {
|
||||
title: "Let Users Pick Which NFT They Want to Mint",
|
||||
url: "https://blog.thirdweb.com/guides/mint-specific-nft/",
|
||||
},
|
||||
mintNftUniqueCode: {
|
||||
title:
|
||||
"Create A Community-Made NFT Collection using Signature-Based Minting",
|
||||
url: "https://blog.thirdweb.com/guides/mint-nft-unique-code/",
|
||||
},
|
||||
airdropEditionNftsToHoldersOfAnNftCollection: {
|
||||
title: "Airdrop Edition NFTs to Holders of an NFT Collection",
|
||||
url: "https://blog.thirdweb.com/guides/airdrop-edition-nfts-to-holders-of-an-nft-collection/",
|
||||
},
|
||||
createAnNftGalleryUsingThirdwebAndNextJs: {
|
||||
title: "Create an NFT Gallery using thirdweb and Next.js",
|
||||
url: "https://blog.thirdweb.com/guides/create-an-nft-gallery-using-thirdweb-and-next-js/",
|
||||
},
|
||||
nftGatedWebsite: {
|
||||
title: "Create An NFT Gated Website",
|
||||
url: "https://blog.thirdweb.com/guides/nft-gated-website/",
|
||||
},
|
||||
allowGithubContributorsToMintAnNft: {
|
||||
title: "Allow GitHub contributors to mint an NFT",
|
||||
url: "https://blog.thirdweb.com/guides/allow-github-contributors-to-mint-an-nft/",
|
||||
},
|
||||
airdropFreeToOwnNftsForAWeb3Game: {
|
||||
title: "Airdrop Free-To-Own NFTs For Your Web3 Game",
|
||||
url: "https://blog.thirdweb.com/guides/airdrop-free-to-own-nfts-for-a-web3-game/",
|
||||
},
|
||||
createAnNftLootbox: {
|
||||
title: "Create NFT Loot-Boxes Using the Pack Contract",
|
||||
url: "https://blog.thirdweb.com/guides/create-an-nft-lootbox/",
|
||||
},
|
||||
signatureDrop: {
|
||||
title: "Create an ERC721A NFT Drop with Signature-Based Minting",
|
||||
url: "https://blog.thirdweb.com/guides/signature-drop/",
|
||||
},
|
||||
nftMarketplaceWithTypescriptNext: {
|
||||
title: "Create Your Own NFT Marketplace with TypeScript and Next.js",
|
||||
url: "https://blog.thirdweb.com/guides/nft-marketplace-with-typescript-next/",
|
||||
},
|
||||
auctionButtonReact: {
|
||||
title: "Build An Auction Button For Your NFT Marketplace",
|
||||
url: "https://blog.thirdweb.com/guides/auction-button-react/",
|
||||
},
|
||||
buildTreasuryAndGovernanceForYourDao: {
|
||||
title: "Build a DAO With a Treasury and a Governance Token",
|
||||
url: "https://blog.thirdweb.com/guides/build-treasury-and-governance-for-your-dao/",
|
||||
},
|
||||
deploySmartWallet: {
|
||||
title: "How to Deploy a Smart Account (ERC-4337)",
|
||||
url: "https://blog.thirdweb.com/guides/how-to-use-erc4337-smart-wallets/",
|
||||
},
|
||||
customSmartWallet: {
|
||||
title:
|
||||
"How to Extend the Base Account Abstraction Contracts Using the Solidity SDK",
|
||||
url: "https://blog.thirdweb.com/guides/custom-smart-wallet-contracts/",
|
||||
},
|
||||
};
|
||||
|
||||
const GUIDES_FOR_TYPE: Record<ContractType, Item[]> = {
|
||||
pack: [ALL_GUIDES.createAnNftLootbox],
|
||||
"signature-drop": [ALL_GUIDES.signatureDrop],
|
||||
marketplace: [
|
||||
ALL_GUIDES.nftMarketplaceWithTypescriptNext,
|
||||
ALL_GUIDES.auctionButtonReact,
|
||||
],
|
||||
vote: [ALL_GUIDES.buildTreasuryAndGovernanceForYourDao],
|
||||
custom: [],
|
||||
"edition-drop": [],
|
||||
edition: [],
|
||||
"marketplace-v3": [],
|
||||
multiwrap: [],
|
||||
"nft-collection": [],
|
||||
"nft-drop": [],
|
||||
split: [],
|
||||
"token-drop": [],
|
||||
token: [],
|
||||
};
|
||||
|
||||
const GUIDES_FOR_EXTENSION: Record<string, Item[]> = {
|
||||
ERC20ClaimConditionsV1: [
|
||||
ALL_GUIDES.claimErc20TokenNextjs,
|
||||
ALL_GUIDES.tokenButtonReact,
|
||||
ALL_GUIDES.sellYourInGameCurrencyForCryptoInUnity,
|
||||
],
|
||||
ERC20ClaimConditionsV2: [
|
||||
ALL_GUIDES.claimErc20TokenNextjs,
|
||||
ALL_GUIDES.tokenButtonReact,
|
||||
ALL_GUIDES.sellYourInGameCurrencyForCryptoInUnity,
|
||||
],
|
||||
ERC20ClaimPhasesV2: [
|
||||
ALL_GUIDES.claimErc20TokenNextjs,
|
||||
ALL_GUIDES.tokenButtonReact,
|
||||
ALL_GUIDES.sellYourInGameCurrencyForCryptoInUnity,
|
||||
],
|
||||
ERC20ClaimPhasesV1: [
|
||||
ALL_GUIDES.claimErc20TokenNextjs,
|
||||
ALL_GUIDES.tokenButtonReact,
|
||||
ALL_GUIDES.sellYourInGameCurrencyForCryptoInUnity,
|
||||
],
|
||||
ERC20Burnable: [],
|
||||
ERC20BatchMintable: [],
|
||||
ERC20Mintable: [],
|
||||
ERC20: [ALL_GUIDES.howToSellNftsInACustomErc20Token],
|
||||
ERC721Burnable: [],
|
||||
ERC721Revealable: [ALL_GUIDES.nftDropWithDelayedReveal],
|
||||
ERC721TieredDrop: [],
|
||||
ERC721ClaimConditionsV1: [
|
||||
ALL_GUIDES.realeaseAnNftDropWithNoCode,
|
||||
ALL_GUIDES.biconomyGaslessGuide,
|
||||
ALL_GUIDES.acceptCreditCardPayments,
|
||||
ALL_GUIDES.createGaslessNftDrop,
|
||||
ALL_GUIDES.createNftDropWithClaimPhases,
|
||||
],
|
||||
ERC721ClaimConditionsV2: [
|
||||
ALL_GUIDES.realeaseAnNftDropWithNoCode,
|
||||
ALL_GUIDES.biconomyGaslessGuide,
|
||||
ALL_GUIDES.acceptCreditCardPayments,
|
||||
ALL_GUIDES.createGaslessNftDrop,
|
||||
ALL_GUIDES.createNftDropWithClaimPhases,
|
||||
],
|
||||
ERC721ClaimPhasesV1: [
|
||||
ALL_GUIDES.realeaseAnNftDropWithNoCode,
|
||||
ALL_GUIDES.biconomyGaslessGuide,
|
||||
ALL_GUIDES.acceptCreditCardPayments,
|
||||
ALL_GUIDES.createGaslessNftDrop,
|
||||
ALL_GUIDES.createNftDropWithClaimPhases,
|
||||
],
|
||||
ERC721ClaimPhasesV2: [
|
||||
ALL_GUIDES.realeaseAnNftDropWithNoCode,
|
||||
ALL_GUIDES.biconomyGaslessGuide,
|
||||
ALL_GUIDES.acceptCreditCardPayments,
|
||||
ALL_GUIDES.createGaslessNftDrop,
|
||||
ALL_GUIDES.createNftDropWithClaimPhases,
|
||||
],
|
||||
ERC721ClaimCustom: [],
|
||||
ERC721LazyMintable: [ALL_GUIDES.createAnMaycCollectionClone],
|
||||
ERC721BatchMintable: [
|
||||
ALL_GUIDES.howToBatchUpload,
|
||||
ALL_GUIDES.releaseAnErc721DropWithCentralizedMetadata,
|
||||
],
|
||||
ERC721Mintable: [
|
||||
ALL_GUIDES.createNftWithNoCode,
|
||||
ALL_GUIDES.howToRenderNftMetadataInAReactAppUsingThirdwebNftMedia,
|
||||
],
|
||||
ERC721SignatureMintV2: [
|
||||
ALL_GUIDES.onDemandPass,
|
||||
ALL_GUIDES.mintSpecificNft,
|
||||
ALL_GUIDES.mintNftUniqueCode,
|
||||
],
|
||||
ERC721SignatureMintV1: [
|
||||
ALL_GUIDES.onDemandPass,
|
||||
ALL_GUIDES.mintSpecificNft,
|
||||
ALL_GUIDES.mintNftUniqueCode,
|
||||
],
|
||||
ERC721Enumerable: [],
|
||||
ERC721Supply: [
|
||||
ALL_GUIDES.airdropEditionNftsToHoldersOfAnNftCollection,
|
||||
ALL_GUIDES.createAnNftGalleryUsingThirdwebAndNextJs,
|
||||
],
|
||||
ERC721: [ALL_GUIDES.nftGatedWebsite],
|
||||
ERC1155Burnable: [],
|
||||
ERC1155ClaimConditionsV1: [ALL_GUIDES.nftGatedWebsite],
|
||||
ERC1155ClaimConditionsV2: [ALL_GUIDES.nftGatedWebsite],
|
||||
ERC1155ClaimPhasesV2: [ALL_GUIDES.nftGatedWebsite],
|
||||
ERC1155ClaimPhasesV1: [ALL_GUIDES.nftGatedWebsite],
|
||||
ERC1155ClaimCustom: [],
|
||||
ERC1155Revealable: [ALL_GUIDES.nftDropWithDelayedReveal],
|
||||
ERC1155LazyMintableV2: [],
|
||||
ERC1155LazyMintableV1: [],
|
||||
ERC1155SignatureMintable: [ALL_GUIDES.allowGithubContributorsToMintAnNft],
|
||||
ERC1155BatchMintable: [],
|
||||
ERC1155Mintable: [],
|
||||
ERC1155Enumerable: [ALL_GUIDES.createAnNftGalleryUsingThirdwebAndNextJs],
|
||||
ERC1155UpdatableMetadata: [],
|
||||
ERC1155Supply: [],
|
||||
ERC1155: [ALL_GUIDES.airdropFreeToOwnNftsForAWeb3Game],
|
||||
Royalty: [],
|
||||
PrimarySale: [],
|
||||
PlatformFee: [],
|
||||
PermissionsEnumerable: [],
|
||||
Permissions: [],
|
||||
ContractMetadata: [],
|
||||
AppURI: [],
|
||||
Ownable: [],
|
||||
ERC20SignatureMintable: [],
|
||||
ERC20Permit: [],
|
||||
ERC721AQueryable: [],
|
||||
ERC721ClaimZora: [],
|
||||
ERC721SharedMetadata: [],
|
||||
ERC721LoyaltyCard: [],
|
||||
ERC721UpdatableMetadata: [],
|
||||
Gasless: [],
|
||||
PackVRF: [],
|
||||
PluginRouter: [],
|
||||
DynamicContract: [],
|
||||
ModularCore: [],
|
||||
ModularModule: [],
|
||||
DirectListings: [],
|
||||
EnglishAuctions: [],
|
||||
Offers: [],
|
||||
Account: [],
|
||||
AccountFactory: [ALL_GUIDES.deploySmartWallet, ALL_GUIDES.customSmartWallet],
|
||||
AccountPermissions: [],
|
||||
AccountPermissionsV1: [],
|
||||
AirdropERC20: [],
|
||||
AirdropERC721: [],
|
||||
AirdropERC1155: [],
|
||||
Fallback: [],
|
||||
};
|
||||
|
||||
const ALL_TEMPLATES: Record<string, Item> = {
|
||||
tokenDrop: {
|
||||
title: "Token Drop",
|
||||
url: "https://github.com/thirdweb-example/token-drop",
|
||||
},
|
||||
nftStakingApp: {
|
||||
title: "NFT Staking App",
|
||||
url: "https://github.com/thirdweb-example/nft-staking-app/",
|
||||
},
|
||||
nftDrop: {
|
||||
title: "NFT Drop Minting Page",
|
||||
url: "https://github.com/thirdweb-example/erc721/",
|
||||
},
|
||||
burn1155Mint721: {
|
||||
title: "Burn an ERC1155 to Mint an ERC721 NFT",
|
||||
url: "https://github.com/thirdweb-example/burn1155-mint721",
|
||||
},
|
||||
signatureBasedMinting: {
|
||||
title: "Signature Based Minting",
|
||||
url: "https://github.com/thirdweb-example/signature-based-minting",
|
||||
},
|
||||
nftGatedWebsite: {
|
||||
title: "NFT Gated Website",
|
||||
url: "https://github.com/thirdweb-example/nft-gated-website",
|
||||
},
|
||||
editionDrop: {
|
||||
title: "Edition Drop Minting Page",
|
||||
url: "https://github.com/thirdweb-example/edition-drop",
|
||||
},
|
||||
packs: {
|
||||
title: "Packs",
|
||||
url: "https://github.com/thirdweb-example/packs",
|
||||
},
|
||||
signatureDrop: {
|
||||
title: "Signature Drop",
|
||||
url: "https://github.com/thirdweb-example/signature-drop",
|
||||
},
|
||||
marketplace: {
|
||||
title: "Build your own Marketplace",
|
||||
url: "https://github.com/thirdweb-example/marketplace",
|
||||
},
|
||||
dao: {
|
||||
title: "Build your own DAO",
|
||||
url: "https://github.com/thirdweb-example/dao",
|
||||
},
|
||||
multiwrap: {
|
||||
title: "Multiwrap",
|
||||
url: "https://github.com/thirdweb-example/multiwrap",
|
||||
},
|
||||
smartWalletScript: {
|
||||
title: "Account Abstraction Demo Script",
|
||||
url: "https://github.com/thirdweb-example/smart-wallet-script",
|
||||
},
|
||||
smartWalletReact: {
|
||||
title: "Account Abstraction React Demo",
|
||||
url: "https://github.com/thirdweb-example/smart-wallet-react",
|
||||
},
|
||||
};
|
||||
|
||||
const TEMPLATES_FOR_TYPE: Record<ContractType, Item[]> = {
|
||||
pack: [ALL_TEMPLATES.packs],
|
||||
"signature-drop": [ALL_TEMPLATES.signatureDrop],
|
||||
marketplace: [ALL_TEMPLATES.marketplace],
|
||||
vote: [ALL_TEMPLATES.dao],
|
||||
multiwrap: [ALL_TEMPLATES.multiwrap],
|
||||
"nft-drop": [ALL_TEMPLATES.nftDrop],
|
||||
"edition-drop": [ALL_TEMPLATES.editionDrop, ALL_TEMPLATES.nftGatedWebsite],
|
||||
"token-drop": [ALL_TEMPLATES.tokenDrop],
|
||||
custom: [],
|
||||
edition: [],
|
||||
"marketplace-v3": [],
|
||||
"nft-collection": [],
|
||||
split: [],
|
||||
token: [],
|
||||
};
|
||||
|
||||
const TEMPLATES_FOR_EXTENSION: Record<string, Item[]> = {
|
||||
ERC20ClaimConditionsV1: [ALL_TEMPLATES.tokenDrop],
|
||||
ERC20ClaimConditionsV2: [ALL_TEMPLATES.tokenDrop],
|
||||
ERC20ClaimPhasesV1: [ALL_TEMPLATES.tokenDrop],
|
||||
ERC20ClaimPhasesV2: [ALL_TEMPLATES.tokenDrop],
|
||||
ERC20Burnable: [],
|
||||
ERC20BatchMintable: [],
|
||||
ERC20Mintable: [],
|
||||
ERC20: [ALL_TEMPLATES.nftStakingApp],
|
||||
ERC721Burnable: [],
|
||||
ERC721Revealable: [],
|
||||
ERC721TieredDrop: [],
|
||||
ERC721ClaimConditionsV1: [ALL_TEMPLATES.nftDrop],
|
||||
ERC721ClaimConditionsV2: [ALL_TEMPLATES.nftDrop],
|
||||
ERC721ClaimPhasesV1: [ALL_TEMPLATES.nftDrop],
|
||||
ERC721ClaimPhasesV2: [ALL_TEMPLATES.nftDrop],
|
||||
ERC721ClaimCustom: [],
|
||||
ERC721LazyMintable: [ALL_TEMPLATES.burn1155Mint721],
|
||||
ERC721BatchMintable: [],
|
||||
ERC721Mintable: [],
|
||||
ERC721SignatureMintV2: [ALL_TEMPLATES.signatureBasedMinting],
|
||||
ERC721SignatureMintV1: [ALL_TEMPLATES.signatureBasedMinting],
|
||||
ERC721Enumerable: [],
|
||||
ERC721Supply: [],
|
||||
ERC721: [ALL_TEMPLATES.nftGatedWebsite],
|
||||
ERC1155Burnable: [],
|
||||
ERC1155ClaimConditionsV1: [
|
||||
ALL_TEMPLATES.editionDrop,
|
||||
ALL_TEMPLATES.nftGatedWebsite,
|
||||
],
|
||||
ERC1155ClaimConditionsV2: [
|
||||
ALL_TEMPLATES.editionDrop,
|
||||
ALL_TEMPLATES.nftGatedWebsite,
|
||||
],
|
||||
ERC1155ClaimPhasesV2: [
|
||||
ALL_TEMPLATES.editionDrop,
|
||||
ALL_TEMPLATES.nftGatedWebsite,
|
||||
],
|
||||
ERC1155ClaimPhasesV1: [
|
||||
ALL_TEMPLATES.editionDrop,
|
||||
ALL_TEMPLATES.nftGatedWebsite,
|
||||
],
|
||||
ERC1155ClaimCustom: [],
|
||||
ERC1155Revealable: [],
|
||||
ERC1155LazyMintableV2: [],
|
||||
ERC1155LazyMintableV1: [],
|
||||
ERC1155SignatureMintable: [],
|
||||
ERC1155BatchMintable: [],
|
||||
ERC1155Mintable: [],
|
||||
ERC1155Enumerable: [],
|
||||
ERC1155Supply: [],
|
||||
ERC1155: [],
|
||||
Royalty: [],
|
||||
PrimarySale: [],
|
||||
PlatformFee: [],
|
||||
PermissionsEnumerable: [],
|
||||
Permissions: [],
|
||||
ContractMetadata: [],
|
||||
AppURI: [],
|
||||
Ownable: [],
|
||||
ERC20SignatureMintable: [],
|
||||
ERC20Permit: [],
|
||||
ERC721AQueryable: [],
|
||||
ERC721ClaimZora: [],
|
||||
ERC721SharedMetadata: [],
|
||||
ERC721LoyaltyCard: [],
|
||||
ERC721UpdatableMetadata: [],
|
||||
Gasless: [],
|
||||
PackVRF: [],
|
||||
PluginRouter: [],
|
||||
DynamicContract: [],
|
||||
ModularCore: [],
|
||||
ModularModule: [],
|
||||
DirectListings: [],
|
||||
EnglishAuctions: [],
|
||||
Offers: [],
|
||||
Account: [],
|
||||
AccountFactory: [
|
||||
ALL_TEMPLATES.smartWalletScript,
|
||||
ALL_TEMPLATES.smartWalletReact,
|
||||
],
|
||||
AccountPermissions: [],
|
||||
AccountPermissionsV1: [],
|
||||
ERC1155UpdatableMetadata: [],
|
||||
AirdropERC20: [],
|
||||
AirdropERC721: [],
|
||||
AirdropERC1155: [],
|
||||
Fallback: [],
|
||||
};
|
||||
|
||||
export function getGuidesAndTemplates(
|
||||
features: string[],
|
||||
contractType: ContractType,
|
||||
): {
|
||||
guides: Item[];
|
||||
templates: Item[];
|
||||
} {
|
||||
const data = features.reduce(
|
||||
(acc, guide) => ({
|
||||
guides: [
|
||||
...acc.guides,
|
||||
...(GUIDES_FOR_EXTENSION[guide as keyof typeof GUIDES_FOR_EXTENSION] ||
|
||||
[]),
|
||||
],
|
||||
templates: [
|
||||
...acc.templates,
|
||||
...(TEMPLATES_FOR_EXTENSION[
|
||||
guide as keyof typeof TEMPLATES_FOR_EXTENSION
|
||||
] || []),
|
||||
],
|
||||
}),
|
||||
{
|
||||
guides: GUIDES_FOR_TYPE[contractType] || [],
|
||||
templates: TEMPLATES_FOR_TYPE[contractType] || [],
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
guides: data.guides.filter(
|
||||
(guide, index) =>
|
||||
data.guides.findIndex((g) => g.url === guide.url) === index,
|
||||
),
|
||||
templates: data.templates.filter(
|
||||
(template, index) =>
|
||||
data.templates.findIndex((t) => t.url === template.url) === index,
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -1,80 +1,68 @@
|
||||
import { Divider, Flex, GridItem, SimpleGrid } from "@chakra-ui/react";
|
||||
import { Flex, GridItem, SimpleGrid } from "@chakra-ui/react";
|
||||
import { PublishedBy } from "components/contract-components/shared/published-by";
|
||||
import { RelevantDataSection } from "components/dashboard/RelevantDataSection";
|
||||
import type { ContractType } from "constants/contracts";
|
||||
import { useMemo } from "react";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import { AnalyticsOverview } from "./components/Analytics";
|
||||
import { BuildYourApp } from "./components/BuildYourApp";
|
||||
import { ContractChecklist } from "./components/ContractChecklist";
|
||||
import { Extensions } from "./components/Extensions";
|
||||
import { LatestEvents } from "./components/LatestEvents";
|
||||
import { MarketplaceDetails } from "./components/MarketplaceDetails";
|
||||
import { NFTDetails } from "./components/NFTDetails";
|
||||
import { PermissionsTable } from "./components/PermissionsTable";
|
||||
import { TokenDetails } from "./components/TokenDetails";
|
||||
import { getGuidesAndTemplates } from "./helpers/getGuidesAndTemplates";
|
||||
|
||||
interface ContractOverviewPageProps {
|
||||
contract: ThirdwebContract;
|
||||
contractType: ContractType;
|
||||
detectedFeatureNames: string[];
|
||||
hasEnglishAuctions: boolean;
|
||||
hasDirectListings: boolean;
|
||||
isErc721: boolean;
|
||||
isErc1155: boolean;
|
||||
isErc20: boolean;
|
||||
isPermissionsEnumerable: boolean;
|
||||
}
|
||||
|
||||
const TRACKING_CATEGORY = "contract_overview";
|
||||
|
||||
export const ContractOverviewPage: React.FC<ContractOverviewPageProps> = ({
|
||||
contract,
|
||||
contractType,
|
||||
detectedFeatureNames,
|
||||
}) => {
|
||||
const { guides, templates } = useMemo(
|
||||
() => getGuidesAndTemplates(detectedFeatureNames, contractType),
|
||||
[detectedFeatureNames, contractType],
|
||||
);
|
||||
|
||||
isErc1155,
|
||||
isErc20,
|
||||
isErc721,
|
||||
hasEnglishAuctions,
|
||||
hasDirectListings,
|
||||
isPermissionsEnumerable,
|
||||
}) => {
|
||||
return (
|
||||
<SimpleGrid columns={{ base: 1, xl: 10 }} gap={20}>
|
||||
<GridItem as={Flex} colSpan={{ xl: 7 }} direction="column" gap={16}>
|
||||
{contract && <ContractChecklist contract={contract} />}
|
||||
{contract && (
|
||||
<AnalyticsOverview
|
||||
contractAddress={contract.address}
|
||||
chainId={contract.chain.id}
|
||||
<ContractChecklist contract={contract} />
|
||||
|
||||
<AnalyticsOverview
|
||||
contractAddress={contract.address}
|
||||
chainId={contract.chain.id}
|
||||
trackingCategory={TRACKING_CATEGORY}
|
||||
/>
|
||||
|
||||
{(hasEnglishAuctions || hasDirectListings) && (
|
||||
<MarketplaceDetails
|
||||
contract={contract}
|
||||
trackingCategory={TRACKING_CATEGORY}
|
||||
hasEnglishAuctions={hasEnglishAuctions}
|
||||
hasDirectListings={hasDirectListings}
|
||||
/>
|
||||
)}
|
||||
{(isErc1155 || isErc721) && (
|
||||
<NFTDetails
|
||||
contract={contract}
|
||||
trackingCategory={TRACKING_CATEGORY}
|
||||
/>
|
||||
)}
|
||||
{contract &&
|
||||
(contractType === "marketplace" ||
|
||||
["DirectListings", "EnglishAuctions"].some((type) =>
|
||||
detectedFeatureNames.includes(type),
|
||||
)) && (
|
||||
<MarketplaceDetails
|
||||
contract={contract}
|
||||
trackingCategory={TRACKING_CATEGORY}
|
||||
features={detectedFeatureNames}
|
||||
/>
|
||||
)}
|
||||
{contract &&
|
||||
["ERC1155", "ERC721"].some((type) =>
|
||||
detectedFeatureNames.includes(type),
|
||||
) && (
|
||||
<NFTDetails
|
||||
contract={contract}
|
||||
trackingCategory={TRACKING_CATEGORY}
|
||||
features={detectedFeatureNames}
|
||||
/>
|
||||
)}
|
||||
{["ERC20"].some((type) => detectedFeatureNames.includes(type)) && (
|
||||
<TokenDetails contract={contract} />
|
||||
)}
|
||||
{isErc20 && <TokenDetails contract={contract} />}
|
||||
<LatestEvents
|
||||
contract={contract}
|
||||
trackingCategory={TRACKING_CATEGORY}
|
||||
/>
|
||||
{["PermissionsEnumerable"].some((type) =>
|
||||
detectedFeatureNames.includes(type),
|
||||
) && (
|
||||
{isPermissionsEnumerable && (
|
||||
<PermissionsTable
|
||||
contract={contract}
|
||||
trackingCategory={TRACKING_CATEGORY}
|
||||
@@ -84,20 +72,8 @@ export const ContractOverviewPage: React.FC<ContractOverviewPageProps> = ({
|
||||
</GridItem>
|
||||
<GridItem colSpan={{ xl: 3 }} as={Flex} direction="column" gap={6}>
|
||||
<PublishedBy contractAddress={contract.address} />
|
||||
{contract?.abi && <Extensions abi={contract?.abi} />}
|
||||
{(guides.length > 0 || templates.length > 0) && <Divider />}
|
||||
<RelevantDataSection
|
||||
data={guides}
|
||||
title="guide"
|
||||
TRACKING_CATEGORY={TRACKING_CATEGORY}
|
||||
/>
|
||||
{guides.length > 0 && templates.length > 0 && <Divider />}
|
||||
<RelevantDataSection
|
||||
data={templates}
|
||||
title="template"
|
||||
TRACKING_CATEGORY={TRACKING_CATEGORY}
|
||||
/>
|
||||
{templates.length > 0 && <Divider />}
|
||||
{/* TODO: this is broken because it relies on something that no longer exists on contract instance */}
|
||||
{/* {contract?.abi && <Extensions abi={contract?.abi} />} */}
|
||||
</GridItem>
|
||||
</SimpleGrid>
|
||||
);
|
||||
|
||||
@@ -50,6 +50,7 @@ const ContractDeployDetailPage: ThirdwebNextPage = () => {
|
||||
}, [publishMetadataQuery.data, trackEvent]);
|
||||
|
||||
const enabledFeatures = useContractEnabledExtensions(
|
||||
// @ts-expect-error - "wrong" ABI data type
|
||||
publishMetadataQuery.data?.abi,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
// --------------------------------------------------------
|
||||
// Generic
|
||||
// --------------------------------------------------------
|
||||
|
||||
// Read
|
||||
export {
|
||||
contractType,
|
||||
isContractTypeSupported,
|
||||
} from "../../extensions/thirdweb/read/contractType.js";
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Publisher contract
|
||||
// --------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import type { BaseTransactionOptions } from "../../../transaction/types.js";
|
||||
import { hexToString } from "../../../utils/encoding/hex.js";
|
||||
import * as ContractTypeGenerated from "../__generated__/IThirdwebContract/read/contractType.js";
|
||||
|
||||
/**
|
||||
* Checks if the `contractType` 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 `contractType` method is supported.
|
||||
* @extension THIRDWEB
|
||||
* @example
|
||||
* ```ts
|
||||
* import { isContractTypeSupported } from "thirdweb/extensions/thirdweb";
|
||||
*
|
||||
* const supported = isContractTypeSupported(["0x..."]);
|
||||
* ```
|
||||
*/
|
||||
export function isContractTypeSupported(availableSelectors: string[]) {
|
||||
return ContractTypeGenerated.isContractTypeSupported(availableSelectors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the "contractType" function on the contract.
|
||||
* @param options - The options for the contractType function.
|
||||
* @returns The parsed result of the function call.
|
||||
* @extension THIRDWEB
|
||||
* @example
|
||||
* ```ts
|
||||
* import { contractType } from "thirdweb/extensions/thirdweb";
|
||||
*
|
||||
* const result = await contractType({
|
||||
* contract,
|
||||
* });
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
export async function contractType(options: BaseTransactionOptions) {
|
||||
try {
|
||||
const rawContractType = await ContractTypeGenerated.contractType(options);
|
||||
const utf8 = hexToString(rawContractType);
|
||||
// biome-ignore lint/suspicious/noControlCharactersInRegex: required here
|
||||
return utf8.replace(/\x00/g, "") || "custom";
|
||||
} catch {
|
||||
// backwards compat, if this fails for any reason we assume it's a custom contract
|
||||
return "custom";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user