Problem
Farcaster identity display (P7-4, issue #39) is effectively broken — it depends on NEYNAR_API_KEY which is not configured. All wallet addresses show as truncated hex (0x0175...4dBf) instead of Farcaster usernames + avatars.
The current implementation in lib/farcaster.ts uses Neynar API only and silently returns null when the API key is missing (line 39).
Solution
Port the Steemhunt-first, Neynar-fallback pattern from the dropcast repo. Steemhunt's Farcaster Indexer API (https://fc.hunt.town) is free and requires no API key.
Reference implementation (dropcast)
dropcast/lib/farcaster-indexer.ts — Steemhunt client with:
getUserByWallet(address) — primary lookup
getUserByFid(fid) — FID-based lookup
- 1-hour cache TTL, in-flight deduplication, circuit breaker, retry with backoff
- Negative caching for known failures (422 → 4h, other 4xx → 1h)
dropcast/lib/neynar.ts — Neynar fallback with getUserByAddress(address)
Steemhunt API
Required changes
1. Replace lib/farcaster.ts with two-tier lookup
Rewrite lookupByAddress() to:
- Try Steemhunt
https://fc.hunt.town/users/byWallet/{address} first
- If Steemhunt fails/returns null → fall back to Neynar (if
NEYNAR_API_KEY is set)
- Return
FarcasterProfile or null
Simplify from dropcast — PlotLink doesn't need the full circuit breaker/retry/negative-cache complexity. A simple implementation is fine:
- In-memory cache (1h TTL) using a
Map (no node-cache dependency needed)
- 3s timeout per request
- Steemhunt first → Neynar fallback
2. Update FarcasterProfile type
Current type is fine. Map Steemhunt response fields:
fid → fid
username → username
displayName → displayName
pfpUrl → pfpUrl
3. No changes needed to components
WriterIdentity.tsx and WriterIdentityClient.tsx already consume FarcasterProfile via lookupByAddress() — they'll work once the lookup actually returns data.
Files
lib/farcaster.ts — rewrite lookup logic (Steemhunt primary, Neynar fallback)
- Reference:
dropcast/lib/farcaster-indexer.ts (Steemhunt client pattern)
Acceptance criteria
Problem
Farcaster identity display (P7-4, issue #39) is effectively broken — it depends on
NEYNAR_API_KEYwhich is not configured. All wallet addresses show as truncated hex (0x0175...4dBf) instead of Farcaster usernames + avatars.The current implementation in
lib/farcaster.tsuses Neynar API only and silently returnsnullwhen the API key is missing (line 39).Solution
Port the Steemhunt-first, Neynar-fallback pattern from the
dropcastrepo. Steemhunt's Farcaster Indexer API (https://fc.hunt.town) is free and requires no API key.Reference implementation (dropcast)
dropcast/lib/farcaster-indexer.ts— Steemhunt client with:getUserByWallet(address)— primary lookupgetUserByFid(fid)— FID-based lookupdropcast/lib/neynar.ts— Neynar fallback withgetUserByAddress(address)Steemhunt API
https://fc.hunt.townGET /users/byWallet/{address}— returns{ fid, username, displayName, pfpUrl, addresses, ... }Required changes
1. Replace
lib/farcaster.tswith two-tier lookupRewrite
lookupByAddress()to:https://fc.hunt.town/users/byWallet/{address}firstNEYNAR_API_KEYis set)FarcasterProfileor nullSimplify from dropcast — PlotLink doesn't need the full circuit breaker/retry/negative-cache complexity. A simple implementation is fine:
Map(nonode-cachedependency needed)2. Update
FarcasterProfiletypeCurrent type is fine. Map Steemhunt response fields:
fid→fidusername→usernamedisplayName→displayNamepfpUrl→pfpUrl3. No changes needed to components
WriterIdentity.tsxandWriterIdentityClient.tsxalready consumeFarcasterProfilevialookupByAddress()— they'll work once the lookup actually returns data.Files
lib/farcaster.ts— rewrite lookup logic (Steemhunt primary, Neynar fallback)dropcast/lib/farcaster-indexer.ts(Steemhunt client pattern)Acceptance criteria
NEYNAR_API_KEY(Steemhunt is free)npm run typecheckpasses