diff --git a/apps/dashboard/src/@/components/ui/code.tsx b/apps/dashboard/src/@/components/ui/code.tsx
new file mode 100644
index 00000000000..876b87b4896
--- /dev/null
+++ b/apps/dashboard/src/@/components/ui/code.tsx
@@ -0,0 +1,36 @@
+"use client";
+
+import { useClipboard } from "hooks/useClipboard";
+import { CheckIcon, CopyIcon } from "lucide-react";
+import { cn } from "../../lib/utils";
+import { Button } from "./button";
+
+export function PlainTextCodeBlock(props: {
+ code: string;
+ copyButtonClassName?: string;
+}) {
+ const { hasCopied, onCopy } = useClipboard(props.code);
+
+ return (
+
+
+ {props.code}
+
+
+
+ );
+}
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/ContractOverviewPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/ContractOverviewPage.tsx
index 7e2cbc44306..0c2f3ccaa7d 100644
--- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/ContractOverviewPage.tsx
+++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/ContractOverviewPage.tsx
@@ -1,5 +1,3 @@
-"use client";
-
import { PublishedBy } from "components/contract-components/shared/published-by";
import type { ThirdwebContract } from "thirdweb";
import { AnalyticsOverview } from "./components/Analytics";
@@ -21,6 +19,7 @@ interface ContractOverviewPageProps {
isPermissionsEnumerable: boolean;
chainSlug: string;
isAnalyticsSupported: boolean;
+ functionSelectors: string[];
}
const TRACKING_CATEGORY = "contract_overview";
@@ -35,6 +34,7 @@ export const ContractOverviewPage: React.FC = ({
isPermissionsEnumerable,
chainSlug,
isAnalyticsSupported,
+ functionSelectors,
}) => {
return (
@@ -45,6 +45,7 @@ export const ContractOverviewPage: React.FC
= ({
isErc20={isErc20}
contract={contract}
chainSlug={chainSlug}
+ functionSelectors={functionSelectors}
/>
{isAnalyticsSupported && (
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx
index 270194a4774..aa47e4783b2 100644
--- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx
+++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import { ThirdwebAreaChart } from "@/components/blocks/charts/area-chart";
import { Button } from "@/components/ui/button";
import {
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/BuildYourApp.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/BuildYourApp.tsx
index 72714f7f346..9b283c1e78f 100644
--- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/BuildYourApp.tsx
+++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/BuildYourApp.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import {
Flex,
GridItem,
@@ -7,7 +9,9 @@ import {
} from "@chakra-ui/react";
import { ChakraNextImage as Image } from "components/Image";
import { PRODUCTS } from "components/product-pages/common/nav/data";
-import { Card, Text, TrackedLink, type TrackedLinkProps } from "tw-components";
+import { Card } from "tw-components/card";
+import { TrackedLink, type TrackedLinkProps } from "tw-components/link";
+import { Text } from "tw-components/text";
const RENDERED_PRODUCTS = ["sdk", "storage", "ui-components", "auth"];
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/ContractChecklist.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/ContractChecklist.tsx
index e6eb237a3bd..52326429c0e 100644
--- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/ContractChecklist.tsx
+++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/ContractChecklist.tsx
@@ -1,7 +1,8 @@
+"use client";
+
import { AdminOnly } from "@3rdweb-sdk/react/components/roles/admin-only";
import { useIsMinter } from "@3rdweb-sdk/react/hooks/useContractRoles";
import { StepsCard } from "components/dashboard/StepsCard";
-import { useContractFunctionSelectors } from "contract-ui/hooks/useContractFunctionSelectors";
import Link from "next/link";
import { useMemo } from "react";
import type { ThirdwebContract } from "thirdweb";
@@ -18,6 +19,7 @@ interface ContractChecklistProps {
isErc1155: boolean;
isErc20: boolean;
chainSlug: string;
+ functionSelectors: string[];
}
type Step = {
@@ -27,13 +29,10 @@ type Step = {
};
export const ContractChecklist: React.FC = (props) => {
- const functionSelectorQuery = useContractFunctionSelectors(props.contract);
return (
// if no permissions, simply return null (do not fail open)
- {!!functionSelectorQuery.data?.length && (
-
- )}
+
);
};
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/LatestEvents.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/LatestEvents.tsx
index 2e28e150fa5..6a710432ed6 100644
--- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/LatestEvents.tsx
+++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/LatestEvents.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import {
type InternalTransaction,
useActivity,
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/MarketplaceDetails.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/MarketplaceDetails.tsx
index 4c06de88a14..3d608267ea0 100644
--- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/MarketplaceDetails.tsx
+++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/MarketplaceDetails.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import { WalletAddress } from "@/components/blocks/wallet-address";
import { Badge } from "@/components/ui/badge";
import { SkeletonContainer } from "@/components/ui/skeleton";
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/NFTDetails.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/NFTDetails.tsx
index 59f7bd41336..ebf4dcefb6e 100644
--- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/NFTDetails.tsx
+++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/NFTDetails.tsx
@@ -1,9 +1,11 @@
+"use client";
+
import { Flex, useBreakpointValue } from "@chakra-ui/react";
import type { ThirdwebContract } from "thirdweb";
import * as ERC721 from "thirdweb/extensions/erc721";
import * as ERC1155 from "thirdweb/extensions/erc1155";
import { useReadContract } from "thirdweb/react";
-import { TrackedLink, type TrackedLinkProps } from "tw-components";
+import { TrackedLink, type TrackedLinkProps } from "tw-components/link";
import { NFTCards } from "../../_components/NFTCards";
interface NFTDetailsProps {
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx
index 5c14353c650..8ee0f7fcd7f 100644
--- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx
+++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import { ToolTipLabel } from "@/components/ui/tooltip";
import { Box, Flex, List, SimpleGrid, Tag } from "@chakra-ui/react";
import { getAllRoleMembers } from "contract-ui/hooks/permissions";
@@ -8,14 +10,11 @@ import { useMemo } from "react";
import { toast } from "sonner";
import { type ThirdwebContract, ZERO_ADDRESS } from "thirdweb";
import { useReadContract } from "thirdweb/react";
-import {
- Button,
- Card,
- Heading,
- Text,
- TrackedLink,
- type TrackedLinkProps,
-} from "tw-components";
+import { Button } from "tw-components/button";
+import { Card } from "tw-components/card";
+import { Heading } from "tw-components/heading";
+import { TrackedLink, type TrackedLinkProps } from "tw-components/link";
+import { Text } from "tw-components/text";
import { shortenIfAddress } from "utils/usedapp-external";
interface PermissionsTableProps {
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/page.tsx
index dfe1dfa6698..51846f31621 100644
--- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/page.tsx
+++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/page.tsx
@@ -31,6 +31,7 @@ export default async function Page(props: {
}
chainSlug={chainMetadata.slug}
isAnalyticsSupported={contractPageMetadata.isAnalyticsSupported}
+ functionSelectors={contractPageMetadata.functionSelectors}
/>
);
}
diff --git a/apps/dashboard/src/app/(dashboard)/explore/[category]/page.tsx b/apps/dashboard/src/app/(dashboard)/explore/[category]/page.tsx
index 602335a234e..7c2b20404f2 100644
--- a/apps/dashboard/src/app/(dashboard)/explore/[category]/page.tsx
+++ b/apps/dashboard/src/app/(dashboard)/explore/[category]/page.tsx
@@ -1,4 +1,3 @@
-import { ChakraProviderSetup } from "@/components/ChakraProviderSetup";
import {
Breadcrumb,
BreadcrumbItem,
@@ -7,12 +6,16 @@ import {
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
-import { ContractCard } from "components/explore/contract-card";
+import {
+ ContractCard,
+ ContractCardSkeleton,
+} from "components/explore/contract-card";
import { DeployUpsellCard } from "components/explore/upsells/deploy-your-own";
import { ALL_CATEGORIES, getCategory } from "data/explore";
import type { Metadata } from "next";
import Link from "next/link";
import { notFound } from "next/navigation";
+import { Suspense } from "react";
type ExploreCategoryPageProps = {
params: {
@@ -101,35 +104,36 @@ export default async function ExploreCategoryPage(
}
return (
- }
key={publisher + contractId + overrides?.title}
- publisher={publisher}
- contractId={contractId}
- titleOverride={overrides?.title}
- descriptionOverride={overrides?.description}
- tracking={{
- source: category.id,
- itemIndex: `${idx}`,
- }}
- isBeta={category.isBeta}
- modules={
- modules?.length
- ? modules.map((m) => ({
- publisher: m.split("/")[0] || "",
- moduleId: m.split("/")[1] || "",
- }))
- : undefined
- }
- />
+ >
+ ({
+ publisher: m.split("/")[0] || "",
+ moduleId: m.split("/")[1] || "",
+ }))
+ : undefined
+ }
+ />
+
);
})}
- {/* TODO: remove this once we update the deploy upsell card */}
-
-
-
+
);
@@ -140,3 +144,6 @@ export async function generateStaticParams() {
params: { category },
}));
}
+
+// TODO - figure out why this page is not building if we let it be static
+export const dynamic = "force-dynamic";
diff --git a/apps/dashboard/src/app/(dashboard)/explore/page.tsx b/apps/dashboard/src/app/(dashboard)/explore/page.tsx
index 41506da5c6b..c373595f296 100644
--- a/apps/dashboard/src/app/(dashboard)/explore/page.tsx
+++ b/apps/dashboard/src/app/(dashboard)/explore/page.tsx
@@ -1,4 +1,3 @@
-import { ChakraProviderSetup } from "@/components/ChakraProviderSetup";
import {
Breadcrumb,
BreadcrumbItem,
@@ -53,11 +52,11 @@ export default async function ExplorePage() {
- {/* TODO: remove this once we update the deploy upsell card */}
-
-
-
+
);
}
+
+// TODO - figure out why this page is not building if we let it be static
+export const dynamic = "force-dynamic";
diff --git a/apps/dashboard/src/components/contract-components/fetchDeployMetadata.ts b/apps/dashboard/src/components/contract-components/fetchDeployMetadata.ts
index 040128e1763..c2a0971495c 100644
--- a/apps/dashboard/src/components/contract-components/fetchDeployMetadata.ts
+++ b/apps/dashboard/src/components/contract-components/fetchDeployMetadata.ts
@@ -1,7 +1,7 @@
import { getThirdwebClient } from "@/constants/thirdweb.server";
import { fetchDeployMetadata as sdkFetchDeployMetadata } from "thirdweb/contract";
import { removeUndefinedFromObjectDeep } from "../../utils/object";
-import { toContractIdIpfsHash } from "./hooks";
+import type { ContractId } from "./types";
// metadata PRE publish, only has the compiler output info (from CLI)
@@ -15,3 +15,10 @@ export async function fetchDeployMetadata(contractId: string) {
}),
);
}
+
+function toContractIdIpfsHash(contractId: ContractId) {
+ if (contractId?.startsWith("ipfs://")) {
+ return contractId;
+ }
+ return `ipfs://${contractId}`;
+}
diff --git a/apps/dashboard/src/components/contract-components/hooks.ts b/apps/dashboard/src/components/contract-components/hooks.ts
index 9d3721856ee..8943cf933e6 100644
--- a/apps/dashboard/src/components/contract-components/hooks.ts
+++ b/apps/dashboard/src/components/contract-components/hooks.ts
@@ -131,13 +131,6 @@ export function useFunctionParamsFromABI(abi?: Abi, functionName?: string) {
}, [abi, functionName]);
}
-export function toContractIdIpfsHash(contractId: ContractId) {
- if (contractId?.startsWith("ipfs://")) {
- return contractId;
- }
- return `ipfs://${contractId}`;
-}
-
export type PublishedContractDetails = Awaited<
ReturnType
>[number];
diff --git a/apps/dashboard/src/components/contract-components/shared/published-by.tsx b/apps/dashboard/src/components/contract-components/shared/published-by.tsx
index e14f3cb7ba3..50802e0fb01 100644
--- a/apps/dashboard/src/components/contract-components/shared/published-by.tsx
+++ b/apps/dashboard/src/components/contract-components/shared/published-by.tsx
@@ -1,139 +1,147 @@
-import { useQuery } from "@tanstack/react-query";
-import {
- useEns,
- usePublishedContractsFromDeploy,
-} from "components/contract-components/hooks";
+import { getThirdwebClient } from "@/constants/thirdweb.server";
import { ContractCard } from "components/explore/contract-card";
import { THIRDWEB_DEPLOYER_ADDRESS } from "constants/addresses";
-import { useMemo } from "react";
import type { ThirdwebContract } from "thirdweb";
import { polygon } from "thirdweb/chains";
import { getBytecode, getContract } from "thirdweb/contract";
import { getPublishedUriFromCompilerUri } from "thirdweb/extensions/thirdweb";
import { getInstalledModules } from "thirdweb/modules";
-import { useActiveAccount, useReadContract } from "thirdweb/react";
import { download } from "thirdweb/storage";
import { extractIPFSUri } from "thirdweb/utils";
+import { getAuthTokenWalletAddress } from "../../../app/api/lib/getAuthToken";
+import { isEnsName, resolveEns } from "../../../lib/ens";
+import { fetchPublishedContractsFromDeploy } from "../fetchPublishedContractsFromDeploy";
interface PublishedByProps {
contract: ThirdwebContract;
}
-export const PublishedBy: React.FC = ({ contract }) => {
- const publishedContractsFromDeploy =
- usePublishedContractsFromDeploy(contract);
-
- const address = useActiveAccount()?.address;
-
- const publishedContractToShow = useMemo(() => {
- const reversedPublishedContractsFromDeploy = [
- ...(publishedContractsFromDeploy.data || []),
- ].reverse();
-
- return (
- reversedPublishedContractsFromDeploy.find(
- (publishedContract) => publishedContract.publisher === address,
- ) ||
- reversedPublishedContractsFromDeploy.find(
- (publishedContract) =>
- publishedContract.publisher === THIRDWEB_DEPLOYER_ADDRESS,
- ) ||
- reversedPublishedContractsFromDeploy[
- reversedPublishedContractsFromDeploy.length - 1
- ] ||
- undefined
- );
- }, [publishedContractsFromDeploy.data, address]);
-
- const publisherEnsQuery = useEns(publishedContractToShow?.publisher);
- const publisherAddress =
- publisherEnsQuery.data?.ensName || publisherEnsQuery.data?.address;
-
- const installedModules = useReadContract(getInstalledModules, {
+type ModuleMetadataPickedKeys = {
+ publisher: string;
+ moduleId: string;
+ name: string;
+ version: string;
+};
+
+export const PublishedBy: React.FC = async ({ contract }) => {
+ const client = getThirdwebClient();
+ const publishedContractsFromDeploy = await fetchPublishedContractsFromDeploy({
contract,
- queryOptions: {
- enabled: publishedContractToShow?.routerType === "modular" && !!contract,
- },
+ client,
});
- // this handles all the logic for modules in the published contract card
- const installedModulesQuery = useQuery({
- queryKey: [
- "published-by-modules",
- contract,
- installedModules.data?.map((m) => m.implementation),
- ],
- queryFn: async () => {
- if (!installedModules.data?.length) {
- return [];
+ const address = getAuthTokenWalletAddress();
+
+ const reversedPublishedContractsFromDeploy = [
+ ...(publishedContractsFromDeploy || []),
+ ].reverse();
+
+ const publishedContractToShow =
+ reversedPublishedContractsFromDeploy.find(
+ (publishedContract) => publishedContract.publisher === address,
+ ) ||
+ reversedPublishedContractsFromDeploy.find(
+ (publishedContract) =>
+ publishedContract.publisher === THIRDWEB_DEPLOYER_ADDRESS,
+ ) ||
+ reversedPublishedContractsFromDeploy[
+ reversedPublishedContractsFromDeploy.length - 1
+ ] ||
+ undefined;
+
+ if (!publishedContractToShow || !publishedContractToShow.publisher) {
+ return null;
+ }
+
+ // get publisher address/ens
+ let publisherAddressOrEns = publishedContractToShow.publisher;
+ if (!isEnsName(publishedContractToShow.publisher)) {
+ try {
+ const res = await resolveEns(publishedContractToShow.publisher);
+ if (res.ensName) {
+ publisherAddressOrEns = res.ensName;
}
+ } catch {
+ // no op
+ }
+ }
- const moduleContracts = installedModules.data.map((module) => {
- return getContract({ ...contract, address: module.implementation });
+ // Get Modules metadata for modular contract
+ let modules: ModuleMetadataPickedKeys[] = [];
+ if (publishedContractToShow?.routerType === "modular") {
+ try {
+ const installedModules = await getInstalledModules({
+ contract,
});
- const metadataUris = await Promise.allSettled(
- moduleContracts.map(async (c) => {
- const byteCode = await getBytecode(c);
- const ipfsUri = extractIPFSUri(byteCode);
-
- if (!ipfsUri) {
- throw new Error("No IPFS URI found in bytecode");
- }
- let uris = await getPublishedUriFromCompilerUri({
- contract: {
- chain: polygon,
- client: contract.client,
- address: "0xf5b896Ddb5146D5dA77efF4efBb3Eae36E300808",
- },
- compilerMetadataUri: ipfsUri,
- }).catch((e) => {
- console.error("Error fetching published URI", e);
- return [];
- });
-
- uris = uris.filter((uri) => uri.length > 0);
- if (uris.length === 0) {
- throw new Error("No published URI found");
- }
-
- const results = await Promise.allSettled(
- uris.map(async (uri) => {
- const content = await download({
- uri,
- client: contract.client,
- });
- return JSON.parse(await content.text());
- }),
- );
-
- return results
- .filter((r) => r.status === "fulfilled")
- .map((r) => r.value);
- }),
- );
-
- const filtered = metadataUris
- .filter((m) => m.status === "fulfilled")
- .map((m) => m.value);
-
- return filtered.map((m) => m[0]);
- },
- enabled: !!installedModules.data?.length,
- });
- if (!publishedContractToShow || !publisherAddress) {
- return null;
+ if (installedModules.length !== 0) {
+ const moduleContracts = installedModules.map((module) => {
+ return getContract({ ...contract, address: module.implementation });
+ });
+
+ const metadataUris = await Promise.allSettled(
+ moduleContracts.map(async (c) => {
+ const byteCode = await getBytecode(c);
+ const ipfsUri = extractIPFSUri(byteCode);
+
+ if (!ipfsUri) {
+ throw new Error("No IPFS URI found in bytecode");
+ }
+ let uris = await getPublishedUriFromCompilerUri({
+ contract: {
+ chain: polygon,
+ client: contract.client,
+ address: "0xf5b896Ddb5146D5dA77efF4efBb3Eae36E300808",
+ },
+ compilerMetadataUri: ipfsUri,
+ }).catch((e) => {
+ console.error("Error fetching published URI", e);
+ return [];
+ });
+
+ uris = uris.filter((uri) => uri.length > 0);
+ if (uris.length === 0) {
+ throw new Error("No published URI found");
+ }
+
+ const results = await Promise.allSettled(
+ uris.map(async (uri) => {
+ const content = await download({
+ uri,
+ client: contract.client,
+ });
+ return JSON.parse(
+ await content.text(),
+ ) as ModuleMetadataPickedKeys;
+ }),
+ );
+
+ return results
+ .filter((r) => r.status === "fulfilled")
+ .map((r) => r.value);
+ }),
+ );
+
+ const filtered = metadataUris
+ .filter((m) => m.status === "fulfilled")
+ .map((m) => m.value);
+
+ modules = filtered.map((m) => m[0]).filter((m) => m !== undefined);
+ }
+ } catch {
+ // no op
+ }
}
return (
({
+ modules={modules.map((m) => ({
publisher: m.publisher,
moduleId: m.name,
version: m.version,
diff --git a/apps/dashboard/src/components/explore/contract-card/index.tsx b/apps/dashboard/src/components/explore/contract-card/index.tsx
index 185d6534d52..e34684a7fa6 100644
--- a/apps/dashboard/src/components/explore/contract-card/index.tsx
+++ b/apps/dashboard/src/components/explore/contract-card/index.tsx
@@ -1,17 +1,13 @@
-"use client";
-
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
-import { Skeleton, SkeletonContainer } from "@/components/ui/skeleton";
+import { Skeleton } from "@/components/ui/skeleton";
import { TrackedLinkTW } from "@/components/ui/tracked-link";
-import { useThirdwebClient } from "@/constants/thirdweb.client";
+import { getThirdwebClient } from "@/constants/thirdweb.server";
import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler";
import { cn } from "@/lib/utils";
-import { useQuery } from "@tanstack/react-query";
import { moduleToBase64 } from "app/(dashboard)/published-contract/utils/module-base-64";
import { RocketIcon, ShieldCheckIcon } from "lucide-react";
import Link from "next/link";
-import invariant from "tiny-invariant";
import { fetchPublishedContractVersion } from "../../contract-components/fetch-contracts-with-versions";
import { ContractPublisher, replaceDeployerAddress } from "../publisher";
@@ -80,7 +76,7 @@ function getContractUrl(
return replaceDeployerAddress(pathName);
}
-export const ContractCard: React.FC = ({
+export async function ContractCard({
publisher,
contractId,
titleOverride,
@@ -89,25 +85,28 @@ export const ContractCard: React.FC = ({
tracking,
modules = [],
isBeta,
-}) => {
- const client = useThirdwebClient();
- const publishedContractResult = usePublishedContract(
- `${publisher}/${contractId}/${version}`,
- );
+}: ContractCardProps) {
+ const client = getThirdwebClient();
+ const publishedContractResult = await fetchPublishedContractVersion(
+ publisher,
+ contractId,
+ version,
+ ).catch(() => null);
- const showSkeleton = publishedContractResult.isPending;
+ if (!publishedContractResult) {
+ return null;
+ }
const auditLink = resolveSchemeWithErrorHandler({
- uri: publishedContractResult.data?.audit,
+ uri: publishedContractResult.audit,
client,
});
return (
= ({
)}
{/* Version */}
- {
- return (
-
- v{v}
-
- );
- }}
- />
+
+ {publishedContractResult.version && (
+
+ v{publishedContractResult.version}
+
+ )}
{/* Tags */}
@@ -170,34 +164,20 @@ export const ContractCard: React.FC = ({
-
+ {(
titleOverride ||
- publishedContractResult.data?.displayName ||
- publishedContractResult.data?.name
- }
- render={(v) => {
- return (
-
- {v.replace("[Beta]", "")}
-
- );
- }}
- />
+ publishedContractResult.displayName ||
+ publishedContractResult.name
+ ).replace("[Beta]", "")}
+
+
+ {/* Desc */}
+
+ {descriptionOverride || publishedContractResult.description}
+
- {publishedContractResult.data ? (
-
- {descriptionOverride || publishedContractResult.data?.description}
-
- ) : (
-
- )}
{modules.length ? (
{modules.slice(0, 2).map((m) => (
@@ -216,10 +196,9 @@ export const ContractCard: React.FC
= ({
!modules?.length && "mt-auto",
)}
>
-
+ {publishedContractResult.publisher && (
+
+ )}
);
-};
-
-// data fetching
-type PublishedContractId =
- | `${string}/${string}`
- | `${string}/${string}/${string}`;
+}
-function usePublishedContract(publishedContractId: PublishedContractId) {
- const [publisher, contractId, version] = publishedContractId.split("/");
- return useQuery({
- queryKey: ["published-contract", { publishedContractId }],
- queryFn: () => {
- invariant(publisher, "publisher is required");
- invariant(contractId, "contractId is required");
- return fetchPublishedContractVersion(publisher, contractId, version);
- },
- enabled: !!publisher || !!contractId,
- });
+export function ContractCardSkeleton() {
+ return ;
}
diff --git a/apps/dashboard/src/components/explore/contract-row/index.tsx b/apps/dashboard/src/components/explore/contract-row/index.tsx
index 959e927a0ff..876248c01ff 100644
--- a/apps/dashboard/src/components/explore/contract-row/index.tsx
+++ b/apps/dashboard/src/components/explore/contract-row/index.tsx
@@ -1,7 +1,8 @@
import type { ExploreCategory } from "data/explore";
import { ArrowRightIcon } from "lucide-react";
import Link from "next/link";
-import { ContractCard } from "../contract-card";
+import { Suspense } from "react";
+import { ContractCard, ContractCardSkeleton } from "../contract-card";
interface ContractRowProps {
category: ExploreCategory;
@@ -69,26 +70,30 @@ export function ContractRow({ category }: ContractRowProps) {
return null;
}
return (
- ({
- publisher: m.split("/")[0] || "",
- moduleId: m.split("/")[1] || "",
- }))
- : undefined
- }
- />
+ fallback={}
+ >
+ ({
+ publisher: m.split("/")[0] || "",
+ moduleId: m.split("/")[1] || "",
+ }))
+ : undefined
+ }
+ />
+
);
})}
diff --git a/apps/dashboard/src/components/explore/publisher/index.tsx b/apps/dashboard/src/components/explore/publisher/index.tsx
index 53f968d2bc5..a41fe479604 100644
--- a/apps/dashboard/src/components/explore/publisher/index.tsx
+++ b/apps/dashboard/src/components/explore/publisher/index.tsx
@@ -1,41 +1,40 @@
-import { SkeletonContainer } from "@/components/ui/skeleton";
-import { useEns } from "components/contract-components/hooks";
import { PublisherAvatar } from "components/contract-components/publisher/masked-avatar";
import Link from "next/link";
-import type { RequiredParam } from "utils/types";
import { shortenIfAddress } from "utils/usedapp-external";
+import { isEnsName, resolveEns } from "../../../lib/ens";
interface ContractPublisherProps {
- addressOrEns: RequiredParam;
- showSkeleton?: boolean;
+ addressOrEns: string;
}
-export const ContractPublisher: React.FC = ({
+export const ContractPublisher: React.FC = async ({
addressOrEns,
- showSkeleton,
}) => {
- const ensQuery = useEns(addressOrEns || undefined);
+ let ensOrAddressToShow = addressOrEns;
+
+ if (!isEnsName(addressOrEns)) {
+ try {
+ const res = await resolveEns(addressOrEns);
+ if (res.ensName) {
+ ensOrAddressToShow = res.ensName;
+ }
+ } catch {
+ // ignore
+ }
+ }
return (
- {treatAddress(v)}
}
- />
+ {treatAddress(ensOrAddressToShow)}
);
};
diff --git a/apps/dashboard/src/components/explore/upsells/deploy-your-own.tsx b/apps/dashboard/src/components/explore/upsells/deploy-your-own.tsx
index 664cce03423..57e82491941 100644
--- a/apps/dashboard/src/components/explore/upsells/deploy-your-own.tsx
+++ b/apps/dashboard/src/components/explore/upsells/deploy-your-own.tsx
@@ -1,8 +1,6 @@
-"use client";
-
+import { PlainTextCodeBlock } from "@/components/ui/code";
import { TrackedLinkTW } from "@/components/ui/tracked-link";
import { ExternalLinkIcon } from "lucide-react";
-import { CodeBlock } from "tw-components";
export const DeployUpsellCard: React.FC = () => {
return (
@@ -29,11 +27,7 @@ export const DeployUpsellCard: React.FC = () => {
-
+
@@ -53,7 +47,7 @@ export const DeployUpsellCard: React.FC = () => {
-
+
);
};
diff --git a/apps/dashboard/src/components/explore/upsells/publish-submit.tsx b/apps/dashboard/src/components/explore/upsells/publish-submit.tsx
index e245ca3eae2..73507ae6cc1 100644
--- a/apps/dashboard/src/components/explore/upsells/publish-submit.tsx
+++ b/apps/dashboard/src/components/explore/upsells/publish-submit.tsx
@@ -1,5 +1,3 @@
-"use client";
-
import { Button } from "@/components/ui/button";
import { TrackedLinkTW } from "@/components/ui/tracked-link";
import Image from "next/image";