Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/profile/[address]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { CollectionsTabContent } from "@/app/profile/[address]/collections-tab-c
import { MarketplaceTabContent } from "@/app/profile/[address]/marketplace-tab-content";
import { BlueprintsTabContent } from "@/app/profile/[address]/blueprint-tab-content";

import { ContractAccountBanner } from "@/components/profile/contract-accounts-banner";
export default function ProfilePage({
params,
searchParams,
Expand All @@ -22,6 +23,7 @@ export default function ProfilePage({

return (
<section className="flex flex-col gap-2">
<ContractAccountBanner address={address} />
<section className="flex flex-wrap gap-2 items-center">
<h1 className="font-serif text-3xl lg:text-5xl tracking-tight">
Profile
Expand Down
18 changes: 18 additions & 0 deletions components/profile/contract-accounts-banner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"use client";

import { useIsContract } from "@/hooks/useIsContract";

export function ContractAccountBanner({ address }: { address: string }) {
const { isContract, isLoading } = useIsContract(address);

if (!isContract || isLoading) return null;

return (
<div className="bg-yellow-50 border-l-4 border-yellow-400 p-4 mb-4">
<div className="text-sm text-yellow-700">
This is a smart contract address. Contract ownership may vary across
networks. Please verify ownership details for each network.
</div>
</div>
);
}
48 changes: 48 additions & 0 deletions hooks/useIsContract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useState, useEffect } from "react";

import { ChainFactory } from "../lib/chainFactory";
import { EvmClientFactory } from "../lib/evmClient";

const contractCache = new Map<string, boolean>();

export function useIsContract(address: string) {
const [isLoading, setIsLoading] = useState(false);

useEffect(() => {
if (isLoading || contractCache.has(address)) return;

async function checkContract() {
setIsLoading(true);
try {
const supportedChains = ChainFactory.getSupportedChains();
const clients = supportedChains.map((chainId) =>
EvmClientFactory.createClient(chainId),
);

const results = await Promise.allSettled(
clients.map((client) =>
client.getCode({ address: address as `0x${string}` }),
),
);

const result = results.some(
(result) =>
result.status === "fulfilled" &&
result.value !== undefined &&
result.value !== "0x",
);

contractCache.set(address, result);
} finally {
setIsLoading(false);
}
}

checkContract();
}, [address]);

return {
isContract: contractCache.get(address) ?? null,
isLoading,
};
}