Farcaster Integration
Nounspace integrates deeply with the Farcaster protocol to provide social features and identity management.
Overview
Farcaster integration enables:
- Social Identity - Farcaster ID (FID) linking and management
- Social Features - Casts, feeds, and social interactions
- Protocol Access - Direct access to Farcaster protocol features
- Identity Verification - Cryptographic identity verification
Core Components
1. FID Management
// FID linking and management
export type FarcasterStore = {
getFidsForCurrentIdentity: () => Promise<void>;
registerFidForCurrentIdentity: (
fid: number,
signingKey: string,
signMessage: (messageHash: Uint8Array) => Promise<Uint8Array>,
) => Promise<void>;
setFidsForCurrentIdentity: (fids: number[]) => void;
addFidToCurrentIdentity: (fid: number) => void;
};
2. Identity Linking
// Link Farcaster FID to identity
const registerFidForCurrentIdentity = async (
fid: number,
signingKey: string,
signMessage: (messageHash: Uint8Array) => Promise<Uint8Array>,
) => {
const request: Omit<FidLinkToIdentityRequest, "signature"> = {
fid,
identityPublicKey: get().account.currentSpaceIdentityPublicKey!,
timestamp: moment().toISOString(),
signingPublicKey: signingKey,
};
const signedRequest: FidLinkToIdentityRequest = {
...request,
signature: bytesToHex(await signMessage(hashObject(request))),
};
const { data } = await axiosBackend.post<FidLinkToIdentityResponse>(
"/api/fid-link",
signedRequest,
);
if (!isUndefined(data.value)) {
get().account.addFidToCurrentIdentity(data.value!.fid);
analytics.track(AnalyticsEvent.LINK_FID, { fid });
}
};
Farcaster Fidgets
1. Cast Fidget
// Cast display and interaction
export const Cast: FidgetModule<CastArgs> = {
Component: ({ config, properties, theme }) => {
const [cast, setCast] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchCast = async () => {
try {
const castData = await api.getCast(config.castHash);
setCast(castData);
} catch (error) {
console.error('Failed to fetch cast:', error);
} finally {
setLoading(false);
}
};
if (config.castHash) {
fetchCast();
}
}, [config.castHash]);
if (loading) return <div>Loading cast...</div>;
if (!cast) return <div>Cast not found</div>;
return (
<div className="cast-fidget">
<div className="cast-header">
<img src={cast.author.pfp_url} alt={cast.author.display_name} />
<div>
<h3>{cast.author.display_name}</h3>
<p>@{cast.author.username}</p>
</div>
</div>
<div className="cast-content">
<p>{cast.text}</p>
</div>
<div className="cast-actions">
<button>Like</button>
<button>Recast</button>
<button>Reply</button>
</div>
</div>
);
},
properties: {
fidgetName: "Cast",
description: "Display and interact with Farcaster casts",
fields: [
{
fieldName: "castHash",
type: "string",
default: "",
label: "Cast Hash"
}
],
category: "farcaster",
tags: ["farcaster", "social"],
version: "1.0.0"
}
};
2. Feed Fidget
// Feed display and management
export const Feed: FidgetModule<FeedArgs> = {
Component: ({ config, properties, theme }) => {
const [feed, setFeed] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchFeed = async () => {
try {
const feedData = await api.getFeed(config.feedType);
setFeed(feedData);
} catch (error) {
console.error('Failed to fetch feed:', error);
} finally {
setLoading(false);
}
};
fetchFeed();
}, [config.feedType]);
if (loading) return <div>Loading feed...</div>;
return (
<div className="feed-fidget">
<h3>Feed</h3>
<div className="feed-content">
{feed.map(cast => (
<CastItem key={cast.hash} cast={cast} />
))}
</div>
</div>
);
},
properties: {
fidgetName: "Feed",
description: "Display Farcaster feed",
fields: [
{
fieldName: "feedType",
type: "select",
default: "following",
options: ["following", "trending", "recent"],
label: "Feed Type"
}
],
category: "farcaster",
tags: ["farcaster", "social", "feed"],
version: "1.0.0"
}
};
3. Frame Fidget
// Frame display and interaction
export const Frame: FidgetModule<FrameArgs> = {
Component: ({ config, properties, theme }) => {
const [frame, setFrame] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchFrame = async () => {
try {
const frameData = await api.getFrame(config.frameUrl);
setFrame(frameData);
} catch (error) {
console.error('Failed to fetch frame:', error);
} finally {
setLoading(false);
}
};
if (config.frameUrl) {
fetchFrame();
}
}, [config.frameUrl]);
if (loading) return <div>Loading frame...</div>;
if (!frame) return <div>Frame not found</div>;
return (
<div className="frame-fidget">
<iframe
src={frame.url}
width="100%"
height="400"
frameBorder="0"
title={frame.title}
/>
</div>
);
},
properties: {
fidgetName: "Frame",
description: "Display Farcaster frames",
fields: [
{
fieldName: "frameUrl",
type: "string",
default: "",
label: "Frame URL"
}
],
category: "farcaster",
tags: ["farcaster", "frame", "interactive"],
version: "1.0.0"
}
};
API Integration
1. Neynar API
// Neynar API integration
export class NeynarAPI {
private apiKey: string;
private baseUrl: string;
constructor(apiKey: string) {
this.apiKey = apiKey;
this.baseUrl = 'https://api.neynar.com/v2';
}
async getUser(fid: number) {
const response = await fetch(`${this.baseUrl}/farcaster/user/bulk`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'api_key': this.apiKey
},
body: JSON.stringify({ fids: [fid] })
});
return response.json();
}
async getCast(hash: string) {
const response = await fetch(`${this.baseUrl}/farcaster/cast`, {
method: 'GET',
headers: {
'api_key': this.apiKey
}
});
return response.json();
}
async getFeed(feedType: string) {
const response = await fetch(`${this.baseUrl}/farcaster/feed`, {
method: 'GET',
headers: {
'api_key': this.apiKey
}
});
return response.json();
}
}
2. Farcaster Protocol
// Direct Farcaster protocol integration
export class FarcasterProtocol {
private hubUrl: string;
constructor(hubUrl: string) {
this.hubUrl = hubUrl;
}
async getCast(hash: string) {
const response = await fetch(`${this.hubUrl}/v1/castById`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ hash })
});
return response.json();
}
async getUser(fid: number) {
const response = await fetch(`${this.hubUrl}/v1/userDataByFid`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ fid })
});
return response.json();
}
}
Social Features
1. Cast Interaction
// Cast interaction functionality
export const useCastInteraction = (castHash: string) => {
const [cast, setCast] = useState(null);
const [loading, setLoading] = useState(false);
const likeCast = async () => {
setLoading(true);
try {
await api.likeCast(castHash);
setCast(prev => ({ ...prev, likes: prev.likes + 1 }));
} catch (error) {
console.error('Failed to like cast:', error);
} finally {
setLoading(false);
}
};
const recast = async () => {
setLoading(true);
try {
await api.recast(castHash);
setCast(prev => ({ ...prev, recasts: prev.recasts + 1 }));
} catch (error) {
console.error('Failed to recast:', error);
} finally {
setLoading(false);
}
};
const reply = async (text: string) => {
setLoading(true);
try {
await api.replyToCast(castHash, text);
} catch (error) {
console.error('Failed to reply to cast:', error);
} finally {
setLoading(false);
}
};
return { cast, loading, likeCast, recast, reply };
};
2. Social Graph
// Social graph functionality
export const useSocialGraph = (fid: number) => {
const [following, setFollowing] = useState([]);
const [followers, setFollowers] = useState([]);
const [loading, setLoading] = useState(false);
const fetchFollowing = async () => {
setLoading(true);
try {
const data = await api.getFollowing(fid);
setFollowing(data);
} catch (error) {
console.error('Failed to fetch following:', error);
} finally {
setLoading(false);
}
};
const fetchFollowers = async () => {
setLoading(true);
try {
const data = await api.getFollowers(fid);
setFollowers(data);
} catch (error) {
console.error('Failed to fetch followers:', error);
} finally {
setLoading(false);
}
};
return { following, followers, loading, fetchFollowing, fetchFollowers };
};
Identity Management
1. FID Verification
// FID verification process
export const verifyFid = async (fid: number, signature: string) => {
try {
const response = await fetch('/api/verify-fid', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ fid, signature })
});
if (!response.ok) {
throw new Error('FID verification failed');
}
return response.json();
} catch (error) {
console.error('FID verification error:', error);
throw error;
}
};
2. Identity Linking
// Link Farcaster identity to app identity
export const linkFarcasterIdentity = async (
fid: number,
appIdentity: string,
signature: string
) => {
try {
const response = await fetch('/api/link-farcaster', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
fid,
appIdentity,
signature
})
});
if (!response.ok) {
throw new Error('Identity linking failed');
}
return response.json();
} catch (error) {
console.error('Identity linking error:', error);
throw error;
}
};
Error Handling
1. API Error Handling
// Farcaster API error handling
export const handleFarcasterError = (error: any) => {
if (error.response) {
const { status, data } = error.response;
switch (status) {
case 400:
return 'Invalid request parameters';
case 401:
return 'Unauthorized - check API key';
case 404:
return 'Farcaster resource not found';
case 429:
return 'Rate limit exceeded';
case 500:
return 'Farcaster server error';
default:
return `Farcaster API error: ${data.message || 'Unknown error'}`;
}
}
return 'Network error - please check your connection';
};
2. Protocol Error Handling
// Farcaster protocol error handling
export const handleProtocolError = (error: any) => {
if (error.code) {
switch (error.code) {
case 'INVALID_SIGNATURE':
return 'Invalid signature - please try again';
case 'EXPIRED_MESSAGE':
return 'Message expired - please refresh and try again';
case 'INVALID_FID':
return 'Invalid Farcaster ID';
case 'NETWORK_ERROR':
return 'Network error - please check your connection';
default:
return `Protocol error: ${error.message}`;
}
}
return 'Unknown protocol error';
};
Best Practices
1. API Usage
- Rate Limiting - Respect API rate limits
- Error Handling - Handle all possible error cases
- Caching - Cache API responses when appropriate
- Authentication - Secure API key management
2. Protocol Integration
- Signature Verification - Always verify signatures
- Message Validation - Validate all protocol messages
- Error Recovery - Implement proper error recovery
- Security - Follow security best practices
3. User Experience
- Loading States - Show loading states for async operations
- Error Messages - Provide clear error messages
- Offline Support - Handle offline scenarios gracefully
- Performance - Optimize for performance
Troubleshooting
Common Issues
- API Key Issues - Verify Neynar API key configuration
- Signature Errors - Check signature generation and verification
- Network Issues - Handle network connectivity problems
- Rate Limiting - Implement proper rate limiting handling
Debug Tools
- Use browser DevTools to inspect API requests
- Check Farcaster protocol documentation
- Verify signature generation with test tools
- Monitor API usage and limits