Skip to content

Instantly share code, notes, and snippets.

@anastasiarods
Created January 28, 2025 13:10
Show Gist options
  • Save anastasiarods/711afbb8273dd2354a6e7afd8525a5cd to your computer and use it in GitHub Desktop.
Save anastasiarods/711afbb8273dd2354a6e7afd8525a5cd to your computer and use it in GitHub Desktop.
Get Farcaster User Data
import { createPublicClient, http } from "viem";
import { base, optimism } from "viem/chains";
const HUB_URL = "https://hub.pinata.cloud";
const CONTRACTS = {
NEYNAR: "0xdB1eCF22d195dF9e03688C33707b19C68BdEd142",
REGISTRY: "0x00000000Fc6c5F01Fc30151999387Bb99A9f489b"
} as const;
interface User {
fid: number;
username: string;
display_name: string;
pfp_url: string;
profile: {
bio?: { text: string };
location?: { latitude: number; longitude: number };
};
verified_accounts: { platform: string; username: string }[];
verified_addresses: {
eth_addresses?: string[];
sol_addresses?: string[];
};
custody_address: string;
}
async function fetchUserData(fid: number, type: number): Promise<string> {
const response = await fetch(`${HUB_URL}/v1/userDataByFid?fid=${fid}&user_data_type=${type}`);
const data = await response.json();
return data?.data?.userDataBody?.value || "";
}
async function fetchVerifications(fid: number) {
const response = await fetch(`${HUB_URL}/v1/verificationsByFid?fid=${fid}`);
const data = await response.json();
return data?.messages || [];
}
async function getCustodyAddress(fid: number): Promise<string> {
const client = createPublicClient({
chain: optimism,
transport: http()
});
return client.readContract({
address: CONTRACTS.REGISTRY,
abi: [{
name: "custodyOf",
type: "function",
stateMutability: "view",
inputs: [{ name: "fid", type: "uint256" }],
outputs: [{ name: "custody", type: "address" }]
}],
functionName: "custodyOf",
args: [BigInt(fid)]
});
}
export async function getUserByFid(fid: number): Promise<User> {
const [pfp, display, bio, username, location, twitter, verifications, custody] = await Promise.all([
fetchUserData(fid, 1), // PFP
fetchUserData(fid, 2), // Display Name
fetchUserData(fid, 3), // Bio
fetchUserData(fid, 6), // Username
fetchUserData(fid, 7), // Location
fetchUserData(fid, 8), // Twitter
fetchVerifications(fid),
getCustodyAddress(fid)
]);
// Parse location if available
let locationData = undefined;
if (location?.startsWith("geo:")) {
const [lat, lng] = location.substring(4).split(",");
locationData = { latitude: parseFloat(lat), longitude: parseFloat(lng) };
}
// Group verifications by protocol
const eth_addresses = verifications
.filter((msg: any) => msg.data.verificationAddAddressBody.protocol === "PROTOCOL_ETHEREUM")
.map((msg: any) => msg.data.verificationAddAddressBody.address);
const sol_addresses = verifications
.filter((msg: any) => msg.data.verificationAddAddressBody.protocol === "PROTOCOL_SOLANA")
.map((msg: any) => msg.data.verificationAddAddressBody.address);
return {
fid,
username: username || "",
display_name: display || "",
pfp_url: pfp,
profile: {
bio: bio ? { text: bio } : undefined,
location: locationData
},
verified_accounts: twitter ? [{ platform: "x", username: twitter }] : [],
verified_addresses: { eth_addresses, sol_addresses },
custody_address: custody
};
}
export async function getUserByAddress(address: string): Promise<User> {
const client = createPublicClient({
chain: base,
transport: http()
});
const fid = await client.readContract({
address: CONTRACTS.NEYNAR,
abi: [{
name: "getFid",
type: "function",
stateMutability: "view",
inputs: [{ name: "verifier", type: "address" }],
outputs: [{ name: "fid", type: "uint256" }]
}],
functionName: "getFid",
args: [address as `0x${string}`]
});
if (fid === BigInt(0)) throw new Error("User not found");
return getUserByFid(Number(fid));
}
getUserByAddress("0xd7029bdea1c17493893aafe29aad69ef892b8ff2").then((data) => {
console.log(data);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment