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
18 changes: 17 additions & 1 deletion lib/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,22 @@ export async function getFullUserProfile(
dbUser: User | null;
fcProfile: FarcasterProfile | null;
agentMeta: AgentMetadata | null;
isAgentOwner: boolean;
}> {
const dbUser = await getUserFromDB(address);
const [fcProfile, agentMeta] = await Promise.all([
getFarcasterProfile(address, dbUser),
fetchAgentMetadata(address, dbUser),
]);
return { dbUser, fcProfile, agentMeta };

// Detect if this address is the agent OWNER (not the agent itself).
// When true, the profile should display human identity, not agent identity.
const normalized = address.toLowerCase();
const isAgentOwner = agentMeta !== null
&& agentMeta.owner?.toLowerCase() === normalized
&& (dbUser?.agent_wallet == null || dbUser.agent_wallet.toLowerCase() !== normalized);

return { dbUser, fcProfile, agentMeta, isAgentOwner };
}

/**
Expand Down Expand Up @@ -280,6 +289,13 @@ export async function getAgentOwnerProfile(
const agentUser = await getAgentUserFromDB(writerAddress);
if (!agentUser?.agent_id) return null;

// If the queried address IS the agent owner (not the agent wallet),
// this address belongs to the human owner — not an agent.
const normalized = writerAddress.toLowerCase();
const isOwnerAddress = agentUser.agent_owner?.toLowerCase() === normalized;
const isAgentWallet = agentUser.agent_wallet?.toLowerCase() === normalized;
if (isOwnerAddress && !isAgentWallet) return null;

const ownerProfile = agentUser.agent_owner
? await getFarcasterProfile(agentUser.agent_owner)
: null;
Expand Down
26 changes: 19 additions & 7 deletions src/app/profile/[address]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import { getFullUserProfile, getFarcasterProfile } from "../../../../lib/actions";
import { truncateAddress } from "../../../../lib/utils";
import { formatPrice, formatSupply } from "../../../../lib/format";
import { getTokenPrice, mcv2BondAbi, erc20Abi, type TokenPriceInfo, get24hPriceChange, getTokenTVL } from "../../../../lib/price";

Check warning on line 14 in src/app/profile/[address]/page.tsx

View workflow job for this annotation

GitHub Actions / lint-and-typecheck

'TokenPriceInfo' is defined but never used
import { browserClient } from "../../../../lib/rpc";
import type { FarcasterProfile } from "../../../../lib/farcaster";
import type { AgentMetadata } from "../../../../lib/contracts/erc8004";
Expand Down Expand Up @@ -51,7 +51,9 @@
const fcLoading = profileLoading;
const agentMeta = fullProfile?.agentMeta ?? null;
const agentLoading = profileLoading;
const isAgent = !profileLoading && agentMeta !== null && agentMeta !== undefined;
// Owner of an agent is not an agent themselves — show human identity
const isAgentOwner = fullProfile?.isAgentOwner ?? false;
const isAgent = !profileLoading && agentMeta !== null && !isAgentOwner;

// Cumulative claimed royalties (on-chain)
const { data: claimedRoyalties } = useQuery({
Expand Down Expand Up @@ -133,7 +135,7 @@
if (r <= 0) clearInterval(interval);
}, 1000);
return () => clearInterval(interval);
}, [dbUser?.steemhunt_fetched_at]);

Check warning on line 138 in src/app/profile/[address]/page.tsx

View workflow job for this annotation

GitHub Actions / lint-and-typecheck

React Hook useEffect has a missing dependency: 'COOLDOWN_MS'. Either include it or remove the dependency array

const onCooldown = cooldownRemaining > 0;

Expand All @@ -146,6 +148,7 @@
agentMeta={agentMeta ?? null}
agentLoading={agentLoading}
isAgent={isAgent}
isAgentOwner={isAgentOwner}
claimedRoyalties={claimedRoyalties ?? null}
plotBalance={plotBalance ?? null}
plotUsdPrice={plotUsdPrice ?? null}
Expand Down Expand Up @@ -209,6 +212,7 @@
agentMeta,
agentLoading,
isAgent,
isAgentOwner,
claimedRoyalties,
plotBalance,
plotUsdPrice,
Expand All @@ -226,6 +230,7 @@
agentMeta: AgentMetadata | null;
agentLoading: boolean;
isAgent: boolean;
isAgentOwner: boolean;
claimedRoyalties: bigint | null;
plotBalance: bigint | null;
plotUsdPrice: number | null;
Expand All @@ -246,7 +251,10 @@
enabled: hasOwner,
});

const displayName = agentMeta?.name ?? fcProfile?.displayName ?? null;
// Agent owners should show their own FC name, not the agent name
const displayName = isAgentOwner
? fcProfile?.displayName ?? null
: agentMeta?.name ?? fcProfile?.displayName ?? null;
const hasFarcaster = dbUser?.fid != null && dbUser?.username != null;
const hasX = dbUser?.twitter != null;
const hasQuotient = dbUser?.quotient_score != null;
Expand Down Expand Up @@ -293,8 +301,10 @@
{isOwnProfile && <DisconnectButton />}
</div>

{/* Bio */}
{agentMeta?.description ? (
{/* Bio — agent owners show their own FC bio, not the agent description */}
{isAgentOwner ? (
fcProfile?.bio ? <p className="text-muted mt-1 text-xs">{fcProfile.bio}</p> : null
) : agentMeta?.description ? (
<p className="text-muted mt-1 text-xs">{agentMeta.description}</p>
) : fcProfile?.bio ? (
<p className="text-muted mt-1 text-xs">{fcProfile.bio}</p>
Expand Down Expand Up @@ -401,11 +411,13 @@
</div>
)}

{/* Agent Identity card — shown for registered agents */}
{isAgent && agentMeta && (
{/* Agent Identity card — shown for registered agents and agent owners */}
{(isAgent || isAgentOwner) && agentMeta && (
<div className="border-border rounded border p-3">
<div className="flex items-center justify-between">
<span className="text-muted text-[10px] font-medium uppercase tracking-wider">Agent Identity</span>
<span className="text-muted text-[10px] font-medium uppercase tracking-wider">
{isAgentOwner ? "Linked AI Writer" : "Agent Identity"}
</span>
<span className="bg-accent/10 text-accent rounded px-1 py-0.5 text-[9px] font-medium">ERC-8004</span>
</div>
<div className="mt-1.5 space-y-1.5">
Expand Down Expand Up @@ -683,7 +695,7 @@
});

// Claimable royalties (own profile only)
const { data: royaltyInfo } = useQuery({

Check warning on line 698 in src/app/profile/[address]/page.tsx

View workflow job for this annotation

GitHub Actions / lint-and-typecheck

'royaltyInfo' is assigned a value but never used
queryKey: ["profile-royalties", address],
queryFn: async () => {
const [balance, claimed] = await browserClient.readContract({
Expand Down Expand Up @@ -840,7 +852,7 @@
}) {
const tokenAddr = storyline.token_address as Address;

const { data: priceInfo } = useQuery({

Check warning on line 855 in src/app/profile/[address]/page.tsx

View workflow job for this annotation

GitHub Actions / lint-and-typecheck

'priceInfo' is assigned a value but never used
queryKey: ["profile-story-price", storyline.token_address],
queryFn: () => getTokenPrice(tokenAddr, browserClient),
enabled: !!storyline.token_address,
Expand Down
Loading