diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/getContractCreator.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/getContractCreator.tsx
new file mode 100644
index 00000000000..9a4e67ed3b2
--- /dev/null
+++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/getContractCreator.tsx
@@ -0,0 +1,27 @@
+import type { ThirdwebContract } from "thirdweb";
+import { isOwnerSupported, owner } from "thirdweb/extensions/common";
+import {
+ getRoleMember,
+ isGetRoleAdminSupported,
+} from "thirdweb/extensions/permissions";
+
+export async function getContractCreator(
+ contract: ThirdwebContract,
+ functionSelectors: string[],
+) {
+ if (isOwnerSupported(functionSelectors)) {
+ return owner({
+ contract,
+ });
+ }
+
+ if (isGetRoleAdminSupported(functionSelectors)) {
+ return getRoleMember({
+ contract,
+ index: BigInt(0),
+ role: "admin",
+ });
+ }
+
+ return null;
+}
diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractCreatorBadge.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractCreatorBadge.tsx
new file mode 100644
index 00000000000..7bef42d4f05
--- /dev/null
+++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractCreatorBadge.tsx
@@ -0,0 +1,19 @@
+import type { ThirdwebContract } from "thirdweb";
+import { WalletAddress } from "@/components/blocks/wallet-address";
+
+export function ContractCreatorBadge(props: {
+ contractCreator: string;
+ clientContract: ThirdwebContract;
+}) {
+ return (
+
+ By
+
+
+ );
+}
diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.stories.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.stories.tsx
index a0b606a4c8f..ee3a70b6654 100644
--- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.stories.tsx
+++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.stories.tsx
@@ -88,6 +88,25 @@ export const WithImageAndMultipleSocialUrls: Story = {
args: {
chainMetadata: ethereumChainMetadata,
clientContract: mockContract,
+ contractCreator: null,
+ image: mockTokenImage,
+ name: "Sample Token",
+ socialUrls: {
+ discord: mockSocialUrls.discord,
+ github: mockSocialUrls.github,
+ telegram: mockSocialUrls.telegram,
+ twitter: mockSocialUrls.twitter,
+ website: mockSocialUrls.website,
+ },
+ symbol: "SMPL",
+ },
+};
+
+export const WithContractCreator: Story = {
+ args: {
+ chainMetadata: ethereumChainMetadata,
+ clientContract: mockContract,
+ contractCreator: "0x1234567890123456789012345678901234567890",
image: mockTokenImage,
name: "Sample Token",
socialUrls: {
@@ -105,6 +124,7 @@ export const WithBrokenImageAndSingleSocialUrl: Story = {
args: {
chainMetadata: ethereumChainMetadata,
clientContract: mockContract,
+ contractCreator: null,
image: "broken-image.png",
name: "Sample Token",
socialUrls: {
@@ -118,6 +138,7 @@ export const WithoutImageAndNoSocialUrls: Story = {
args: {
chainMetadata: ethereumChainMetadata,
clientContract: mockContract,
+ contractCreator: null,
image: undefined,
name: "Sample Token",
socialUrls: {},
@@ -129,6 +150,7 @@ export const LongNameAndLotsOfSocialUrls: Story = {
args: {
chainMetadata: ethereumChainMetadata,
clientContract: mockContract,
+ contractCreator: null,
image: "https://thirdweb.com/chain-icons/ethereum.svg",
name: "This is a very long token name that should wrap to multiple lines",
socialUrls: {
@@ -148,6 +170,7 @@ export const AllSocialUrls: Story = {
args: {
chainMetadata: ethereumChainMetadata,
clientContract: mockContract,
+ contractCreator: null,
image: "https://thirdweb.com/chain-icons/ethereum.svg",
name: "Sample Token",
socialUrls: {
@@ -171,6 +194,7 @@ export const InvalidSocialUrls: Story = {
args: {
chainMetadata: ethereumChainMetadata,
clientContract: mockContract,
+ contractCreator: null,
image: "https://thirdweb.com/chain-icons/ethereum.svg",
name: "Sample Token",
socialUrls: {
@@ -188,6 +212,7 @@ export const SomeSocialUrls: Story = {
args: {
chainMetadata: ethereumChainMetadata,
clientContract: mockContract,
+ contractCreator: null,
image: "https://thirdweb.com/chain-icons/ethereum.svg",
name: "Sample Token",
socialUrls: {
diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx
index 2f956534db5..8d2ac72f937 100644
--- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx
+++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx
@@ -19,6 +19,7 @@ import { YoutubeIcon } from "@/icons/brand-icons/YoutubeIcon";
import { ChainIconClient } from "@/icons/ChainIcon";
import { cn } from "@/lib/utils";
import { resolveSchemeWithErrorHandler } from "@/utils/resolveSchemeWithErrorHandler";
+import { ContractCreatorBadge } from "./ContractCreatorBadge";
const platformToIcons: Record> = {
discord: DiscordIcon,
@@ -42,6 +43,7 @@ export function ContractHeaderUI(props: {
clientContract: ThirdwebContract;
socialUrls: object;
imageClassName?: string;
+ contractCreator: string | null;
}) {
const socialUrls = useMemo(() => {
const socialUrlsValue: { name: string; href: string }[] = [];
@@ -147,6 +149,13 @@ export function ContractHeaderUI(props: {
{/* bottom row */}
+ {props.contractCreator && (
+
+ )}
+
undefined)
- : undefined;
+ const [contractCreator, claimConditionCurrencyMeta] = await Promise.all([
+ getContractCreator(props.serverContract, functionSelectors),
+ activeClaimCondition
+ ? getCurrencyMeta({
+ chain: props.serverContract.chain,
+ chainMetadata: props.chainMetadata,
+ client: props.serverContract.client,
+ currencyAddress: activeClaimCondition.currency,
+ }).catch(() => undefined)
+ : undefined,
+ ]);
const buyEmbed = (
@@ -20,6 +21,7 @@ export function NFTPublicPageLayout(props: {