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: {