Skip to content

Commit ed5fd50

Browse files
committed
[TOOL-4621] New ERC20 public contract page
1 parent bb7a898 commit ed5fd50

File tree

54 files changed

+1513
-129
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1513
-129
lines changed

apps/dashboard/src/@/actions/getWalletNFTs.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type { WalletNFT } from "lib/wallet/nfts/types";
1212
import { getVercelEnv } from "../../lib/vercel-utils";
1313
import { isAlchemySupported } from "../../lib/wallet/nfts/isAlchemySupported";
1414
import { isMoralisSupported } from "../../lib/wallet/nfts/isMoralisSupported";
15-
import { NET_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID } from "../constants/public-envs";
15+
import { NEXT_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID } from "../constants/public-envs";
1616
import { MORALIS_API_KEY } from "../constants/server-envs";
1717

1818
type WalletNFTApiReturn =
@@ -149,7 +149,7 @@ async function getWalletNFTsFromInsight(params: {
149149

150150
const response = await fetch(url, {
151151
headers: {
152-
"x-client-id": NET_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID,
152+
"x-client-id": NEXT_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID,
153153
},
154154
});
155155

apps/dashboard/src/@/components/blocks/charts/area-chart.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,12 @@ type ThirdwebAreaChartProps<TConfig extends ChartConfig> = {
4646
// chart className
4747
chartClassName?: string;
4848
isPending: boolean;
49+
className?: string;
50+
cardContentClassName?: string;
4951
hideLabel?: boolean;
5052
toolTipLabelFormatter?: (label: string, payload: unknown) => React.ReactNode;
53+
toolTipValueFormatter?: (value: unknown) => React.ReactNode;
54+
emptyChartState?: React.ReactElement;
5155
};
5256

5357
export function ThirdwebAreaChart<TConfig extends ChartConfig>(
@@ -56,7 +60,7 @@ export function ThirdwebAreaChart<TConfig extends ChartConfig>(
5660
const configKeys = useMemo(() => Object.keys(props.config), [props.config]);
5761

5862
return (
59-
<Card>
63+
<Card className={props.className}>
6064
{props.header && (
6165
<CardHeader>
6266
<CardTitle className={cn("mb-2", props.header.titleClassName)}>
@@ -70,12 +74,16 @@ export function ThirdwebAreaChart<TConfig extends ChartConfig>(
7074

7175
{props.customHeader && props.customHeader}
7276

73-
<CardContent className={cn(!props.header && "pt-6")}>
77+
<CardContent
78+
className={cn(!props.header && "pt-6", props.cardContentClassName)}
79+
>
7480
<ChartContainer config={props.config} className={props.chartClassName}>
7581
{props.isPending ? (
7682
<LoadingChartState />
7783
) : props.data.length === 0 ? (
78-
<EmptyChartState />
84+
<EmptyChartState type="area">
85+
{props.emptyChartState}
86+
</EmptyChartState>
7987
) : (
8088
<AreaChart accessibilityLayer data={props.data}>
8189
<CartesianGrid vertical={false} />
@@ -100,6 +108,7 @@ export function ThirdwebAreaChart<TConfig extends ChartConfig>(
100108
props.hideLabel !== undefined ? props.hideLabel : true
101109
}
102110
labelFormatter={props.toolTipLabelFormatter}
111+
valueFormatter={props.toolTipValueFormatter}
103112
/>
104113
}
105114
/>

apps/dashboard/src/@/components/blocks/charts/bar-chart.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ export function ThirdwebBarChart<TConfig extends ChartConfig>(
7575
{props.isPending ? (
7676
<LoadingChartState />
7777
) : props.data.length === 0 ? (
78-
<EmptyChartState>{props.emptyChartState}</EmptyChartState>
78+
<EmptyChartState type="bar">
79+
{props.emptyChartState}
80+
</EmptyChartState>
7981
) : (
8082
<BarChart accessibilityLayer data={props.data}>
8183
<CartesianGrid vertical={false} />
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export function LoadingDots() {
2+
return (
3+
<div className="fade-in-0 flex animate-in gap-2 duration-300">
4+
<span className="sr-only">Loading...</span>
5+
<div className="size-4 animate-bounce rounded-full bg-foreground [animation-delay:-0.3s]" />
6+
<div className="size-4 animate-bounce rounded-full bg-foreground [animation-delay:-0.15s]" />
7+
<div className="size-4 animate-bounce rounded-full bg-foreground" />
8+
</div>
9+
);
10+
}

apps/dashboard/src/@/constants/public-envs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export const NET_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID =
1+
export const NEXT_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID =
22
process.env.NEXT_PUBLIC_DASHBOARD_CLIENT_ID || "";
33

44
export const NEXT_PUBLIC_NEBULA_APP_CLIENT_ID =

apps/dashboard/src/@/constants/thirdweb.server.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
NET_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID,
2+
NEXT_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID,
33
NEXT_PUBLIC_IPFS_GATEWAY_URL,
44
} from "@/constants/public-envs";
55
import {
@@ -76,7 +76,7 @@ export function getConfiguredThirdwebClient(options: {
7676
return createThirdwebClient({
7777
teamId: options.teamId,
7878
secretKey: options.secretKey,
79-
clientId: NET_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID,
79+
clientId: NEXT_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID,
8080
config: {
8181
storage: {
8282
gatewayUrl: NEXT_PUBLIC_IPFS_GATEWAY_URL,
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { TeamHeader } from "../../team/components/TeamHeader/team-header";
2+
3+
export default function Layout({
4+
children,
5+
}: {
6+
children: React.ReactNode;
7+
}) {
8+
return (
9+
<div className="flex grow flex-col">
10+
<div className="border-border border-b bg-card">
11+
<TeamHeader />
12+
</div>
13+
{children}
14+
</div>
15+
);
16+
}

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/live-stats.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { CopyTextButton } from "@/components/ui/CopyTextButton";
44
import { Skeleton } from "@/components/ui/skeleton";
55
import { ToolTipLabel } from "@/components/ui/tooltip";
66
import { isProd } from "@/constants/env-utils";
7-
import { NET_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID } from "@/constants/public-envs";
7+
import { NEXT_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID } from "@/constants/public-envs";
88
import { useQuery } from "@tanstack/react-query";
99
import { CircleCheckIcon, XIcon } from "lucide-react";
1010
import { hostnameEndsWith } from "utils/url";
@@ -14,7 +14,7 @@ function useChainStatswithRPC(_rpcUrl: string) {
1414
let rpcUrl = _rpcUrl.replace(
1515
// eslint-disable-next-line no-template-curly-in-string
1616
"${THIRDWEB_API_KEY}",
17-
NET_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID,
17+
NEXT_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID,
1818
);
1919

2020
// based on the environment hit dev or production

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
getAuthToken,
2525
getAuthTokenWalletAddress,
2626
} from "../../../../api/lib/getAuthToken";
27+
import { TeamHeader } from "../../../../team/components/TeamHeader/team-header";
2728
import { StarButton } from "../../components/client/star-button";
2829
import { getChain, getChainMetadata } from "../../utils";
2930
import { AddChainToWallet } from "./components/client/add-chain-to-wallet";
@@ -95,7 +96,10 @@ The following is the user's message:
9596
}
9697

9798
return (
98-
<>
99+
<div className="flex grow flex-col">
100+
<div className="border-border border-b bg-card">
101+
<TeamHeader />
102+
</div>
99103
<NebulaChatButton
100104
isLoggedIn={!!authToken}
101105
networks={chain.testnet ? "testnet" : "mainnet"}
@@ -225,7 +229,7 @@ The following is the user's message:
225229
{children}
226230
</div>
227231
</div>
228-
</>
232+
</div>
229233
);
230234
}
231235

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/metadata-header.tsx

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,21 +82,19 @@ export const MetadataHeader: React.FC<MetadataHeaderProps> = ({
8282
</h1>
8383
)}
8484

85-
{chain && (
86-
<Link
87-
href={`/${chain.slug}`}
88-
className="flex w-fit shrink-0 items-center gap-2 rounded-3xl border border-border bg-card px-2.5 py-1.5 hover:bg-accent"
89-
>
90-
<ChainIconClient
91-
src={chain.icon?.url}
92-
client={client}
93-
className="size-4"
94-
/>
95-
{cleanedChainName && (
96-
<span className="text-xs">{cleanedChainName}</span>
97-
)}
98-
</Link>
99-
)}
85+
<Link
86+
href={`/${chain.slug}`}
87+
className="flex w-fit shrink-0 items-center gap-2 rounded-3xl border border-border bg-card px-2.5 py-1.5 hover:bg-accent"
88+
>
89+
<ChainIconClient
90+
src={chain.icon?.url}
91+
client={client}
92+
className="size-4"
93+
/>
94+
{cleanedChainName && (
95+
<span className="text-xs">{cleanedChainName}</span>
96+
)}
97+
</Link>
10098
</div>
10199
)}
102100

@@ -115,7 +113,7 @@ export const MetadataHeader: React.FC<MetadataHeaderProps> = ({
115113
<CopyAddressButton
116114
address={address}
117115
copyIconPosition="left"
118-
className="bg-card text-xs"
116+
className="bg-card px-2.5 py-1.5 text-xs"
119117
variant="outline"
120118
/>
121119

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { resolveFunctionSelectors } from "lib/selectors";
2+
import type { ThirdwebContract } from "thirdweb";
3+
import { isERC20 } from "thirdweb/extensions/erc20";
4+
5+
export type NewPublicPageType = "erc20";
6+
7+
export async function shouldRenderNewPublicPage(
8+
contract: ThirdwebContract,
9+
): Promise<false | { type: NewPublicPageType }> {
10+
const functionSelectors = await resolveFunctionSelectors(contract).catch(
11+
() => undefined,
12+
);
13+
14+
if (!functionSelectors) {
15+
return false;
16+
}
17+
18+
const isERC20Contract = isERC20(functionSelectors);
19+
20+
if (isERC20Contract) {
21+
return {
22+
type: "erc20",
23+
};
24+
}
25+
26+
return false;
27+
}

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/shared-analytics-page.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { ProjectMeta } from "../../../../../team/[team_slug]/[project_slug]
55
import { redirectToContractLandingPage } from "../../../../../team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/utils";
66
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
77
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
8+
import { shouldRenderNewPublicPage } from "../_utils/newPublicPage";
89
import { ContractAnalyticsPage } from "./ContractAnalyticsPage";
910

1011
export async function SharedAnalyticsPage(props: {
@@ -22,6 +23,18 @@ export async function SharedAnalyticsPage(props: {
2223
notFound();
2324
}
2425

26+
// new public page can't show /analytics page
27+
if (!props.projectMeta) {
28+
const shouldHide = await shouldRenderNewPublicPage(info.serverContract);
29+
if (shouldHide) {
30+
redirectToContractLandingPage({
31+
contractAddress: props.contractAddress,
32+
chainIdOrSlug: props.chainIdOrSlug,
33+
projectMeta: props.projectMeta,
34+
});
35+
}
36+
}
37+
2538
const [
2639
{ eventSelectorToName, writeFnSelectorToName },
2740
{ isInsightSupported },

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/shared-claim-conditions-page.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { redirectToContractLandingPage } from "../../../../../team/[team_slug]/[
44
import { ClaimConditions } from "../_components/claim-conditions/claim-conditions";
55
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
66
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
7+
import { shouldRenderNewPublicPage } from "../_utils/newPublicPage";
78
import { ClaimConditionsClient } from "./ClaimConditions.client";
89

910
export async function SharedClaimConditionsPage(props: {
@@ -22,6 +23,18 @@ export async function SharedClaimConditionsPage(props: {
2223
notFound();
2324
}
2425

26+
// new public page can't show /claim-conditions page
27+
if (!props.projectMeta) {
28+
const shouldHide = await shouldRenderNewPublicPage(info.serverContract);
29+
if (shouldHide) {
30+
redirectToContractLandingPage({
31+
contractAddress: props.contractAddress,
32+
chainIdOrSlug: props.chainIdOrSlug,
33+
projectMeta: props.projectMeta,
34+
});
35+
}
36+
}
37+
2538
const { clientContract, serverContract, isLocalhostChain } = info;
2639

2740
if (isLocalhostChain) {

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/code/shared-code-page.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { notFound } from "next/navigation";
22
import { resolveContractAbi } from "thirdweb/contract";
33
import type { ProjectMeta } from "../../../../../team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/types";
4+
import { redirectToContractLandingPage } from "../../../../../team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/utils";
45
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
6+
import { shouldRenderNewPublicPage } from "../_utils/newPublicPage";
57
import { ContractCodePage } from "./contract-code-page";
68
import { ContractCodePageClient } from "./contract-code-page.client";
79

@@ -20,6 +22,18 @@ export async function SharedCodePage(props: {
2022
notFound();
2123
}
2224

25+
// new public page can't show /code page
26+
if (!props.projectMeta) {
27+
const shouldHide = await shouldRenderNewPublicPage(info.serverContract);
28+
if (shouldHide) {
29+
redirectToContractLandingPage({
30+
contractAddress: props.contractAddress,
31+
chainIdOrSlug: props.chainIdOrSlug,
32+
projectMeta: props.projectMeta,
33+
});
34+
}
35+
}
36+
2337
const { clientContract, serverContract, chainMetadata, isLocalhostChain } =
2438
info;
2539

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/shared-cross-chain-page.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ import { eth_getCode, getRpcClient } from "thirdweb/rpc";
2020
import type { TransactionReceipt } from "thirdweb/transaction";
2121
import { type AbiFunction, decodeFunctionData } from "thirdweb/utils";
2222
import type { ProjectMeta } from "../../../../../team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/types";
23+
import { redirectToContractLandingPage } from "../../../../../team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/utils";
2324
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
2425
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
26+
import { shouldRenderNewPublicPage } from "../_utils/newPublicPage";
2527
import { DataTable } from "./data-table";
2628
import { NoCrossChainPrompt } from "./no-crosschain-prompt";
2729

@@ -48,6 +50,18 @@ export async function SharedCrossChainPage(props: {
4850
notFound();
4951
}
5052

53+
// new public page can't show /cross-chain page
54+
if (!props.projectMeta) {
55+
const shouldHide = await shouldRenderNewPublicPage(info.serverContract);
56+
if (shouldHide) {
57+
redirectToContractLandingPage({
58+
contractAddress: props.contractAddress,
59+
chainIdOrSlug: props.chainIdOrSlug,
60+
projectMeta: props.projectMeta,
61+
});
62+
}
63+
}
64+
5165
const { clientContract, serverContract } = info;
5266

5367
const isModularCore = (await getContractPageMetadata(serverContract))

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/events/shared-events-page.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { notFound } from "next/navigation";
22
import type { ProjectMeta } from "../../../../../team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/types";
3+
import { redirectToContractLandingPage } from "../../../../../team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/utils";
34
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
5+
import { shouldRenderNewPublicPage } from "../_utils/newPublicPage";
46
import { EventsFeed } from "./events-feed";
57

68
export async function SharedEventsPage(props: {
@@ -18,6 +20,18 @@ export async function SharedEventsPage(props: {
1820
notFound();
1921
}
2022

23+
// new public page can't show /events page
24+
if (!props.projectMeta) {
25+
const shouldHide = await shouldRenderNewPublicPage(info.serverContract);
26+
if (shouldHide) {
27+
redirectToContractLandingPage({
28+
contractAddress: props.contractAddress,
29+
chainIdOrSlug: props.chainIdOrSlug,
30+
projectMeta: props.projectMeta,
31+
});
32+
}
33+
}
34+
2135
return (
2236
<EventsFeed
2337
contract={info.clientContract}

0 commit comments

Comments
 (0)